Package DisplayProject.actions

Source Code of DisplayProject.actions.ActionMgr

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package DisplayProject.actions;

import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;

import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import org.apache.log4j.Logger;

import DisplayProject.ArrayColumn;
import DisplayProject.BodyGridCell;
import DisplayProject.UIutils;
import DisplayProject.table.GenericCellEditor;
import Framework.EventManager;
import Framework.Task;

/**
* This class provides an abstraction mechanism between any thread switching to modify the GUI and the EDT.
* Essentially any action on the GUI i.e. setBackground(), setVisible(), etc, is made pending and all
* actions are processed in the correct order at a later time by the EDT.
*/
public class ActionMgr {
    private static Logger _log = Logger.getLogger(ActionMgr.class);
    private static boolean isHeadless = GraphicsEnvironment.isHeadless();//PM:31/07/2008:
    private static PendingAction currentAction = null;
    private ActionMgr() {
        super();
    }

    /**
     * A call back interface to filter the list of pending actions. This call back will be passed each pending
     * action in turn and should return a boolean as to whether it's the one being sought or not.
     */
    public static interface Filter {
        public boolean filter(PendingAction pAction);
    }

    /**
     * Elements are stored in 2 collections -- one flat array for all events, in the order they arrived, and
     * one hash table keyed by the object (or null if no object) for efficient searching of specific items.
     * This class implements the collections and coordinates the actions on collections<p>
     * <p>
     * Note that this class is local to each thread and thus is inherently thread safe. We therefore do not
     * need to be concerned about synchronization like we normally would. (If we changed to use InvokeLater
     * rather than InvokeAndWait, this would not be the case...)
     */
    private static class ThreadPendingActions {
        private Map<Component, List<PendingAction>> objectSpecific;
        private List<PendingAction> orderedList;

        public ThreadPendingActions() {
            this.objectSpecific = new WeakHashMap<Component, List<PendingAction>>();
            this.orderedList = new ArrayList<PendingAction>();
        }

        public void add(PendingAction pAction) {
            Component c = pAction.getComponent();
            List<PendingAction> compList = this.objectSpecific.get(c);
            if (compList == null) {
                compList = new ArrayList<PendingAction>();
                this.objectSpecific.put(c, compList);
            }
            compList.add(pAction);
            this.orderedList.add(pAction);
        }

        /**
         * process all actions pending for this thread, and then clear out the set.
         */
        private void processActionList(final List<PendingAction> pList) {
            if (pList != null && !pList.isEmpty()) {
                UIutils.invokeOnGuiThread(new Runnable() {
                    public void run() {
                        boolean canPostEvents = true;
                        List<Container> windowsToPack = new ArrayList<Container>();
                        try {
                        //_log.debug("***** Begin processGUIActions");
                            for (PendingAction action : pList) {
                                if (action.shouldFireEvents() != canPostEvents) {
                                    canPostEvents = !canPostEvents;
                                    if (canPostEvents) {
                                        EventManager.enableEventPosting();
                                    }
                                    else {
                                        EventManager.disableEventPosting();
                                    }
                                }
                                // TF:18/06/2008:Performance improvement
                                if (_log.isDebugEnabled()) {
                                  _log.debug("\tPerforming action [" + action.toString() + "]");
                                }
                                currentAction = action;
                                action.performAction();
                                currentAction = null;
                                if (action.requiresPack()) {
                                    Component c = action.getComponent();
                                    if ((c != null)&&(c instanceof JComponent)){
                                        Container win = ((JComponent)c).getTopLevelAncestor();
                                    if (win != null && (win instanceof JFrame || win instanceof JDialog) && windowsToPack.indexOf(win)<0) {
                                            windowsToPack.add(win);
                                        }
                                    }
                                }
                            }
                        //_log.debug("***** End processGUIActions");

                        }
                        finally {
                            EventManager.enableEventPosting();
                        }
                        if (windowsToPack.size() > 0) {
                          for (Container container : windowsToPack) {
                                if (container instanceof JFrame) {
                                    JFrame c = (JFrame)container;
                                    // -------------------------------------------------------------------------------------
                                    // Only pack if we are not visible yet, or we are not maximised.
                                    // Packing a window that is maximised will cause it to srink to a normal windowed state.
                                    // Craig Mitchell: 30/01/2007.
                                    // -------------------------------------------------------------------------------------
                                    if (c.isVisible() == false || c.getExtendedState() != Frame.MAXIMIZED_BOTH) {
                                        c.pack();
                                    }
                                }
                                else if (container instanceof JDialog) {
                                    JDialog c = (JDialog)container;
                                    c.pack();
                                }
                            }
                        }
                    }
                });
            }
        }

