
at.spardat.xma.appshell.AppShell Maven / Gradle / Ivy
/*******************************************************************************
* 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 6395 2010-09-01 11:28:08Z gub $
*
*
*
*
*/
package at.spardat.xma.appshell;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import at.spardat.xma.boot.component.IXMAControl;
import at.spardat.xma.component.ComponentClient;
import at.spardat.xma.page.DialogPage;
/**
* 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 {
/**
* The root of the menu tree. It is not shown itself, but contains all the MenuItems
* which are shown at top level.
*/
protected MenuItem rootMenu;
/**
* The task at the very bottom of the call stack. This task is called automaticaly
* at application startup. All furter tasks are called from this task.
*/
protected ITask rootTask;
/**
* Lookup table for all MenueItems contained somewhere in the menu-tree.
* Every menu item can be found by its unique name.
*/
private HashMap menuItems = new HashMap();
/**
* Lookup table for named Tasks. see registerTask() and getTask().
*/
HashMap tasks = new HashMap();
/**
* Component or page currently shown in the client area.
*/
IXMAControl client;
/**
* Lock-counter for the menu used by lockMenu() and unlockMenu().
* If it is greater than zero, the menu is disabled.
* If it is less or equal to zero, the menu is enabled.
*/
int menuLock=0;
// /**
// * Indicates if the client may be outdated. The client my be
// * no longer the active page or component of the top task.
// */
// boolean clientDirty;
/**
* Initializes the application shell.
*
* @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 comonent is null.
*/
public AppShell(ComponentClient component, boolean stateless, int style) {
super(component, stateless, style);
}
/**
* 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.
*/
public void setClientArea(IXMAControl newClient) {
Composite compW = null;
try {
if(client!=null) {
removeChild(client);
}
client = newClient;
// // add EventListener
// if (newClient instanceof IComponent) {
// ((IComponent)newClient).addEventListener(this);
// }
Composite clientCompositeW = getClientComposite();
compW = ((IXMAControl)newClient).createComposite(clientCompositeW);
FormData data = new FormData();
data.left = new FormAttachment(0,0);
data.right = new FormAttachment(100,0);
data.top = new FormAttachment(0,0);
data.bottom = new FormAttachment(100,0);
compW.setLayoutData(data);
clientCompositeW.setTabList(new Control[]{compW});
compW.setVisible(false);
addChild(newClient);
} catch (Exception e) {
showException(e);
} finally {
if(compW!=null) compW.setVisible(true);
}
}
/**
* Removes the embedded page or componet form the client area.
* Nothing is embedded afterwards.
*/
public void clearClientArea() {
if(client!=null) {
removeChild(client);
client=null;
// if(client instanceof IComponent) {
// ((IComponent)client).removeEventListener(this);
// }
}
}
// public void refreshClientArea() {
// if(clientDirty) {
// IXMAControl actClient = getTopTask().getPage();
// if(client!=actClient) {
// if(actClient!=null) {
// setClientArea(actClient);
// } else {
// clearClientArea();
// }
// }
// clientDirty=false;
// }
// }
//
// public void setClientDirty() {
// clientDirty=true;
// }
/**
* 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. Internaly increments a lock counter.
* To unlock the menu again unlockMenu() must be called as often
* as lockMenu().
*/
public void lockMenu() {
if(menuLock==0) {
setMenuEnabled(false);
}
menuLock++;
}
/**
* Unlocks the menu. Internaly decrements a lock counter.
* To unlock the menu unlockMenu() must be called as often
* as lockMenu() before.
*/
public void unlockMenu() {
if(menuLock==0) throw new IllegalStateException("menu is not locked");
menuLock--;
if(menuLock==0) {
setMenuEnabled(true);
}
}
/**
* Completely enable/disable the menu.
* @param enabled true: enable, false: disable.
*/
abstract protected void setMenuEnabled(boolean 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
*/
abstract protected void setMenuItemEnabled(IMenuItem item,boolean 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
*/
abstract protected boolean isMenuItemEnabled(IMenuItem item);
/**
* 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));
}
/**
* Attach the given MenuItem to its viusal representation.
* The visual representation can be implemented by a
* Widget Model or directly by a Widget.
* The change must be propagated to the GUI.
* @param item to attach
*/
abstract public void attachMenu(IMenuItem item);
/**
* Detach the MenuItem from its visual representation.
* The change must be propagated to the GUI.
* @param item to remove
*/
abstract public void detachMenu(IMenuItem item);
/**
* 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
*/
public void registerMenu(IMenuItem item) {
List list = (List) menuItems.get(item.getName());
if(list==null) {
list = new LinkedList();
menuItems.put(item.getName(),list);
}
list.add(item);
attachMenu(item);
//registerTask(item.getName(),item.getTask());
}
/**
* 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
*/
public void unregisterMenu(IMenuItem item) {
List list = (List) menuItems.get(item.getName());
if(list!=null) {
list.remove(item);
if(list.isEmpty()) {
menuItems.remove(item.getName());
}
}
detachMenu(item);
//unregisterTask(item.getName());
}
/**
* 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) return; // no task -> nothing to do
if(closeTasks(item)) {
item.select();
}
}
/**
* 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) {
LinkedList list = (LinkedList)menuItems.get(menuId);
if(list!=null && !list.isEmpty()) {
return (IMenuItem) list.getLast();
} else {
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.
*/
abstract public void selectMenu(String menuId);
/**
* Visibly marks the named MenuItem as selected.
* @param menuId the name of the MenuItem to mark as selected.
*/
abstract public void markMenu(String menuId);
/**
* 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();
rootMenu = new MenuItem(this);
rootMenu.setStyle(SWT.CASCADE); //needed by MenuAppShell
rootTask = createRootTask(rootMenu);
rootTask.setMenu(rootMenu);
rootMenu.setTask(rootTask);
enterBase();
stateChangedBase();
updateErrorStatus(getFocusControl());
getShell().open();
if(rootTask!=null) rootTask.run();
setEventsEnabled(true);
if(!getShell().isDisposed()) {
clientEventLoop(this);
}
return true;
}
/**
* Get the task on top of the call stack. This is the currently active Task.
*/
public ITask getTopTask() {
ITask top = rootTask;
for(ITask task = rootTask;task!=null;task=task.getSubTask()) {
top = task;
}
return top;
}
/**
* 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 util newClient is finished
* if modla if false this method reuturns imediatly.
*/
public void pushClientComponent(final IXMAControl newClient, boolean modal) {
getTopTask().call(new SimpleTask(newClient,modal));
}
/**
* Closes the component or page on top of the call stack.
*/
public void closeOK() {
ITask top = getTopTask();
if(top.isRunfinished()) {
top.closeRequested(true);
} else {
top.pageClosed(true);
}
}
/**
* Closes the component or page on top of the call stack.
*/
public void closeCancel() {
ITask top = getTopTask();
if(top.isRunfinished()) {
top.closeRequested(true);
} else {
top.pageClosed(true);
}
}
/**
* Notify the AppShell, all Tasks and all Subpages, that the Page no longer is visible.
*/
public void leaveBase() {
for(ITask top = 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() {
return menuLock==0;
}
/**
* 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.
*/
protected boolean closeTasks(IMenuItem item) {
ITask oldTop = getTopTask();
for(ITask top = getTopTask();top!=rootTask&&!top.contains(item);top=getTopTask()) {
if(!top.closeRequested(false)) {
if(top!=oldTop) {
top.showTopPage();
}
return false;
}
}
return true;
}
/**
* 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();
}
}
}
// protected void forceRedraw() {
// Control[] controls = this.getComposite().getChildren();
// for (int i = 0; i < controls.length; i++) {
// if (controls[i] instanceof Composite) {
// ((Composite) controls[i]).layout();
// }
// }
// this.getComposite().layout();
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy