/**
* 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.screen;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.daikit.daikit4gxt.client.DkMain;
import com.daikit.daikit4gxt.client.action.BaseAction;
import com.daikit.daikit4gxt.client.action.standard.BaseReloadCurrentScreenAction;
import com.daikit.daikit4gxt.client.controller.BaseMainController;
import com.daikit.daikit4gxt.client.log.BaseLogger;
import com.daikit.daikit4gxt.client.ui.UIInvalidatable;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.TabPanel;
import com.sencha.gxt.widget.core.client.container.Container;
import com.sencha.gxt.widget.core.client.event.AddEvent;
import com.sencha.gxt.widget.core.client.event.AddEvent.AddHandler;
/**
* Methods that should be overridden to provide custom mechanism :<br>
* - {@link #invalidateUi()}<br>
* - {@link #onFullScreenChanged()}<br>
* - {@link #initializeScreen(Object...)}<br>
* - {@link #screenNeedToBeReloadedRegardlessOfSubPanels()}<br>
* - {@link #getReloadScreenAction(boolean, Object...)}<br>
* - {@link #onBeforeScreenShow(Screen)}<br>
* - {@link #onBeforeScreenLeft(BaseAction)}<br>
* - {@link #onBeforeDisconnect(BaseAction)}<br>
*
* @author Thibaut CASELLI
*
*/
public abstract class Screen extends ContentPanel implements UIInvalidatable
{
protected final BaseLogger log = BaseLogger.getLog(getClass());
private boolean isFirstReloadAfterShow = true;
/**
* Constructor
*/
public Screen()
{
setFillBackgroundColor(true);
addAddHandler(new RegisterChildrenAddHandler());
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// SUB PANELS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected final List<ScreenSubPanel> subPanels = new ArrayList<ScreenSubPanel>();
protected final List<UIInvalidatable> uiInvalidateables = new ArrayList<UIInvalidatable>();
private final void registerSubPanel(final ScreenSubPanel screenSubPanel)
{
subPanels.add(screenSubPanel);
}
private class RegisterChildrenAddHandler implements AddHandler
{
@Override
public void onAdd(final AddEvent event)
{
final Widget addedWidget = event.getWidget();
recursiveRegisterScreenSubPanel(addedWidget);
}
}
private void recursiveRegisterScreenSubPanel(final Widget widget)
{
if (widget instanceof UIInvalidatable && !uiInvalidateables.contains(widget))
{
uiInvalidateables.add((UIInvalidatable) widget);
}
Container container = null;
Panel panel = null;
if (widget instanceof Container)
{
container = (Container) widget;
}
else if (widget instanceof TabPanel)
{
container = ((TabPanel) widget).getContainer();
}
else if (widget instanceof Panel)
{
panel = (Panel) widget;
}
if (panel != null)
{
for (final Widget widget2 : (Panel) widget)
{
recursiveRegisterScreenSubPanel(widget2);
}
}
else if (container != null)
{
container.addAddHandler(new RegisterChildrenAddHandler());
if (container instanceof ScreenSubPanel)
{
final ScreenSubPanel widgetAsScreenSubPanel = (ScreenSubPanel) container;
if (!subPanels.contains(widgetAsScreenSubPanel))
{
Screen.this.registerSubPanel(widgetAsScreenSubPanel);
}
widgetAsScreenSubPanel.setParentScreen(Screen.this);
uiInvalidateables.remove(widgetAsScreenSubPanel);
}
for (final Widget widgetChild : container)
{
recursiveRegisterScreenSubPanel(widgetChild);
}
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// INVALIDATE UI
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Method called by invalidateUI action and MainController.invalidateUi(force)
*/
public final void baseInvalidateUi()
{
invalidateUi();
for (final ScreenSubPanel screenSubPanel : subPanels)
{
screenSubPanel.baseInvalidateUi();
}
final List<UIInvalidatable> toRemove = new ArrayList<UIInvalidatable>();
for (final UIInvalidatable invalidateable : uiInvalidateables)
{
// if (invalidateable instanceof Widget && !((Widget) invalidateable).isAttached())
// {
// toRemove.add(invalidateable);
// // BaseLogger.debug(getClass(), "Remove UIInvalidateable : " + invalidateable);
// }
// else
// {
invalidateable.invalidateUi();
// }
}
uiInvalidateables.removeAll(toRemove);
isFirstReloadAfterShow = false;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// ON FULL SCREEN CHANGED
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Method to be overridden to provide custom behavior when the full screen status changed.
*/
public void onFullScreenChanged()
{
// Nothing done by default
}
/**
* Method called by the {@link BaseMainController} when the full screen status changed.
*/
public final void baseOnFullScreenChanged()
{
onFullScreenChanged();
for (final ScreenSubPanel screenSubPanel : subPanels)
{
screenSubPanel.onFullScreenChanged();
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// INITIALIZE SCREEN ACTIONS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
public void initializeScreen(final Object... args)
{
// Nothing done by default
}
public final BaseAction<?> getBaseInitializeScreenAction(final Object... args)
{
final BaseAction<Void> ret = new BaseAction<Void>(DkMain.i18n().action_initialize_screen_loading_label(), true)
{
@Override
protected void run()
{
initializeScreen(args);
Collections.sort(subPanels);
for (final ScreenSubPanel screenSubPanel : subPanels)
{
screenSubPanel.initializeScreenSubPanel(args);
}
onSuccess();
}
};
return ret;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// RELOAD SCREEN ACTIONS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
protected boolean screenNeedToBeReloadedRegardlessOfSubPanels()
{
return true;
}
public final boolean screenOrSubPanelNeedToBeReloaded()
{
boolean needToBeReloaded = screenNeedToBeReloadedRegardlessOfSubPanels();
final Iterator<ScreenSubPanel> it = subPanels.iterator();
while (!needToBeReloaded && it.hasNext())
{
if (it.next().needToBeReloaded())
{
needToBeReloaded = true;
}
}
return needToBeReloaded;
}
public abstract BaseAction<?> getReloadScreenAction(final boolean force, final Object... optionalArgs);
public BaseAction<?> getEndOfReloadScreenAction(final boolean force, final Object... optionalArgs)
{
// Nothing done by default
return null;
}
/**
* Method called in the {@link BaseReloadCurrentScreenAction} chain. This action chain is initiated by the
* {@link MainController} and is intended to provide a refresh of the current screen.
*
* @return The {@link BaseAction} created.
*/
public final BaseAction<?> getBaseReloadScreenAction(final boolean force, final Object... optionalArgs)
{
BaseAction<?> ret = null;
if (force || screenOrSubPanelNeedToBeReloaded())
{
// Create Reload action
final List<BaseAction<?>> actionsReload = new ArrayList<BaseAction<?>>();
final BaseAction<?> actionReloadScreen = getReloadScreenAction(force, optionalArgs);
if (actionReloadScreen != null)
{
actionsReload.add(actionReloadScreen);
}
for (final ScreenSubPanel screenSubPanel : subPanels)
{
if (force || screenSubPanel.needToBeReloaded())
{
final BaseAction<?> actionReloadScreenSubPanel = screenSubPanel.getReloadSubPanelAction(force, optionalArgs);
if (actionReloadScreenSubPanel != null)
{
actionsReload.add(actionReloadScreenSubPanel);
}
}
}
// Create chain action.
for (final BaseAction<?> baseAction : actionsReload)
{
if (ret == null)
{
ret = baseAction;
}
else
{
ret.setLastChainAction(baseAction);
}
}
}
return ret;
}
/**
* Method called in the {@link BaseReloadCurrentScreenAction} chain. This action chain is initiated by the
* {@link MainController} and is intended to provide the action chain to be executed once the screen has been
* reloaded. This will be executed after the common invalidateUi action.
*
* @return The {@link BaseAction} created.
*/
public final BaseAction<?> getBaseEndOfReloadScreenAction(final boolean force, final Object... optionalArgs)
{
BaseAction<?> ret = null;
if (force || screenOrSubPanelNeedToBeReloaded())
{
// Create end of reload action
final List<BaseAction<?>> actionsEndOfReload = new ArrayList<BaseAction<?>>();
final BaseAction<?> actionEndOfReloadScreen = getEndOfReloadScreenAction(force, optionalArgs);
if (actionEndOfReloadScreen != null)
{
actionsEndOfReload.add(actionEndOfReloadScreen);
}
for (final ScreenSubPanel screenSubPanel : subPanels)
{
if (force || screenSubPanel.needToBeReloaded())
{
final BaseAction<?> actionEndOfReloadScreenSubPanel = screenSubPanel.getEndOfReloadScreenActionForSubPanel(force,
optionalArgs);
if (actionEndOfReloadScreenSubPanel != null)
{
actionsEndOfReload.add(actionEndOfReloadScreenSubPanel);
}
}
}
for (final BaseAction<?> baseAction : actionsEndOfReload)
{
if (ret == null)
{
ret = baseAction;
}
else
{
ret.setLastChainAction(baseAction);
}
}
}
return ret;
}
public final boolean isFirstReloadAfterShow()
{
return isFirstReloadAfterShow;
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// CALLBACKS BEFORE SCREEN SHOW / LEFT OR DISCONNECT
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Function called before this screen is shown. 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 previously displayed screen.
* @param optionalArgs
* optional arguments
*/
public void onBeforeScreenShow(final Screen previousDisplayedScreen, final Object... optionalArgs)
{
// nothing done by default.
}
/**
* Function called when this screen is asked to be left.
*
* @return whether or not this screen can be left. If return is false, the screen won't be left and the current
* action will be stopped.
* @param currentAction
* the current chain action. To be able to restart it after.
*/
public boolean onBeforeScreenLeft(final BaseAction<?> currentAction)
{
// nothing done by default.
return true;
}
/**
* Function called before a refresh screen process is initiated. This method is only called if the screen refresh is
* initiated from the {@link BaseMainController#refreshScreen(boolean, Object...)} or
* {@link BaseMainController#refreshScreen()} method. Not if the refresh screen is initiated directly from the
* {@link BaseReloadCurrentScreenAction} inside any action chain.
*
* @param currentRefreshAction
* the current refresh screen chain action. To be able to execute it after a potential asynchronous action.
* @return whether or not this screen can be refreshed. If return is false, the screen won't be refreshed and the
* current refresh screen action will be aborted.
*/
public boolean onBeforeScreenRefresh(final BaseAction<?> currentRefreshAction)
{
// nothing done by default
return true;
}
/**
* Method called by the {@link BaseMainController#onScreenBeforeShow(Screen, Object...)} and not intended to be
* called anywhere else. Use {@link #onBeforeScreenShow(Screen)} instead.
*
* @param previousDisplayedScreen
* the previously displayed screen.
* @param optionalArgs
* optional arguments
*/
public final void baseBeforeScreenShow(final Screen previousDisplayedScreen, final Object... optionalArgs)
{
isFirstReloadAfterShow = true;
onBeforeScreenShow(previousDisplayedScreen, optionalArgs);
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// UTILS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer(1000);
sb.append("(" + this.getClass().getName() + ":heading=").append(getTitle());
return sb.append(")").toString();
}
public void setFillBackgroundColor(final boolean fill)
{
if (fill)
{
addStyleName("fill-background-color");
}
else
{
removeStyleName("fill-background-color");
}
}
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// DISCONNECTION IN CASE OF STANDALONE APPLICATIONS
// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
/**
* Function called when the user want to get disconnected.
*
* @return whether or not the user can disconnect. If return is false, the user won't disconnect and the current
* action will be stopped.
* @param the
* current chain action. To be able to restart it after.
*/
public boolean onBeforeDisconnect(final BaseAction<?> currentAction)
{
// nothing done by default.
return true;
}
}