/**
* Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
*
* Project home : http://code.daikit.com/daikit4gxt
*
* 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.daikit.daikit4gxt.client.controller;
import java.util.HashMap;
import java.util.Map;
import com.daikit.commons.shared.bean.BaseUser;
import com.daikit.commons.shared.utils.DkObjectUtils;
import com.daikit.daikit4gxt.client.DkMain;
import com.daikit.daikit4gxt.client.action.BaseAction;
import com.daikit.daikit4gxt.client.action.processor.DkConnectionReturnProcessor;
import com.daikit.daikit4gxt.client.action.processor.DkStandardConnectionReturnProcessor;
import com.daikit.daikit4gxt.client.action.standard.BaseConnectAction;
import com.daikit.daikit4gxt.client.action.standard.BaseDisconnectAction;
import com.daikit.daikit4gxt.client.action.standard.BaseInvalidateUiAction;
import com.daikit.daikit4gxt.client.action.standard.BaseLoadInitializationDataAction;
import com.daikit.daikit4gxt.client.action.standard.BaseOnApplicationLoadedAction;
import com.daikit.daikit4gxt.client.action.standard.BaseOnStandaloneApplicationLoadedAction;
import com.daikit.daikit4gxt.client.action.standard.BaseReloadCurrentScreenAction;
import com.daikit.daikit4gxt.client.action.standard.BaseShowScreenAction;
import com.daikit.daikit4gxt.client.event.DkDisconnectEvent;
import com.daikit.daikit4gxt.client.event.DkDisconnectEvent.DisconnectEventHandler;
import com.daikit.daikit4gxt.client.event.DkDisconnectEvent.HasDisconnectEventHandlers;
import com.daikit.daikit4gxt.client.exception.ApplicationInitializationException;
import com.daikit.daikit4gxt.client.log.BaseLogger;
import com.daikit.daikit4gxt.client.rpc.BaseRpcMiscs;
import com.daikit.daikit4gxt.client.rpc.BaseRpcMiscsAsync;
import com.daikit.daikit4gxt.client.rpc.BaseRpcUser;
import com.daikit.daikit4gxt.client.rpc.BaseRpcUserAsync;
import com.daikit.daikit4gxt.client.screen.Screen;
import com.daikit.daikit4gxt.client.screen.WelcomeScreen;
import com.daikit.daikit4gxt.client.ui.BaseGui;
import com.daikit.daikit4gxt.client.ui.popup.ConnectionPopup;
import com.daikit.daikit4gxt.shared.bean.ConnectionData;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.form.FormPanel;
/**
* Application controller class that all applications should extend.
*
* @author tcaselli
* @version $Revision$ Last modifier: $Author$ Last commit: $Date$
*/
public abstract class BaseMainController implements HasDisconnectEventHandlers
{
private final BaseLogger log = BaseLogger.getLog(BaseMainController.class);
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// ATTRIBUTES
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* The Graphic User Interface.
*/
private final BaseGui gui;
private Screen currentScreen = null;
private Screen previousScreen = null;
private DkConnectionReturnProcessor connectionReturnProcessor = new DkStandardConnectionReturnProcessor();
private String connectionPopupMessage = null;
private boolean isInvalidatingUI = false;
private String screenDisplayId;
private final Map<String, Screen> createdScreens = new HashMap<String, Screen>();
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// CREATION
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Only constructor.
*
* @param gui
* the application graphical user interface extending {@link BaseGui}
*/
public BaseMainController(final BaseGui gui)
{
this.gui = gui;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// ACTIONS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* @return whether a chain action is currently running.
*/
public boolean isChainActionRunning()
{
return BaseAction.isChainRunning();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// CONNECTION
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* @return the connection popup instance
*/
public ConnectionPopup getConnectionPopupInstance()
{
return ConnectionPopup.instance();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// SCREEN MANAGEMENT
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Call onBeforeScreenLeft() on the current screen if not null.
*
* @param currentAction
* the current chain action. To be able to restart it after.
* @return whether or not the current screen can be changed to the given currentScreen.
*/
public boolean onBeforeScreenLeft(final BaseAction<?> currentAction)
{
return this.currentScreen == null || this.currentScreen.onBeforeScreenLeft(currentAction);
}
/**
* Call onScreenBeforeShow(previousDisplayedScreen) on the current screen. After the call to this function the
* mainController.currentScreen is set to this and the show screen process (layout and chain actions) is done.
*
* @param previousDisplayedScreen
* the previous displayed screen
* @param optionalArgs
* optional arguments
*/
public void onScreenBeforeShow(final Screen previousDisplayedScreen, final Object... optionalArgs)
{
if (currentScreen != null)
{
currentScreen.baseBeforeScreenShow(previousDisplayedScreen, optionalArgs);
}
}
/**
* Create a {@link Screen} instance from the given class.<br>
* This screen must be specified in the {@link #getSpecificScreenInstance(Class)} implementation.
*
* @param clazz
* the class of the screen to be created
* @return the created screen instance
*/
@SuppressWarnings("unchecked")
public final <S extends Screen> S getScreenInstance(final Class<S> clazz)
{
S ret = (S) createdScreens.get(clazz.getName());
if (ret == null)
{
final String className = clazz.getName();
if (WelcomeScreen.class.getName().equals(className))
{
ret = (S) new WelcomeScreen();
createdScreens.put(className, ret);
}
else
{
ret = (S) getSpecificScreenInstance(clazz);
if (ret != null)
{
createdScreens.put(className, ret);
}
else
{
throw new ApplicationInitializationException("The given Screen class : " + className
+ " is not referenced in the Screen Creator.");
}
}
log.debug("[ShowScreenAction Screen {" + className + "} Created]");
}
return ret;
}
/**
* Method to be overridden to provide a way to access the default application screen. This screen will be loaded
* first when the application starts.
*
* @return the default {@link Screen} class.
*/
public abstract Class<? extends Screen> getDefaultScreen();
/**
* Method to be used to know if a screen is the currently displayed screen.
*
* @param screenClass
* the {@link Screen} class to test if it is the currently displayed screen.
*
* @return whether the given screen is the current one
*/
public boolean isCurrentScreen(final Class<? extends Screen> screenClass)
{
return DkObjectUtils.equalsWithNull(screenClass, getCurrentScreen() == null ? null : getCurrentScreen().getClass());
}
/**
* Show the screen corresponding to the given class.<br>
* Given parameters will be sent to the screen via the {@link Screen#initializeScreen(Object...)} and
* {@link Screen#getReloadScreenAction(boolean, Object...)} methods.
*
* @param screen
* the {@link Screen} to be shown.
* @param initializationArgs
* parameters to be sent to the screen for initialization and reload.
*/
public void showScreen(final Class<? extends Screen> screen, final Object... initializationArgs)
{
BaseShowScreenAction.get(screen, initializationArgs).execute();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// REFRESH / RELOAD CURRENT SCREEN
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Wrapper to {@link #refreshScreen(boolean, Object...)} with force=false and no optional argument
*/
public void refreshScreen()
{
refreshScreen(false);
}
/**
* Refresh current screen by getting the screen reload action and executing it after having called
* {@link Screen#onBeforeScreenRefresh(BaseAction)}
*
* @param force
* to force layout refresh
* @param optionalArgs
* to pass optional arguments to {@link Screen#getReloadScreenAction(boolean, Object...)} method.
*/
public void refreshScreen(final boolean force, final Object... optionalArgs)
{
final BaseAction<?> reloadScreenAction = BaseReloadCurrentScreenAction.get(force, optionalArgs);
if (reloadScreenAction != null)
{
if (currentScreen.onBeforeScreenRefresh(reloadScreenAction))
{
reloadScreenAction.execute();
}
}
else
{
log.error("Don't call Main.controller().refreshScreen() before having shown the screen.");
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// URL METHODS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Call URL , for example to download a file.
*
* @param url
* the url to be called (must be absolute starting with http://)
*/
public void callUrl(final String url)
{
final FormPanel hyperlinkFormPanel = gui.getHyperlinkCallerFormPanel();
hyperlinkFormPanel.setAction(url);
log.debug("CALL URL : " + url);
hyperlinkFormPanel.submit();
}
/**
* Open a new window (or tab depending on the browser) and display the page with the given url.
*
* @param url
* the url to be called (must be absolute starting with http://)
*/
public void openUrlInNewWindow(final String url)
{
openHyperlinkInNewWindow(url);
}
private static native void openHyperlinkInNewWindow(String url)/*-{
window.open(url);
}-*/;
/**
* Open a new tab (or window depending on the browser) and display the page with the given url.
*
* @param url
* the url to be called (must be absolute starting with http://)
*/
public void openUrlInNewTab(final String url)
{
openHyperlinkInNewTab(url);
}
private static native void openHyperlinkInNewTab(String url)/*-{
window.open(url, '_blank');
}-*/;
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// METHODS TO BE OVERRIDEN
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Method intended to create Screen instances according to their Class. This is useful because there is no way to
* instantiate an Object from its class excepted with a call to "new Class(...)". Class.newInstance() is not
* compilable with GWT.
*
* @param clazz
* the Class for the instance to be created.
* @return a Screen object
*/
protected abstract <S extends Screen> Screen getSpecificScreenInstance(Class<S> clazz);
/**
* Return the action to be executed after Connection success. May be overridden to provide custom action.
*
* @param previousLoggedUser
* the previous logged user
* @return the created {@link BaseAction}
*/
public BaseAction<?> getSpecificAfterConnectAction(final BaseUser previousLoggedUser)
{
return BaseLoadInitializationDataAction.get();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// RPCS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected BaseRpcUserAsync rpcUser;
protected BaseRpcMiscsAsync rpcMiscs;
/**
* Method to be overridden if you want to override {@link BaseRpcUser}
*
* @return the asynchronous interface client side implementation.
*/
public BaseRpcUserAsync baseRpcUser()
{
if (rpcUser == null)
{
rpcUser = GWT.create(BaseRpcUser.class);
}
return rpcUser;
}
/**
* Method to be overridden if you want to override {@link BaseRpcMiscs}
*
* @return the asynchronous interface client side implementation.
*/
public BaseRpcMiscsAsync baseRpcMiscs()
{
if (rpcMiscs == null)
{
rpcMiscs = GWT.create(BaseRpcMiscs.class);
}
return rpcMiscs;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// METHODS - UI
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Do invalidate UI without action (GUI+ currentScreen). This method is also called by the
* {@link BaseInvalidateUiAction}.
*
* @param doForceLayout
* to force the application layout refresh
*/
public void invalidateUi(final boolean doForceLayout)
{
isInvalidatingUI = true;
gui.invalidateUi();
if (currentScreen != null)
{
currentScreen.baseInvalidateUi();
}
if (doForceLayout)
{
gui.getViewport().forceLayout();
}
isInvalidatingUI = false;
}
/**
* Method called when user wants to changed full screen status.
*
* @param fullScreen
* indicating whether the application should now be displayed in full screen.
*/
public void onFullScreen(final boolean fullScreen)
{
DkMain.model().setFullSize(fullScreen);
gui.onFullScreenChanged();
if (currentScreen != null)
{
currentScreen.baseOnFullScreenChanged();
}
invalidateUi(true);
}
/**
* Method called once the application is loaded. Wrapper to {@link #getOnApplicationLoadedAction()}.execute()
*/
public void onApplicationLoaded()
{
getOnApplicationLoadedAction().execute();
}
/**
*
* @return the {@link BaseAction} to be executed once the application is loaded.
*/
public BaseAction<?> getOnApplicationLoadedAction()
{
return DkMain.config().isStandalone() ? BaseOnStandaloneApplicationLoadedAction.get() : BaseOnApplicationLoadedAction.get();
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// GETTERS / SETTERS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* @return the graphical user interface extending {@link BaseGui}
*/
public BaseGui getGui()
{
return gui;
}
/**
* @return the current screen extending {@link Screen}
*/
public Screen getCurrentScreen()
{
return currentScreen;
}
/**
* Set the current screen. This method <b>MUST NOT</b> be called directly but only via the action
* {@link BaseShowScreenAction} or the controller method {@link #showScreen(Class, Object...)}
*
* @param currentScreen
* the screen instance to be shown.
*/
public final void setCurrentScreen(final Screen currentScreen)
{
this.currentScreen = currentScreen;
}
/**
* @return the previous screen extending {@link Screen}
*/
public Screen getPreviousScreen()
{
return previousScreen;
}
/**
* Sets the previous displayed screen. This method <b>MUST NOT</b> be called directly but only via the action
* {@link BaseShowScreenAction} or the controller method {@link #showScreen(Class, Object...)}
*
* @param previousScreen
* the previousScreen to set
*/
public void setPreviousScreen(final Screen previousScreen)
{
this.previousScreen = previousScreen;
}
/**
* @return the connectionPopupMessage
*/
public String getConnectionPopupMessage()
{
return connectionPopupMessage;
}
/**
* @param connectionPopupMessage
* the connectionPopupMessage to set
*/
public void setConnectionPopupMessage(final String connectionPopupMessage)
{
this.connectionPopupMessage = connectionPopupMessage;
}
/**
* @return the connectionReturnProcessor
*/
public DkConnectionReturnProcessor getConnectionReturnProcessor()
{
return connectionReturnProcessor;
}
/**
* @param connectionReturnProcessor
* the connectionReturnProcessor to set
*/
public void setConnectionReturnProcessor(final DkConnectionReturnProcessor connectionReturnProcessor)
{
this.connectionReturnProcessor = connectionReturnProcessor;
}
/**
* @return the screenDisplayId
*/
public String getScreenDisplayId()
{
return screenDisplayId;
}
/**
* @param screenDisplayId
* the screenDisplayId to set
*/
public void setScreenDisplayId(final String screenDisplayId)
{
this.screenDisplayId = screenDisplayId;
}
/**
* @return the isInvalidatingUI
*/
public boolean isInvalidatingUI()
{
return isInvalidatingUI;
}
/**
* @param isInvalidatingUI
* the isInvalidatingUI to set
*/
public void setInvalidatingUI(final boolean isInvalidatingUI)
{
this.isInvalidatingUI = isInvalidatingUI;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// CONNECTION / DISCONNECTION IN CASE OF STANDALONE APPLICATIONS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* In case of a stand-alone application this method execute the {@link BaseConnectAction} with given parameters
*
* @param connectionData
* the {@link ConnectionData}
*/
public void doConnect(final ConnectionData connectionData)
{
doConnect(connectionData, null);
}
/**
* In case of a stand-alone application this method execute the {@link BaseConnectAction} with given parameters
*
* @param connectionData
* the {@link ConnectionData}
* @param customChainActionIfLoginSucceded
* an eventual chain action to execute if connection succeeded (it may be null)
*/
public void doConnect(final ConnectionData connectionData, final BaseAction<?> customChainActionIfLoginSucceded)
{
BaseConnectAction.get(connectionData, customChainActionIfLoginSucceded).execute();
}
/**
* In case of a stand-alone application this method execute the {@link BaseDisconnectAction}
*/
public void doDisconnect()
{
BaseDisconnectAction.get().execute();
}
/**
* Call onBeforeDisconnect() on the current screen if not null.
*
* @param currentAction
* the current chain action. To be able to restart it after.
* @return whether or not the user can disconnect now. For example if there are things to be saved before you can
* return false in this method and display a pop-up asking to save.
*/
public boolean onBeforeDisconnect(final BaseAction<?> currentAction)
{
return this.currentScreen == null || this.currentScreen.onBeforeDisconnect(currentAction);
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// EVENT HANDLING
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* @see Component#fireEvent(GwtEvent)
* @param event
* the event
*/
public void fireEvent(final GwtEvent<?> event)
{
getGui().getViewport().fireEvent(event);
}
@Override
public HandlerRegistration addDisconnectEventHandler(final DisconnectEventHandler handler)
{
return getGui().getViewport().addHandler(handler, DkDisconnectEvent.getType());
}
}