/** * Sencha GXT 3.0.0b - Sencha for GWT * Copyright(c) 2007-2012, Sencha, Inc. * licensing@sencha.com * * http://www.sencha.com/products/gxt/license/ */ package com.sencha.gxt.desktopapp.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.GWT.UncaughtExceptionHandler; import com.google.gwt.user.client.History; import com.google.gwt.user.client.ui.Frame; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.UIObject; import com.sencha.gxt.desktop.client.layout.DesktopLayoutType; import com.sencha.gxt.desktop.client.widget.ShortcutCell.ShortcutCellAppearance; import com.sencha.gxt.desktopapp.client.event.LoginEvent; import com.sencha.gxt.desktopapp.client.event.LoginEvent.LoginHandler; import com.sencha.gxt.desktopapp.client.event.LogoutEvent; import com.sencha.gxt.desktopapp.client.event.LogoutEvent.LogoutHandler; import com.sencha.gxt.desktopapp.client.service.LoginServiceProvider; import com.sencha.gxt.desktopapp.client.service.ProfileServiceProvider; import com.sencha.gxt.desktopapp.client.utility.Prompt; import com.sencha.gxt.widget.core.client.box.AlertMessageBox; import com.sencha.gxt.widget.core.client.form.HtmlEditor; import com.sencha.gxt.widget.core.client.grid.Grid; import com.sencha.gxt.widget.core.client.treegrid.TreeGrid; /** * A sample application that provides a basic desktop capability in a browser * window. This includes a start menu, shortcuts and a task bar with buttons for * each active application, as well as support for multiple users, each with * their own profile settings. Desktop applications include: * <dl> * <dt>File Manager</dt> * <dd>Uses a {@link TreeGrid} to provides access to a hierarchical, * browser-based file system, which is persistent on browsers that support HTML5 * Local Storage. Users may create folders, documents, browser shortcuts, and * other files, rename files and drag and drop to move files in the file system. * As file operations occur, open applications respond appropriately (e.g. to * indicate renamed or moved files).</dd> * <dt>Word Processor</dt> * <dd>Uses a {@link HtmlEditor} to provides a simple rich editing capability * with documents that can be stored locally in the browser based file system. * Automatically propagates saved changes to other Word Processor windows, * closes if the document is deleted in File Manager, as well as handling * documents that are renamed or moved using File Manager.</dd> * <dt>Browser</dt> * <dd>Uses a {@link Frame} to provide a browser-within-a-browser that displays * a browser web page within the desktop. Although this may sound like a * gimmick, it's amazing how useful it can be to display multiple web pages * simultaneously in conjunction with the {@link DesktopLayoutType#TILE} * feature.</dd> * <dt>Spreadsheet</dt> * <dd>Uses a {@link Grid} to provide a simple spreadsheet capability, including * sorting, column and row move, insert and delete as well as formulas and * charts.</dd> * <dt>Program</dt> * <dd>Uses <code>JSNI</code> to provide a simple JavaScript snippet * interpreter, including use of references to the page's window object and * call-backs to a Java method.</dd> * </dl> * In addition to highlighting the capability of the GXT components and * providing useful, reusable function, the desktop application illustrates a * number of best practices for creating complex, interactive real-world * applications. * <p/> * <ul> * <li>Use of an enhanced Model View Presenter (MVP) pattern to improve * separation of concerns and simplify testing.</li> * <ul> * <li>Model - the structured data used by the application</li> * <li>View - the components that display information and receive user input. * The View receives data and commands from the Presenter via the View interface * and sends Events to the Presenter using the Presenter interface. The actual * widgets the View uses to display the data are hidden from the Presenter. To * paraphrase Einstein, "The View is as simple as possible, but no simpler."</li> * <li>Presenter - the components that prepare Models for display and act on * events from the user. The Presenter sends commands and data to the View via * the View interface. The Presenter receives events from the View via the * Presenter interface. The Presenter is generally responsible for creating the * View and connecting it to the application's user interface.</li> * </ul> * <li>Use of a Bus to decouple providers and consumers</li> * <ul> * <li>Event Bus - broadcast application level events from one producer to zero * or more consumers</li> * <li>Service Bus - request a service (with optional response) from one and * only one provider</li> * </ul> * <li>Use of encapsulated field access in declaring type to support lazy * instantiation and initialization as well as implicit order of execution</li> * </ul> * <p/> * In addition, this application illustrates the use of a number of GWT and * Sencha technologies, including: * <ul> * <li>GWT Editor / Driver Framework - {@link LoginView}</li> * <li>GWT AutoBeans - {@link ProfileModel} and {@link ProfileFactory}</li> * <li>GWT / Sencha Appearances - {@link ShortcutCellAppearance}</li> * </ul> * <p/> * If the browser does not support HTML5 Local Storage, or it is not configured * for use with the application, this application will prompt the user whether * to degrade gracefully to temporary (in-memory) storage that will be freed * when the browser terminates or the the browser window is refreshed. * <p/> * To request that the desktop application prompt the user to clear all local * storage for all users of this application, add <code>#clear</code> to the * application URL. * <p/> * This sample application is under continuing development. The API is subject * to change without notice. */ public class DesktopApp implements EntryPoint { private DesktopBus desktopBus; private DesktopAppPresenter desktopAppPresenter; private LoginHandler loginHandler; private LoginServiceProvider loginServiceProvider; private ProfileServiceProvider profileServiceProvider; private LogoutHandler logoutHandler; private LoginPresenter loginPresenter; private ProfilePresenter profilePresenter; public void loadModule(HasWidgets hasWidgets) { setBackground(hasWidgets); initializeDesktopBus(hasWidgets); if (!getDesktopAppPresenter().isLocalStorageSupported()) { loadApplicationAfterAlertingUser(hasWidgets); } else { loadApplication(hasWidgets); } } @Override public void onModuleLoad() { setUncaughtExceptionHandler(); loadModule(RootPanel.get()); } private void checkForLogin(HasWidgets hasWidgets) { if (getDesktopAppPresenter().isLoggedIn()) { displayView(hasWidgets); } else { getDesktopBus().invokeLoginService(); } } private void displayView(HasWidgets hasWidgets) { getDesktopAppPresenter().go(hasWidgets); } private DesktopAppPresenter getDesktopAppPresenter() { if (desktopAppPresenter == null) { desktopAppPresenter = new DesktopAppPresenterImpl(getDesktopBus()); } return desktopAppPresenter; } private DesktopBus getDesktopBus() { if (desktopBus == null) { desktopBus = new DesktopBus(); } return desktopBus; } private LoginHandler getLoginHandler(final HasWidgets hasWidgets) { if (loginHandler == null) { loginHandler = new LoginHandler() { @Override public void onLogin(LoginEvent loginEvent) { displayView(hasWidgets); if (loginEvent.isNewUser()) { getDesktopBus().invokeProfileService(); } } }; } return loginHandler; } private LoginPresenter getLoginPresenter() { if (loginPresenter == null) { loginPresenter = new LoginPresenterImpl(getDesktopAppPresenter()); } return loginPresenter; } private LoginServiceProvider getLoginServiceProvider(HasWidgets rootPanel) { if (loginServiceProvider == null) { loginServiceProvider = new LoginServiceProvider(getLoginPresenter(), rootPanel); } return loginServiceProvider; } private LogoutHandler getLogoutHandler() { if (logoutHandler == null) { logoutHandler = new LogoutHandler() { @Override public void onLogout(LogoutEvent logoutEvent) { com.google.gwt.user.client.Window.Location.reload(); } }; } return logoutHandler; } private ProfilePresenter getProfilePresenter() { if (profilePresenter == null) { profilePresenter = new ProfilePresenterImpl(getDesktopAppPresenter()); } return profilePresenter; } private ProfileServiceProvider getProfileServiceProvider(HasWidgets hasWidgets) { if (profileServiceProvider == null) { profileServiceProvider = new ProfileServiceProvider(getProfilePresenter(), hasWidgets); } return profileServiceProvider; } private void initializeDesktopBus(HasWidgets hasWidgets) { getDesktopBus().registerLoginService(getLoginServiceProvider(hasWidgets)); getDesktopBus().registerProfileService(getProfileServiceProvider(hasWidgets)); getDesktopBus().addLoginHandler(getLoginHandler(hasWidgets)); getDesktopBus().addLogoutHandler(getLogoutHandler()); } private void loadApplication(HasWidgets hasWidgets) { if ("clear".equals(History.getToken())) { promptToClearStorage(hasWidgets); } else { checkForLogin(hasWidgets); } } private void loadApplicationAfterAlertingUser(final HasWidgets hasWidgets) { Prompt.get().alert( "Local Storage is Not Supported", "Either your browser does not support HTML5 Local Storage or it is not configured for use by this application.<br/><br/>This application will continue to run, but anything you create will be discarded when the browser terminates or the browser window is refreshed.", new Runnable() { @Override public void run() { loadApplication(hasWidgets); } }); } private void promptToClearStorage(final HasWidgets hasWidgets) { Prompt.get().confirm("Desktop", "Would you like to clear this domain's local storage before continuing?", new Runnable() { @Override public void run() { getDesktopAppPresenter().clearLocalStorage(); checkForLogin(hasWidgets); } }, new Runnable() { @Override public void run() { checkForLogin(hasWidgets); } }); } private void setBackground(HasWidgets hasWidgets) { if (hasWidgets instanceof UIObject) { ((UIObject) hasWidgets).addStyleName("x-desktop"); } } private void setUncaughtExceptionHandler() { GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void onUncaughtException(Throwable e) { e.printStackTrace(); Throwable rootCause = getRootCause(e); new AlertMessageBox("Exception", rootCause.toString()).show(); } }); } private Throwable getRootCause(Throwable e) { Throwable lastCause; do { lastCause = e; } while ((e = e.getCause()) != null); return lastCause; } }