All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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