        /**
         * process all actions pending for this thread, and then clear out the set.
         */
        public void process() {
            this.processActionList(orderedList);
            this.orderedList.clear();
            for (Iterator<List<PendingAction>> it = this.objectSpecific.values().iterator(); it.hasNext();) {
                it.next().clear();
            }
        }

        /**
         * Process all the pending events only for the passed object
         * @param pObject the object for which to process the events.
         */
        public void process(final Component pObject) {
            List<PendingAction> aList = objectSpecific.get(pObject);
            this.processActionList(aList);

            // Now, off the GUI thread, remove the processed items from the array
            if (aList != null) {
              for (PendingAction action : aList) {
                this.orderedList.remove(action);
              }
                aList.clear(); // CraigM: 29/03/2008
            }
        }

        private class ReverseIterator<E> implements Iterator<E> {
            private List<E> list;
            private int index;
            public ReverseIterator(List<E> pList) {
                this.list = pList;
                this.index = pList.size() - 1;
            }
            public boolean hasNext() {
                return this.index >= 0;
            }
            public E next() {
                if (index >= 0) {
                    return this.list.get(this.index--);
                }
                else {
                    throw new NoSuchElementException();
                }
            }
            public void remove() {
                throw new UnsupportedOperationException();
            }
        }

        /**
         * Convenience method to iterate over the whole collection of objects. Note that
         * this is not a true iterator -- the underlying collection must not be altered
         * by this iterator, in particular invoking Iterator.remove.
         *
         * We must traverse the list BACKWARDS -- the most recent actions are last in the
         * list, and something like a get must return the latest version.
         * @return
         */
        public Iterator<PendingAction> iterator() {
            return new ReverseIterator<PendingAction>(this.orderedList);
        }

        /**
         * Convenience method to iterate over the collection of objects related to the passed
         * Object. Note that this is not a true iterator -- the underlying collection must not be altered
         * by this iterator, in particular invoking Iterator.remove.
         * @return
         */
        public Iterator<PendingAction> iterator(Component pComponent) {
            List<PendingAction> aList = objectSpecific.get(pComponent);
            if (aList == null) {
                // Return a dummy array iterator over an empty collection.
                return new ArrayList<PendingAction>().iterator();
            }
            else {
                return new ReverseIterator<PendingAction>(aList);
            }
        }
    }

    private static ThreadLocal<ThreadPendingActions> pendingGUIActions = new ThreadLocal<ThreadPendingActions>() {
        protected ThreadPendingActions initialValue() {
            return new ThreadPendingActions();
        }
    };

    public static void processGUIActions() {
      //PM:31/07/2008:if running on a server do nothing
      if (isHeadless){
        return;
      }
      // Check to see if the task has been cancelled
      Task.checkTaskCancelled();
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        pendingActions.process();
    }

    /**
     * Process the GUI actions only for those actions that are related to the passed object
     * @param o
     */
    public static void processGUIActions(Component o) {
      // Check to see if the task has been cancelled
      Task.checkTaskCancelled();

        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        pendingActions.process(o);
    }

    /**
     * Add an action into the appropriate pending action table for this Thread
     * @param pAction the action to add
     */
    public static void addAction(PendingAction pAction) {
      // Tricky stuff -- if the top level object is associated with a body grid cell, this is a pending
      // action for a JTable component, not a standard pending action
      BodyGridCell cell = BodyGridCell.getAssociatedCell(pAction.getComponent());
      if (cell != null) {
        cell.add(pAction);
        // TF:29/9/07: We might be doing something like setting the background colour on a click. Hence we
        // need to invalidate the cell and force it to be re-painted.
        cell.getBodyGrid().getTable().repaint();
      }
      else {
        if (SwingUtilities.isEventDispatchThread()) {
          pAction.performAction();
        }
        else {
          ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
          pendingActions.add(pAction);
        }
      }
    }

