at.spardat.xma.appshell.AppShell Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*
* @(#) $Id: AppShell.java 9627 2012-06-22 10:32:05Z hoenninger $
*
*
*
*
*/
package at.spardat.xma.appshell;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import at.spardat.xma.boot.component.IXMAControl;
import at.spardat.xma.component.ComponentClient;
import at.spardat.xma.component.EmbeddableComponent;
import at.spardat.xma.page.DialogPage;
import at.spardat.xma.page.IEmbeddable;
import at.spardat.xma.page.PageClient;
/**
* Base class for application shells in XMA. An application shell is the window opened at startup
* of the client side application. It stays open during the whole user session. It typically contains
* a header area showing some context information, a menu area containing the menu tree of the
* application and a client area showing the currently active component.
*
* @author s2877
* @since 1.4.0
*/
public abstract class AppShell extends DialogPage implements IAppShell {
AppShellDelegate[] delegates = new AppShellDelegate[0];
/**
* Initializes an AppShell.
*
* @param component the Component containing the DialogPage.
* @param stateless indicating if this page is stateless on the server.
* @param style The SWT-Style for the Shell of the DialogPage.
* @throws IllegalArgumentException if component is null.
*/
public AppShell(ComponentClient component, boolean stateless, int style) {
super(component, stateless, style);
}
/**
* Initializes an AppShell inside a given Component.
*
* @param component the Component containing the DialogPage.
* @param parentShell the Shell which shall be the parent of the Shell of the DialogPage.
* @param style The SWT-Style for the Shell of the DialogPage.
* @param stateless indicating if this page is stateless on the server.
* @throws IllegalArgumentException if component is null.
*/
public AppShell(ComponentClient component, Shell parentShell, boolean stateless, int style) {
super(component,parentShell,stateless,style);
}
/**
* Initializes an AppShell inside the same Component as the parent PageClient.
*
* @param parent the PageClient calling this DialogPage.
* @param stateless indicating if this page is stateless on the server.
* @param style The SWT-Style for the Shell of the DialogPage.
*/
public AppShell(PageClient parent, boolean stateless, int style) {
super(parent,stateless,style);
}
// TODO: comment me
public void addDelegate ( AppShellDelegate delegate ) {
delegate.setAppShell(this);
AppShellDelegate[] newDelegates = new AppShellDelegate[delegates.length + 1];
System.arraycopy(delegates,0,newDelegates,0,delegates.length);
newDelegates[delegates.length] = delegate;
delegates = newDelegates;
}
/**
* Get the SWT-composite where client pages or composites
* can be embedded.
* @return the composite representing the client area.
*/
abstract public Composite getClientComposite();
/**
* Create the MenuItem which will serve as root of all
* other MenuItems.
* @return the newly created root of the menu tree.
*/
abstract public ITask createRootTask(IMenuItem rootMenu);
/**
* Creates the Task with the given name. The default implementation
* takes the name as the fully qualified classname of the Task.
* @param name unique name for the task within the client side application.
*/
public ITask createTask(String name) {
return getComponent().createTask(name);
}
/**
* Locks and disables the menu. Internally increments a lock counter.
* To unlock the menu again unlockMenu() must be called as often
* as lockMenu().
*/
public void lockMenu() {
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].lockMenu();
}
}
/**
* Unlocks the menu. Internally decrements a lock counter.
* To unlock the menu unlockMenu() must be called as often
* as lockMenu() before.
*/
public void unlockMenu() {
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].unlockMenu();
}
}
/**
* Completely enable/disable the menu.
* @param enabled true: enable, false: disable.
*/
protected void setMenuEnabled(boolean enabled) {
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].setMenuEnabled(enabled);
}
}
/**
* Enables the given menu item if the argument enabled is true
,
* and disables it otherwise. A disabled menu item is
* not selectable from the user interface and
* draws with an inactive or "grayed" look.
* Disabling a menu item makes all its children not selectable, too.
* @since 2.2.0
*/
protected void setMenuItemEnabled(IMenuItem item,boolean enabled) {
for ( int i = 0; i < delegates.length; i++ ) {
if ( delegates[i].rootMenu.containsItem(item,true) ) {
delegates[i].setMenuItemEnabled(item,enabled);
break;
}
}
}
/**
* Returns true
if the given menu item is enabled and false
* otherwise. A disabled menu item is not selectable from the user interface
* and draws with an inactive or "grayed" look. A menu item is disabled, if
* it is either directly disabled or any of its parents is disabled.
* @since 2.2.0
*/
protected boolean isMenuItemEnabled(IMenuItem item) {
for ( int i = 0; i < delegates.length; i++ ) {
if ( delegates[i].rootMenu.containsItem(item,true) ) {
return delegates[i].isMenuItemEnabled(item);
}
}
return false;
}
/**
* Enables the given menu item if the argument enabled is true
,
* and disables it otherwise. A disabled menu item is
* not selectable from the user interface and
* draws with an inactive or "grayed" look.
* Disabling a menu item makes all its children not selectable, too.
* @since 2.2.0
*/
protected void setMenuItemEnabled(String menuId,boolean enabled) {
setMenuItemEnabled(getMenu(menuId), enabled);
}
/**
* Returns true
if the given menu item is enabled and false
* otherwise. A disabled menu item is not selectable from the user interface
* and draws with an inactive or "grayed" look. A menu item is disabled, if
* it is either directly disabled or any of its parents is disabled.
* @since 2.2.0
*/
protected boolean isMenuItemEnabled(String menuId) {
return isMenuItemEnabled(getMenu(menuId));
}
/**
* Register the given MenuItem at the AppShell.
* After this, the MenuItem will be attached to
* its visual representation. It will be visible
* and events will be delivered.
* @param item to register
* @deprecated use {@link AppShellDelegate#registerMenu(IMenuItem)} instead
*/
public void registerMenu(IMenuItem item) {
IMenuItem parentItem = item.getParent();
for ( int i = 0; i < delegates.length; i++ ) {
if ( parentItem == null || delegates[i].rootMenu.containsItem(parentItem,true) ) {
delegates[i].registerMenu(item);
break;
}
}
}
/**
* Unregister the given MenuItem fromt the AppShell.
* After this, the MenuItem will no longer be attached
* to its visual representation. It will not be
* visible and no events will be delivered any more.
* @param item to remove
* @deprecated use {@link AppShellDelegate#unregisterMenu(IMenuItem)} instead
*/
public void unregisterMenu(IMenuItem item) {
IMenuItem parentItem = item.getParent();
for ( int i = 0; i < delegates.length; i++ ) {
if ( parentItem == null || delegates[i].rootMenu.containsItem(parentItem,true) ) {
delegates[i].unregisterMenu(item);
break;
}
}
}
/**
* Calls the action associated which the given MenuItem.
* Before it is actually called, all currently open Tasks
* outside the given MenuItem are closed.
* If there is no Task attached to the given MenuItem, nothing happens.
* @param item the MenuItem to call.
*/
public void callMenu(IMenuItem item) {
if (item.getTask()==null && item.hasMenuSelectionListener()==false) return; // nothing to do
for ( int i = 0; i < delegates.length; i++ ) {
if ( item == null || delegates[i].rootMenu.containsItem(item,true) ) {
delegates[i].callMenu(item);
break;
}
}
}
/**
* Calls the action associated which the named MenuItem.
* Before it is actually called, all currently open Tasks
* outside the named MenuItem are closed.
* If there is no Task attached to the named MenuItem, nothing happens.
* @param menuId the name of the MenuItem to call.
*/
public void callMenu(String menuId) {
IMenuItem item = getMenu(menuId);
if(item==null) {
throw new IllegalArgumentException("no such menu item: "+menuId);
} else {
callMenu(item);
}
}
/**
* Get the MenuItem registered under the given menuId.
* If there are more than one MenuItem registered under the same name,
* the one registerd last is returned.
* @param menuId the name of the MenuItem you want.
* @return the MenuItem with the given name or null if not found.
*/
public IMenuItem getMenu(String menuId) {
for ( int i = 0; i < delegates.length; i++ ) {
IMenuItem item = delegates[i].getMenu(menuId);
if ( item != null ) {
return item;
}
}
return null;
}
/**
* Selects the named MenuItem. This method behaves like
* the user has selected the MenuItem via the GUI.
* @param menuId the name of the MenuItem to select.
*/
public void selectMenu(String menuId) {
for ( int i = 0; i < delegates.length; i++ ) {
IMenuItem item = delegates[i].getMenu(menuId);
if ( item != null ) {
delegates[i].selectMenu(menuId);
break;
}
}
}
/**
* Visibly marks the named MenuItem as selected.
* @param menuId the name of the MenuItem to mark as selected.
*/
public void markMenu(String menuId) {
for ( int i = 0; i < delegates.length; i++ ) {
IMenuItem item = delegates[i].getMenu(menuId);
if ( item != null ) {
delegates[i].markMenu(menuId);
break;
}
}
}
/**
* Get the ResourceBundle containing the labels for all menu items.
* @return the ResourceBundle for the menu.
*/
abstract protected ResourceBundle getMenuResource();
/**
* Method called to start the AppShell.
* The AppShell will be shown and executed.
* It blocks until the AppShell is closed.
* @return true
*/
public boolean invoke() {
initGUI();
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].prepare();
}
enterBase();
stateChangedBase();
updateErrorStatus(getFocusControl());
getShell().open();
List livingDelegates = new ArrayList();
for ( int i = 0; i < delegates.length; i++ ) {
AppShellDelegate delegate = (AppShellDelegate)delegates[i];
if ( delegate.rootTask != null ) {
delegate.rootTask.run();
if ( isDelegateLiving(delegate) ) {
livingDelegates.add(delegate);
}
}
}
// so we remove all the delegates witch has no root task and, after executing that task, no menus
delegates = (AppShellDelegate[]) livingDelegates.toArray(new AppShellDelegate[livingDelegates.size()]);
setEventsEnabled(true);
if ( ! getShell().isDisposed() ) {
clientEventLoop(this);
}
return true;
}
/**
* This method determines whether a delegate is living and is therefore not
* removed from the array of delegates during invokation of the AppShell.
*
* @param delegate
* @return
*/
protected boolean isDelegateLiving ( AppShellDelegate delegate ) {
if ( delegate.rootMenu != null ) {
return delegate.rootMenu.getItems().size() > 0;
}
return false;
}
/**
* Get the task on top of the call stack. This is the currently active Task.
* @deprecated use {@link AppShellDelegate#getTopTask()} instead
*/
public ITask getTopTask() {
for ( int i = 0; i < delegates.length; i++ ) {
ITask t = delegates[i].getTopTask();
if ( t != null )
return t;
}
return null;
}
/**
* Pushes the given Component or Page on top of the call stack.
* It is shown embedded in the client area.
* If modal is true, this method blocks until the Component or Page is
* closed.
* @param newClient the component or page to embedd.
* @param modal if modal is true this method blocks until newClient is finished
* if modal if false this method returns immediately.
* @deprecated use {@link AppShellDelegate#pushClientComponent(IXMAControl, boolean)} instead
*/
public void pushClientComponent(final IXMAControl newClient, boolean modal) {
getTopTask().call(new SimpleTask(newClient,modal));
}
/**
* Closes the dialog and all tasks with exitStatus true.
*/
public void closeOK() {
closeTasks();
super.closeOK();
}
/**
* Closes the dialog and all tasks with exitStatus false.
*/
public void closeCancel() {
closeTasks();
super.closeCancel();
}
/**
* Closes the component or page on top of the call stack.
*/
public void closeTasks () {
for ( int i = 0; i < delegates.length; i++ ) {
ITask top = delegates[i].getTopTask();
if ( top.isRunfinished() ) {
top.closeRequested(true);
}
else {
top.pageClosed(true);
}
}
}
/**
* Closes the task and/or the page in the call stack
*
* @param pageClient
*/
public void closeTasks(PageClient pageClient) {
for (int i = 0; i < delegates.length; i++) {
List taskStack = delegates[i].getTaskStack();
for (int j=taskStack.size()-1;j>=0;j--) {
ITask task = (ITask)taskStack.get(j);
IEmbeddable embeddable = task.getPage();
PageClient pageOfTask=null;
if (embeddable instanceof EmbeddableComponent) {
EmbeddableComponent embeddableComponent = (EmbeddableComponent)embeddable;
pageOfTask = embeddableComponent.getMainPage();
} else if (embeddable instanceof PageClient) {
pageOfTask = (PageClient)embeddable;
}
if (pageOfTask == pageClient) {
if ( task.isRunfinished() ) {
task.closeRequested(true);
}
else {
task.pageClosed(true);
}
return;
}
}
}
}
/**
* Notify the AppShell, all Tasks and all Subpages, that the Page no longer is visible.
*/
public void leaveBase() {
for ( int i = 0; i < delegates.length; i++ ) {
for ( ITask top = delegates[i].getTopTask(); top != null; top = top.getParentTask() ) {
try {
if ( top.isRunfinished() ) {
top.closeRequested(false);
}
else {
top.pageClosed(false);
}
}
catch ( Exception exc ) {
showException(exc);
}
}
}
super.leaveBase();
}
/**
* Closes the AppShell.
*/
public void exit() {
super.closeCancel();
}
/**
* This method will be called every time the Shell is tried to be closed.
* It is intended to be implemented by the application programmer if he
* wants to react on this event. The application programmer my decide if
* the shell will actually be closed.
* Per default closing is permitted if the menu is enabled - no blocking Task is active.
*
* @return true means it is ok to close the Shell
* false means do not close the Shell
*/
protected boolean close() {
for ( int i = 0; i < delegates.length; i++ ) {
if ( ! delegates[i].mayClose() )
return false;
}
return true;
}
/**
* Closes all tasks on the call stack which are outside the given
* MenuItem. The call stack is rewinded until the task is on top which
* corresponds to the given MenuItem.
* @param item the menu item outside which all tasks should be closed.
* @return true if all this tasks could be successfully closed.
* false if a task refused to close.
* @deprecated use {@link AppShellDelegate#closeTasks(IMenuItem)} instead
*/
protected boolean closeTasks(IMenuItem item) {
for ( int i = 0; i < delegates.length; i++ ) {
if ( delegates[i].rootMenu.containsItem(item,true) )
return delegates[i].closeTasks(item);
}
return false;
}
/**
* Determine if the AppShell still contains widgets. Can be used to
* test if it is disposed.
* @return true if it has widgets.
*/
public boolean hasWidgets() {
return getWidgets()!=null;
}
/**
* Execute an eventLoop for modal clients. This call blocks until
* the client is detached from the appshell by calling removeChild().
* Dispatches all events in the meantime.
*/
private void clientEventLoop(IXMAControl client) {
Display display = getShell().getDisplay();
Composite comp = client.getComposite();
while (!comp.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
// TODO: comment me
public void removeWidgetsBase() {
super.removeWidgetsBase();
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].removeWidgets();
}
}
/**
* Calls the clientevent configured for the widget of the event by
* calling {@link #clientEvent(SelectionEvent,int)}. Handles the selection
* events of the menu-tree, too.
*
* @param event the SWT-Event that happened.
* @param type the type of the Event, as defined by the event type constants in class SWT.
*/
protected void clientEventBase(SelectionEvent event,int type) {
super.clientEventBase(event,type);
for ( int i = 0; i < delegates.length; i++ ) {
delegates[i].clientEvent(event,type);
}
}
/**
* Replaces the embedded page or component in the client area. The currently embedded
* page or component is removed from this AppShell and the new one is shown in
* the client area.
* @param newClient the new page or component to embedd.
* @deprecated use {@link AppShellDelegate#setClientArea(IXMAControl)} instead
*/
final public void setClientArea(IXMAControl newClient) {
if ( delegates != null && delegates.length > 0 )
delegates[0].setClientArea(newClient);
}
/**
* Removes the embedded page or componet form the client area.
* Nothing is embedded afterwards.
* @deprecated use {@link AppShellDelegate#clearClientArea()} instead
*/
final public void clearClientArea() {
if ( delegates != null && delegates.length > 0 )
delegates[0].clearClientArea();
}
}