Google Web Toolkit(GWT)中的多页教程

Google Web Toolkit(GWT)中的多页教程,gwt,navigation,Gwt,Navigation,我刚开始学习(GWT)。如何在GWT应用程序中创建不同的HTML页面 例如,我想为书店创建一个应用程序。在此应用程序中,我将有三个页面: 主页,我将欢迎用户并提供用户书籍 按类别浏览书籍并查看详细信息的页面(使用GWT小部件) 网上借书 当然,可能还有其他页面,如用户详细信息、添加新书等。 那么,在GWT中创建不同页面的最佳方法是什么?如何在页面之间进行导航?有什么例子或教程吗?或者,当我可以在一个页面中创建一个完整的应用程序时,我甚至需要创建不同的页面吗?如果你想让它完全AJAXified(如

我刚开始学习(GWT)。如何在GWT应用程序中创建不同的HTML页面

例如,我想为书店创建一个应用程序。在此应用程序中,我将有三个页面:

  • 主页,我将欢迎用户并提供用户书籍
  • 按类别浏览书籍并查看详细信息的页面(使用GWT小部件)
  • 网上借书
  • 当然,可能还有其他页面,如用户详细信息、添加新书等。
    那么,在GWT中创建不同页面的最佳方法是什么?如何在页面之间进行导航?有什么例子或教程吗?或者,当我可以在一个页面中创建一个完整的应用程序时,我甚至需要创建不同的页面吗?

    如果你想让它完全AJAXified(如桌面应用程序),当然你只需要一个页面。然后根据链接更改主体的内容


    另外,有一个针对GWT的google小组非常活跃,我知道之前有人问过这个问题,你只需要使用“搜索”功能。

    我会使用HyperLink和History类。Hyperlink类的好处在于,它设置了这个令牌(例如#foobar),您所要做的就是捕获事件,该事件在令牌的值更改时触发(ValueChangeEvent)。然后在eventHandler中替换页面

    例如: 欢迎页面地址:www.yourpage.com/#home 在这个页面上会有一个指向“浏览书籍”页面的链接,当点击这个链接时,新地址会是这样的:www.yourpage.com/#browse

    代码如下:
    
    public class MainEntryPoint implements EntryPoint, ValueChangeHandler {
        VerticalPanel panel = new VerticalPanel();
        Label label=new Label();
        public void onModuleLoad() {
            Hyperlink link1 = new Hyperlink("books", "browse");
            Hyperlink link2 = new Hyperlink("user details", "details");
            panel.add(link1);
            panel.add(link2);
            panel.add(label);
            RootPanel.get().add(panel);
            History.addValueChangeHandler(this);
            //when there is no token, the "home" token is set else changePage() is called.
            //this is useful if a user has bookmarked a site other than the homepage.
            if(History.getToken().isEmpty()){
                History.newItem("home");
            } else {
                changePage(History.getToken());
            }
        }

    public void onValueChange(ValueChangeEvent event) {
        changePage(History.getToken());
    }
    public void changePage(String token) {
        if(History.getToken().equals("browse")) {
            label.setText("Here would be some books");
        } else if (History.getToken().equals("details")) {
            label.setText("Here would be the user details");
        } else {
            label.setText("Welcome page");
        }
    }
    

    }

    在这种情况下,我通常首先设计网页框架。我会为页眉、侧菜单和页脚添加一个div。我的HTML中还有一个
    div
    作为主要内容

    例如:

    RootPanel.get("header").add(new Header());
    RootPanel.get("leftnav").add(new NavigationMenu());
    RootPanel.get("footer").add(new Footer());
    
    当然,可以有一个静态的页脚和页眉,但这既不在这里也不在那里

    我还有一个名为“Content”的抽象类。内容对象扩展了“复合”,并将有各种方法来简化新页面的创建和布局。我为此应用程序构建的每个页面,无论是帮助屏幕、搜索屏幕、购物车还是其他任何页面,都属于
    内容类型

    现在,我要做的是创建一个名为“ContentContainer”的类。这是一个负责管理“content”元素的单例。它有一个方法“setContent”,它接受类型为“Content”的对象。然后,它基本上删除“内容”中的任何内容,并用您通过“setContent”方法指定的任何小部件(组合)替换它。setContent方法还处理历史记录和标题栏管理。基本上,ContentContainer用于聚合所有绑定点,如果每个页面内容必须“知道”它必须执行的所有功能,那么您可能必须进行这些绑定点

    最后,你需要一种方法去那一页,对吗?这很简单:

    public class Index implements EntryPoint, ValueChangeHandler<String> {
        public void onModuleLoad() {
            History.addValueChangeHandler(this);
            if (History.getToken().isEmpty()) History.newItem("index");
            Composite c = new Login(); 
            FlowControl.go(c);
        }
    
        public void onValueChange(ValueChangeEvent<String> e) {
            FlowControl.go(History.getToken());
        }
    }
    
    将以上内容放在某个点击事件中,您将获得金牌

    您的其他小部件需要绑定的唯一内容是ContentContainer和它们添加的内容类型

    我可以看到ChrisBo方法的缺点是,您有一个必须维护的标记->页面列表。我可以看到的另一个缺点是,我不知道如何使用这种方法来创建一个实际的历史记录系统

    与我的方法相比,它确实提供了一件事,那就是所有的页面选择都是相当集中的。我会使用某种枚举或至少是一个带有字符串值的静态类来防止自己混淆链接


    在任何一种情况下,我认为这一点都可以总结为:根据用户单击的内容和用户执行的操作来交换某些中心页面元素的内容。为需要GWT功能的每个页面添加一个模块。重用组件。

    -多页GWT应用程序的简单框架。

    太棒了!我将Chris R.的回答与Chris Boesing的结合起来得出以下结论:

    这是“索引”的起始页

    public class FlowControl {
    private static FlowControl instance;
    private FlowControl() {}
    public static void go(Composite c) {
        if (instance == null) instance = new FlowControl(); // not sure why we need this yet since everything is static.
        RootPanel.get("application").clear();
        RootPanel.get("application").getElement().getStyle().setPosition(Position.RELATIVE); // not sure why, but GWT throws an exception without this. Adding to CSS doesn't work.
        // add, determine height/width, center, then move. height/width are unknown until added to document. Catch-22!
        RootPanel.get("application").add(c);
        int left = Window.getClientWidth() / 2 - c.getOffsetWidth() / 2; // find center
        int top = Window.getClientHeight() / 2 - c.getOffsetHeight() / 2;
        RootPanel.get("application").setWidgetPosition(c, left, top);
        History.newItem(c.getTitle()); // TODO: need to change and implement (or override) this method on each screen
    }
    
    public static void go(String token) {
        if (token == null) go(new Login());
        if (token.equals("cart")) go(new Cart());
        if (token.equals("login")) go(new Login());
        // Can probably make these constants in this class
    }
    
    然后,您可以在代码中的任何位置添加超链接和按钮。(尚未尝试超链接。)

    我在HTML中添加了一个div

        initWidget(myPanel); // all composites must call this in constructor
    
    你可以使用MVP模式。 这是我的简单图书馆

    您可以将代码拆分为更多的js文件

    我使用Chloe S.answer(结合Chris R.的答案和Chris Boesing的答案)为一个工作的GWT Web应用程序构建了这个应用程序控制器。正在生产中的版本已经过测试(并且可以使用%100),但是需要修改下面的修订版本,以便与您自己的应用程序集成(首先将页面键重命名为菜单项)

    AppController.java

    package com.*;
    
    import com.google.gwt.event.logical.shared.AttachEvent;
    import com.google.gwt.user.client.ui.Composite;
    
    /**
     * Base interface for all 'Presenters' used by AppController.java
     * NOTE: classes that implement this interface do not launch the presenter's view 
     * into the provided container; rather, the view is retrieved and used by the 
     * AppController instance by calling the 'view()' method
     */
    public interface AppControlPresenter {
    
        /**
         * Gets the view (for use in AppController.java)
         */
        public Composite view();
    
        /**
         * Indicates if current search data is present and unsaved.
         * @returns true to if a search is still active  
         */
        public boolean unsavedData();
    
        /**
         * Called on resize event to notify presenters with visible
         * components that need resizing for different screen sizes.
         * @returns true if elements were resized
         */
        public boolean resize();
    
        /**
         * Called on attachEvents to tell the presenter to update.
         * @param event the AttachEvent
         */
        public void updateAttachOrDetach(AttachEvent event);
    
        /**
         * Gets the message to display for unsaved data.
         * @returns a message String describing the data
         */
        public String dataDescription();
    
        /**
         *  Gets a fully qualified name for use in comparisons
         * @return the name of this presenter used by the <code>AppController</code>
         */
        public String toString();
    }
    
    AppControlContainerPresenter.java:

    package com.*;
    
    import com.google.gwt.user.client.ui.Composite;
    import com.google.gwt.user.client.ui.LayoutPanel;
    
    /**
     */
    public interface AppControlContainerPresenter extends AppControlPresenter {
    
        /**
         * 
         * @return
         */
        public LayoutPanel getContentPane();
    
        /**
         * 
         * @param pageName
         * @return
         */
        public Composite setCurrentPage(String pageName);
    }
    

    我认为,对于页面,提问者的意思是如何将web应用程序的传统“页面”概念转化为GWT的世界,在GWT中,您实际上只有一个html文件/页面。考虑到对这个问题的误解,答案根本没有真正回答任何问题。这让它在我的脑海里咔哒作响。设计一个GWT网站就像设计一个传统的桌面应用程序一样,你有一个“主窗口”(主html页面),其中有多个“用户控件”,可以在其中停靠和停靠。哇,真是太棒了。“我不明白你怎么能用这个方法建立一个实际的历史系统”这句话是指我的方法吗?因为它有一个历史系统,不是很好的,但它有一个。我不知道“我去过哪里”的历史记录系统如何与新页面的命名标识符相结合。你的解决方案更像是对历史系统的滥用。我想如果你按“后退”按钮,你会回到上次的位置。不过,这并不是针对你的解决方案的攻击或任何东西。应该是这样的:“我不认为‘我去过哪里’的历史记录系统如何与新页面的命名标识符相结合”这是一个很好的答案,b
    <!-- This is where the application will reside within. It is controlled by FlowControl class. -->
    <div id="application"></div>
    
        initWidget(myPanel); // all composites must call this in constructor
    
    /**
     * This App Controller utilizes two static inner-classes (Pages and External) 
     * to manage and server multiple pages with multiple sub-page (through their presenters) 
     * via String key constants which also serve as the literal text for the menu items.
     * 
     * Pages are added as menu commands in their respective views:
     *  // Add menu items to the menu with commands:
     *  menuItems.put(Pages.PAGE1, mainMenu.addItem(Pages.PAGE1, new Command() {
     *      public void execute() {
     *          History.newItem(Pages.PAGE1);
     *      }
     *  }));
     * 
     * Pages are fired as History tokens (from entry point java class):
     * 
     *   **
     *   * Receives history events and pushes them to the AppController using a deferred command.
     *   * Changes the cursor to show waiting.
     *   * @param the value change token
     *   *
     *  public void onValueChange(ValueChangeEvent<String> e) {
     *      // check token to cover first historical "back" navigation:
     *      if(!History.getToken().isEmpty()) {
     *          AppController.waitCursor.execute(); // cursor is reset in page attach method
     *      }
     *      Scheduler.get().scheduleDeferred(new ScheduledCommand() {
     *          public void execute() {
     *              AppController.go(History.getToken());
     *          }
     *      });
     *  }
     *  
     * Wait cursors are implemented as CSS:
     *
     *  body.wait, body.wait * {
     *      cursor: wait !important;   
     *  }
     * 
     * NOTE: This page swapping implementation technique (based on the StackOverflow solution 
     * found here: [http://stackoverflow.com/questions/1061705/multiple-pages-tutorial-in-google-web-toolkit-gwt][1]) 
     * differs from the obtuse and ancient 2010 GWT framework documentation in that the App Controller manages / handles 
     * adding the widget to the container, and therefore all the Presenters must implement the 
     * "AppControlPresenter" or "AppControlContainerPresenter" interface to give it access to their containers.
     * (thus eliminating "public void go(final HasWidgets container);" method in all presenter architecture except for 'MainAppPresenter')
     * There is also no event bus; static method calls are used for any needed interactivity.
     *
     * Includes a popup for pages still under construction.
     */
    
    package com.;
    
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.core.client.Scheduler;
    import com.google.gwt.core.client.Scheduler.ScheduledCommand;
    import java.util.HashMap;
    import java.util.Map;
    import com.google.gwt.dom.client.Style.Unit;
    import com.google.gwt.event.logical.shared.AttachEvent;
    import com.google.gwt.event.logical.shared.ResizeEvent;
    import com.google.gwt.event.shared.HandlerManager;
    import com.google.gwt.user.client.History;
    import com.google.gwt.user.client.Window;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.Composite;
    import com.google.gwt.user.client.ui.DecoratedPopupPanel;
    import com.google.gwt.user.client.ui.Frame;
    import com.google.gwt.user.client.ui.HTML;
    import com.google.gwt.user.client.ui.Image;
    import com.google.gwt.user.client.ui.LayoutPanel;
    import com.google.gwt.user.client.ui.RootLayoutPanel;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.google.gwt.user.client.ui.UIObject;
    
    /**
     * 
     */
    public class AppController {
        /** */
        public final static String DEFAULT_INITIAL_PAGE1_SUB_PAGE = Pages.PAGE_1A;
        /** Singleton instance for the AppController */
        private static AppController instance = new AppController();
        /** Presenter for the main app */
        private static MainAppPresenter mainAppPresenter;
        /** container for the different views */
        private static LayoutPanel container;
        /** sub-container for the different sub-views */
        private static LayoutPanel page1Container;
        /** */
        private static DecoratedPopupPanel popup;
        /** constant for Style-Dependent names for menu items (see menu-style.css) */
        public final static String MENU_ACTIVE_STYLE = "active";
        /** constant for Style-Dependent class name in css */
        public final static String CURSOR_WAIT_CLASS = "wait";  
        /** */
        public final static String POPUP_DEMO_ID = "popupDemo";
        /** */  
        public final static int DEMOP_POPUP_VERTICAL_OFFSET = 0;
        /** */
        public final static String POPUP_DEMO_STATEMENT = "<span class='text'>This page is under construction</span>"
                                                        + "<span class='char'>&hellip;</span>";
        /** */
        public static ScheduledCommand waitCursor = new ScheduledCommand() {
            @Override
            public void execute() { 
                AppController.waitCursor(true); 
            }
        };
    
        /** */
        public static ScheduledCommand normalCursor = new ScheduledCommand() {
            @Override
            public void execute() { 
                AppController.waitCursor(false); 
            }
        };
    
        /** Flag for determining if the page was reloaded */
        private static boolean reloaded = false;
    
        private static final LoginServiceAsync loginRpcService = GWT.create(LoginService.class);
    
        /**
         * Called on the resize event to set the position of the demo popup
         * window to be adjusted to the correct dimensions (size and positoin)
         * regardless of screen size.
         */
        private static ScheduledCommand resetPopupDimensions = new ScheduledCommand() {
            @Override
            public void execute() {
                if(!UNDER_CONSTRUCTION || popup == null) {
                    return;
                }
                int demoWidth = Math.round(Window.getClientWidth() / MainApp.PHI),
                    demoYPosition = Window.getClientHeight() / 2 - Math.round(popup.getOffsetHeight() / 2);
                popup.setWidth(String.valueOf(demoWidth) + "px");
                if(popup.getOffsetWidth() >= Window.getClientWidth()) {
                    popup.setWidth("100%");
                    popup.setPopupPosition(0, demoYPosition);
                } else {
                    popup.setPopupPosition(Window.getClientWidth() / 2 - (popup.getOffsetWidth() / 2), demoYPosition);
                }
            }
        };
    
        /** */
        private static final String LOGIN_OBJECT_NAME = "Login Presenter Object";
    
        /**
         * static inner-class for external websites 
         */
        public static class External {
            /** The frame to contain the website */
            private static Frame frame;
            /**  */
            public static final String EXTERNAL_URL_1 = "http://";
            /**  */
            public static final String EXTERNAL_URL_2 = "http://";
    
            /**
             * @returns true if the name of the token is equal to one of the URLs
             * @param token the name to check
             */
            public static boolean has(String token) {
                return token.equalsIgnoreCase(EXTERNAL_URL_1)      ||
                       token.equalsIgnoreCase(EXTERNAL_URL_2);
            }
    
            /**
             * Gets the external Frame object
             * @param url
             * @return Frame
             */
            public static Frame get(String url) {
                if(frame == null) {
                    frame = new Frame(url);
                    frame.addAttachHandler(new AttachEvent.Handler() {
                        @Override
                        public void onAttachOrDetach(AttachEvent event) { 
                            // hide the popup:
                            showPopup(false);
                            Scheduler.get().scheduleFinally(resetPopupDimensions);
                            Scheduler.get().scheduleFinally(normalCursor);
                        }
                    });
                }
                else if(!frame.getUrl().equalsIgnoreCase(url)) {
                    frame.setUrl(url);
                }
                return frame;
            }
        }
    
        /**
         * static inner-class for holding pages activated by the app's main menu commands
         */
        public static class Pages {
            /** */
            public static final String PAGE1 = "foo";
            /** */
            public static final String PAGE2 = "bar";
            /** */
            public static final String PAGE_1A = "baz";
            /** */
            public static final String PAGE_1B = "qux";
            /** */
            public static String lastPage;
            /** */
            public static String lastPage1SubPage;
            /** */
            public static String unsavedMessage;
            /** */
            private static HashMap<String, AppControlPresenter> pageMap;
            /** */
            private static AppControlPresenter presenter;
            /** */
            private static Composite view;
    
            /**
             * initializes the hashmap of pages  
             */
            public static void init() {
                pageMap = new HashMap<String, AppControlPresenter>();
            }
    
            /**
             * @returns true if the name of the token is equal to one of the pages
             * @param token the name to check
             */
            public static boolean has(String token) {
                return token.equalsIgnoreCase(PAGE1)            ||
                       token.equalsIgnoreCase(PAGE2)            ||
                       token.equalsIgnoreCase(PAGE_1A);
            }
    
    
            /**
             * Gets the correct page container to display as a Composite
             * @param page the token name of the page
             * @return Composite page
             */
            public static Composite get(String page) {
                view = null;
                presenter = null;
                if(page.equalsIgnoreCase(PAGE1)) {
                    if(pageMap.get(PAGE1) == null) {
                        pageMap.put(PAGE1, new Page1Presenter(PAGE1)); 
                        page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane();
                    }
                    presenter = pageMap.get(PAGE1);
                    lastPage = page;
                    mainAppPresenter.setCurrentMenuItem(page);
                }
                else if(page.equalsIgnoreCase(PAGE_1A) ||
                        page.equalsIgnoreCase(PAGE_1B) {
                    if(pageMap.get(PAGE1) == null) {
                        pageMap.put(PAGE1, new Page1Presenter(PAGE1)); 
                        page1Container = ((AppControlContainerPresenter) pageMap.get(PAGE1)).getContentPane();
                    }
                    presenter = pageMap.get(PAGE1);
                    lastPage1SubPage = page;
                    view = ((AppControlContainerPresenter)presenter).setCurrentPage(page);
                }
                else if(page.equalsIgnoreCase(PAGE2)) {
                    if(pageMap.get(PAGE2) == null) {
                        pageMap.put(PAGE2, new Page2Presenter(PAGE2));
                    }
                    presenter = pageMap.get(PAGE2);
                    lastPage = PAGE2;
                    mainAppPresenter.setCurrentMenuItem(page);
                }
                else if(External.has(page)) {
                    throw new Error("App Controller Error -- Use 'External' inner-class for: " + page);
                }
                else {
                    throw new Error("App Controller Error -- Page name not found: " + page);
                }
                if(view == null) {
                    view = (Composite)presenter.view();
                }
                view.addAttachHandler(new AttachEvent.Handler() {
                    @Override
                    public void onAttachOrDetach(AttachEvent event) {
                        AppController.showPopup(false);
                        presenter.updateAttachOrDetach(event);
                        Scheduler.get().scheduleFinally(resetPopupDimensions);
                        Scheduler.get().scheduleFinally(normalCursor);
                    }
                });
                return view;
            }
    
            /**
             * Gets the current AppControlPresenter for the last page.
             * @returns the current AppControlPresenter  
             */
            public static AppControlPresenter getCurrentPresenter() {
                return presenter;
            }
    
            /**
             * Gets an AppControlPresenter from the pageMap.
             * @param token the name of the presenter
             * @returns the AppControlPresenter  
             */
            public static AppControlPresenter getPresenter(String token) {
                return pageMap.get(token);
            }
    
            /**
             * Returns true if the page is already loaded.
             * @param token name of the page
             */
            public static boolean alreadyLoaded(String token) {
                MainApp.debug(1, "[already loaded: " + presenter.toString() + " (token: " + token + ")");
                return presenter.toString().equalsIgnoreCase(token);
            }
    
            /**
             * Returns true if the page is visible
             * @param page the token name of the page
             */
            public static boolean isVisible(String page) {
                UIObject component = pageMap.get(page).view();
                return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0);
            }
    
            /**
             * Returns true if the page is visible
             * @param presenter the AppControlPresenter instance
             */
            public static boolean isVisible(AppControlPresenter presenter) {
                UIObject component = presenter.view();
                return !(component.getOffsetHeight() == 0 && component.getOffsetWidth() == 0);
            }
    
            /**
             * Returns true if the application has unsaved data.
             * Iterates through all the pages and checks each presenter.
             */
            public static boolean unsavedData() {
                if(pageMap.isEmpty()) return false;
                boolean unsaved = false;
                for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) {
                    AppControlPresenter presenter = entry.getValue();
                    if(presenter != null && presenter.unsavedData()) {
                        MainApp.debug(1, "(!) " + presenter.toString() + " has unsaved data");
                        unsavedMessage = presenter.dataDescription();
                        unsaved = true;
                        break; // just need to know one exists for now (window closing event)
                    }
                }
                return unsaved;
            }
    
            /**
             * Called on a resize event on the window. Iterates through all the pages
             * and tells their presenters to resize their content.
             */
            public static void resize() {
                for(Map.Entry<String, AppControlPresenter> entry : pageMap.entrySet()) {
                    AppControlPresenter presenter = entry.getValue();
                    if(presenter != null && isVisible(presenter)) {
                        presenter.resize();
                    }
                }
            }
        } //end class Pages
    
        /**
         * @returns true if the history token is equal to any of the pages in the app 
         */
        public static boolean hasHistory() {
            String token = History.getToken();
            return External.has(token) || Pages.has(token);
        }
    
        /**
         * Starts the login view at the root layout level
         */
        public static void goLoginScreen() {
            //check for reload:
            if(hasHistory()) {
                MainApp.debug(1, "(!) AppController has History on Login");
                reloaded = true;
            }
            else {
                reloaded = false;
            }
            RootLayoutPanel.get().clear();
            RootLayoutPanel.get().add(new LoginPresenter(LOGIN_OBJECT_NAME).view());
        }
    
        /**
         * @returns the last "Page1" page
         */
        public static String getLastPage1Page() {
            if(Pages.lastPage1SubPage == null || Pages.lastPage1SubPage.isEmpty()) {
                Pages.lastPage1SubPage = DEFAULT_INITIAL_PAGE1_SUB_PAGE;
            }
            return Pages.lastPage1SubPage;
        }
    
        /**
         * Tells the app to start with the Page1 page.
         * @param username the username of the person logged-in
         */
        public static void goMainApp(String username) {
            //hide the login background:
            RootPanel.getBodyElement().getStyle().setProperty("background", "none");
            mainAppPresenter = new MainAppPresenter(username);
            RootLayoutPanel.get().clear();
            mainAppPresenter.go(RootLayoutPanel.get());
            //get the center panel:
            container = mainAppPresenter.getContainer();
            //check for reload:
            //NOTE: the token will be empty if the user refreshes 
            //      and navigates all the way back to the zero-state 
            //      from the login screen. 
            //NOTE: this logic may change after user-persistence is implemented
            if(hasHistory() || History.getToken().isEmpty()) {
                // reset the reloaded flag:
                reloaded = false;
                if(History.getToken().isEmpty()) {
                    //land on the first page:
                    History.newItem(AppController.Pages.PAGE1);
                }
                else {
                    MainApp.debug(2, "(!) AppController has History on reload: " + History.getToken());
                    History.fireCurrentHistoryState();
                }
            }
            else {
                //land on the first page:
                History.newItem(AppController.Pages.PAGE1);
            }
    
        }
    
        /**
         * 
         */
        public static void checkIfSessionActive() { 
            loginRpcService.loginFromSession(new AsyncCallback<LoginSummary>() {
                @Override
                public void onFailure(Throwable throwable) {
                    goLoginScreen();
                }
    
                @Override
                public void onSuccess(LoginSummary loginSummary) {
                    if (loginSummary.getErrorString() != null)
                        goLoginScreen();
                    else
                        goMainApp(loginSummary.getUser().getName());
                }
            });
        }
    
        /**
         * 
         */
        public static void sessionLogout() {
            DialogBoxWidget.confirm(200,
                    "Logout",
                    "Are you sure you want to log out?",
                    new ConfirmDialogCallback() {
                        @Override
                        public void onAffirmative() {
    
                            loginRpcService.logout(new AsyncCallback<Void>() {
                                @Override
                                public void onFailure(Throwable throwable) {
                                    goLoginScreen();
                                }
    
                                @Override
                                public void onSuccess(Void aVoid) {
                                    goLoginScreen();
                                }
                            });
                        }
    
                        @Override
                        public void onCancel() {
                        }
                    });
    
        }
    
        /**
         * Shows or hides the "Under Construction" popup if UNDER_CONSTRUCION is true.
         * @param show true to show and false to hide
         */
        public static void showPopup(boolean show) {
            if(MainApp.UNDER_CONSTRUCTION && popup != null) {
                if(show) {
                    popup.show();
                }
                else {
                    popup.hide();
                }
            }
        }
    
        /**
         * Called by every history event fired (including the back and forward buttons).
         * Ignores the login and empty index historically.
         * @param token the name of the page to load
         */
        public static void go(String token) {
            if(reloaded) {
                normalCursor.execute();
            }
            if(token == null || token.isEmpty() || reloaded == true) return;
            MainApp.debug("<history changed> - AppController.go()-> " + token);
            // build the popup message for all unfinished pages:
            if(MainApp.UNDER_CONSTRUCTION) {
                if(popup == null) {
                    popup = new DecoratedPopupPanel(false);
                    popup.ensureDebugId(POPUP_DEMO_ID);
                    popup.addStyleDependentName(POPUP_DEMO_ID);
                    popup.setWidget(new HTML(new Image("images/workingman.png") + POPUP_DEMO_STATEMENT + new Image("images/workingmanFLIP.png")));
                }
            }
            // check token for which page to return:
            if(token.equalsIgnoreCase(External.EXTERNAL_URL_1)) {
                MainAppPresenter.clearActiveMenuItems();
                setExternalContentURL(External.get(token));
            }
            else if(token.equalsIgnoreCase(External.EXTERNAL_URL_2)) {
                MainAppPresenter.clearActiveMenuItems();
                setExternalContentURL(External.get(token));
            }
            else if(token.equalsIgnoreCase(Pages.PAGE1)) {
                setContent(Pages.get(Pages.PAGE1));
                setPage1Content(Pages.get(getLastPage1Page()));
            }
            else if(token.equalsIgnoreCase(Pages.PAGE_1A) ||
                    token.equalsIgnoreCase(Pages.PAGE_1B)) {
                setContent(Pages.get(Pages.PAGE1));
                setPage1Content(Pages.get(token));
            }
            else if(token.equalsIgnoreCase(Pages.PAGE2)) {
                setContent(Pages.get(Pages.PAGE2));
            }
            else { // default behavior for a page not described:
                MainApp.debug(2, "(!) Unknown page: " + token);
                setContent(Pages.get(token));
            }
        }
    
        /**
         * Called by MainApp on a window resize event.
         * @param e the ResizeEvent
         */
        public static void resize(ResizeEvent e) {
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                @Override
                public void execute() {
                    if(mainAppPresenter != null) {
                        mainAppPresenter.resize();
                    }
                    Pages.resize();
                    Scheduler.get().scheduleFinally(resetPopupDimensions);
                }
            });
        }
    
        /**
         * Changes the cursor to "wait" or "auto" depending on the parameter
         * @param wait true to set the cursor to waiting
         */
        private static void waitCursor(Boolean wait) {
            if(wait) {
                RootPanel.getBodyElement().addClassName(CURSOR_WAIT_CLASS);
            }
            else {
                RootPanel.getBodyElement().removeClassName(CURSOR_WAIT_CLASS);
            }
        }
        /**
         * Private Constructor which initializes the Pages object.
         */
        private AppController() {
            Pages.init();
        }
    
        /**
         * Sets the content of the main app container to one of the "Pages."
         * @param c the Composite widget to be added
         */
        private static void setContent(Composite c) {
            container.clear();
            container.add(c.asWidget());
        }
    
        /**
         * Sets the content of the main app container an external URL.
         * @param f the Frame by which external web sites are added
         */ 
        private static void setExternalContentURL(Frame f) {
            container.clear();
            container.add(f);
            // must reset the width and height every time:
            f.getElement().getStyle().setWidth(100, Unit.PCT);
            f.getElement().getStyle().setHeight(100, Unit.PCT);
        }
    
        /**
         * Sets the content of the Page1 container to one of the sub pages.
         * @param c the Composite widget to be added
         */
        private static void setPage1Content(Composite c) {
            page1Container.clear();
            page1Container.add(c.asWidget());
        }
    }
    
    package com.*;
    
    import com.google.gwt.event.logical.shared.AttachEvent;
    import com.google.gwt.user.client.ui.Composite;
    
    /**
     * Base interface for all 'Presenters' used by AppController.java
     * NOTE: classes that implement this interface do not launch the presenter's view 
     * into the provided container; rather, the view is retrieved and used by the 
     * AppController instance by calling the 'view()' method
     */
    public interface AppControlPresenter {
    
        /**
         * Gets the view (for use in AppController.java)
         */
        public Composite view();
    
        /**
         * Indicates if current search data is present and unsaved.
         * @returns true to if a search is still active  
         */
        public boolean unsavedData();
    
        /**
         * Called on resize event to notify presenters with visible
         * components that need resizing for different screen sizes.
         * @returns true if elements were resized
         */
        public boolean resize();
    
        /**
         * Called on attachEvents to tell the presenter to update.
         * @param event the AttachEvent
         */
        public void updateAttachOrDetach(AttachEvent event);
    
        /**
         * Gets the message to display for unsaved data.
         * @returns a message String describing the data
         */
        public String dataDescription();
    
        /**
         *  Gets a fully qualified name for use in comparisons
         * @return the name of this presenter used by the <code>AppController</code>
         */
        public String toString();
    }
    
    package com.*;
    
    import com.google.gwt.user.client.ui.Composite;
    import com.google.gwt.user.client.ui.LayoutPanel;
    
    /**
     */
    public interface AppControlContainerPresenter extends AppControlPresenter {
    
        /**
         * 
         * @return
         */
        public LayoutPanel getContentPane();
    
        /**
         * 
         * @param pageName
         * @return
         */
        public Composite setCurrentPage(String pageName);
    }