    /**
     * Return the last pending action in the list to which the filter returns trus
     * @param pFilter the filtering class
     * @return the last pending action to which the filter returns true, or null if no actions match
     */
    public static PendingAction getAction(Filter pFilter) {
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(); it.hasNext();) {
            PendingAction action = it.next();
            if (pFilter.filter(action)) {
                return action;
            }
        }
        return null;
    }

    /**
     * gets an action for the GenericCellEditor comp of the given type, or returns null if one is not pending
     * @param comp
     * @param type
     * @return
     */
    @SuppressWarnings("unchecked")
  public static <T extends PendingAction> T getAction(GenericCellEditor comp, Class<T> type){
      // TF:12/05/2009:Changed this to use generics
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(); it.hasNext();) {
            Enabled action = (Enabled)it.next();
            if (action != null && comp == action.getEditor() 
                    && (action.getClass().equals(type))) {
                return (T)action;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
  public static <T extends PendingAction> T getAction(ArrayColumn column, Class<T> type){
      // TF:12/05/2009:Changed this to use generics
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(); it.hasNext();) {
            PendingAction action = it.next();
            if (action != null &&
                    (action.getClass().equals(type)) &&
                    (action instanceof ColumnAction) &&
                    column == ((ColumnAction)action).getColumn()) {
                return (T)action;
            }
        }
        return null;
    }


    /**
     * gets an action for the ButtonGroup comp of the given type, or returns null if one is not pending
     * @param comp
     * @param type
     * @return
     */
    @SuppressWarnings("unchecked")
  public static <T extends PendingAction> T getAction(ButtonGroup comp, Class<T> type){
      // TF:12/05/2009:Changed this to use generics
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(); it.hasNext();) {
            PendingAction action = (PendingAction)it.next();
            if (action != null && action instanceof IntegerValue){
            if (action != null &&
                (action.getClass().equals(type)) &&
                comp == ((IntegerValue)action).getButtonGroup()) {
                return (T)action;
            }
            }
        }
        return null;
    }

    /**
     * gets the last action for the component comp of the given type, or returns null if one is not pending
     * @param comp
     * @param type
     * @return
     */
    @SuppressWarnings("unchecked")
  public static <T extends PendingAction> T getAction(Component comp, Class<T> type){
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(comp); it.hasNext();) {
            PendingAction action = (PendingAction)it.next();
            if (action != null && comp == action.getComponent()&& (action.getClass().equals(type))) {
                return (T)action;
            }
        }
        return null;
    }

    /**
     * Return the last action for this component whose class (of the Action) is contained withing the
     * passed array. If the pHandleRelativesAction flag is set, and an action in the pActionTypes set
     * is found for either a child or parent component prior to finding the specified action for this component, the
     * gui actions are performed and null returned<p>
     * <p>
     * This can be used where there is an interdependency between parent and child. For example, causing a
     * parent widget to resize might cause this widget to resize
     * @param comp
     * @param pActionTypes
     * @return
     */
    public static PendingAction getAction(Component comp, Class<?>[] pActionTypes, boolean pHandleRelativesAction){
        ThreadPendingActions pendingActions = (ThreadPendingActions)ActionMgr.pendingGUIActions.get();
        for (Iterator<PendingAction> it = pendingActions.iterator(); it.hasNext();) {
            PendingAction action = (PendingAction)it.next();
            if (action != null) {
                Class<?> actionType = action.getClass();
                Component thisComp = action.getComponent();
                for (int i = 0; i < pActionTypes.length; i++) {
                    if (actionType == pActionTypes[i]) {
                        if (comp == thisComp) {
                            return action;
                        }
                        else if (pHandleRelativesAction) {
                            // See if this is either a parent or child of this component.
                            // Parent
                            Component c = comp.getParent();
                            while (c != null) {
                                if (c == thisComp) {
                                    ActionMgr.processGUIActions();
                                    return null;
                                }
                                c = c.getParent();
                            }
                            // Child
                            c = thisComp.getParent();
                            while (c != null) {
                                if (c == comp) {
                                    ActionMgr.processGUIActions();
                                    return null;
                                }
                                c = c.getParent();
                            }
                        }
                    }
                }
            }
        }
        return null;
    }
   
    public static PendingAction getCurrentAction() {
      // This is only allowed on the EDT as other threads don't have current actions
      if (SwingUtilities.isEventDispatchThread()) {
        return currentAction;
      }
      else {
        return null;
      }
    }
}
TOP

Related Classes of DisplayProject.actions.ActionMgr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.