Package org.jdesktop.wonderland.client.scenemanager

Source Code of org.jdesktop.wonderland.client.scenemanager.SceneManager$SelectionManagerHolder

/**
* Open Wonderland
*
* Copyright (c) 2011, Open Wonderland Foundation, All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* The Open Wonderland Foundation designates this particular file as
* subject to the "Classpath" exception as provided by the Open Wonderland
* Foundation in the License file that accompanied this code.
*/

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.client.scenemanager;

import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import org.jdesktop.mtgame.Entity;
import org.jdesktop.wonderland.client.cell.Cell;
import org.jdesktop.wonderland.client.input.Event;
import org.jdesktop.wonderland.client.input.EventClassFocusListener;
import org.jdesktop.wonderland.client.input.EventClassListener;
import org.jdesktop.wonderland.client.input.EventListener;
import org.jdesktop.wonderland.client.input.InputManager;
import org.jdesktop.wonderland.client.jme.CellRefComponent;
import org.jdesktop.wonderland.client.jme.input.MouseEvent3D;
import org.jdesktop.wonderland.client.scenemanager.event.ActivatedEvent;
import org.jdesktop.wonderland.client.scenemanager.event.ContextEvent;
import org.jdesktop.wonderland.client.scenemanager.event.EnterExitEvent;
import org.jdesktop.wonderland.client.scenemanager.event.HoverEvent;
import org.jdesktop.wonderland.client.scenemanager.event.SceneEvent;
import org.jdesktop.wonderland.client.scenemanager.event.SelectionEvent;

/**
* Manages the global "selection" in Wonderland. The Selection Manager handles
* all sorts of interactions with Entities in the world. The selection manager
* sits above the basic input mechanism and adds interpretation in terms of
* "actions" in the Wonderland context.
* <ol>
* <li>Selection: The selection of an Entity in the world. This includes
* selecting one or more Entities in the world. A selected Entity(s) will be
* indicated in some way. This also involves deselecting (clearing) any of
* the Entities.
* <li>Context: A Context action is one that depends upon the set of Entities
* that are selected. Typically a menu pops up with further actions possible.
* <li>Enter/Exit: An Enter/Exit action is where the pointer enters or exits an
* Entity, typically with the mouse pointer.
* <li>Hover: A Hover action is where the pointer hovers over an Entity (typically
* by hovering the mouse). This includes the "start" hovering and the "stop"
* hovering.
* <li>Activation: The activation of an Entity in the world. For example, when
* a user double-clicks on an Entity, it is activated.
* </ol>
* Threads can register listeners on the scene manager for these actions.
* There are methods to fetch the collection of selected entities and the
* Entity where there is a hover. Threads can also manually clear the selected
* Entities.
* <p>
* The Selection Manager acts on individual Entities, so multiple Entities with
* a Cell can be individually selected. Threads can fetch the Cell object
* associated with an Entity via the getCellForEntity() method.
* <p>
* Which mouse and keyboard events correspond to the Selection Manager actions
* are controlled via a "selection policy" as defined by a class that implements
* the SceneManagerPolicy interface.
*
* @author Jordan Slott <jslott@dev.java.net>
*/
public class SceneManager {
    /* The selection policy */
    private SceneManagerPolicy policy = new DefaultSceneManagerPolicy();

    /* The mouse event listener */
    private final MouseEventListener mouseEventListener;

    /*
     * A bunch of member variables to keep track of the hover process: the
     * hoverEntity tracks the entity we are currently hovering over if there
     * is a current hover, a hoverTimer that keeps track of the timer most
     * recently launched, and a hoverStartTime that tracks when the timer is
     * launched. This last field is used to determine whether the hover was
     * cancelled during the execution of the hover timer task run() method
     */
    protected Entity hoverEntity = null;
    protected Timer hoverTimer = null;
    protected long hoverStartTime = -1;
   
    /* The Entity that is "entered" or null if none */
    private Entity enterEntity = null;
   
    /*
     * An ordered list of selected entities, in the order that they were
     * selected.
     */
    private final Set<Entity> selectedEntityList;
   
    /** Default Constructor */
    public SceneManager() {
        selectedEntityList = Collections.synchronizedSet(new LinkedHashSet());
        mouseEventListener = new MouseEventListener();
       
        InputManager.inputManager().addGlobalEventListener(mouseEventListener);
       
        // Uncomment the following line to see an example listener
//        addSceneListener(new MySelectionListener());
    }

    /**
     * Clean up this scene manager and remove all registered listeners
     */
    public void cleanup() {
        InputManager.inputManager().removeGlobalEventListener(mouseEventListener);
        clearSelection();
    }

    /**
     * Singleton to hold instance of SceneManager. This holder class is
     * loader on the first execution of SceneManager.getSelectionManager().
     */
    private static class SelectionManagerHolder {
        private final static SceneManager manager = new SceneManager();
    }

    /**
     * Returns a single instance of this class
     * <p>
     * @return Single instance of this class.
     */
    public static final SceneManager getSceneManager() {
        return SelectionManagerHolder.manager;
    }
   
    /**
     * Provides a raw input event to process
     */
    protected void inputEvent(Event event) {
        InputManager inputManager = InputManager.inputManager();       
        Entity entity = event.getEntity();
                       
        // Implement hover. We check if the event interrupts hover and we
        // restart the timer. If we kill a timer, there may be an executing
        // hover task run() method. This may lead to a race condition where
        // the run() method has been called at the same time a hover interrupt
        // event happens. To fix this, we keep track of when we kicked off
        // the last timer event.
        if (policy.isHoverInterrupt(event) == true) {
            synchronized(this) {
                // Cancel the current timer (although a run() method of the timer
                // may still happen as a race condition
                if (hoverTimer != null) {
                    hoverTimer.cancel();
                }
               
                // If there is a currently hovering entity, then send a stop
                // event
                MouseEvent mouseEvent = (MouseEvent) ((MouseEvent3D) event).getAwtEvent();
                if (hoverEntity != null) {
                    inputManager.postEvent(new HoverEvent(hoverEntity, false, mouseEvent));
                    hoverEntity = null;
                }
               
                // Update the hover start time. This will cause any remaining
                // timer tasks that may have been run to ignore themselves
                hoverStartTime = System.currentTimeMillis();
               
                // Launch a new timer task, but not unless we are actually over
                // a non-null entity
                if (entity != null) {
                    HoverTimerTask task = new HoverTimerTask(entity, hoverStartTime, mouseEvent);
                    hoverTimer = new Timer();
                    hoverTimer.schedule(task, policy.getHoverDelay());
                }
            }
        }
       
        if (policy.isClearedSelection(event) == true) {
            // If we wish to clear the selection, then simply clear out the
            // list and fire an event
            selectedEntityList.clear();
            inputManager.postEvent(new SelectionEvent(new LinkedList(selectedEntityList)));
            return;
        }
        else if (policy.isSingleSelection(event) == true) {
            // issue #1115: ignore the event if it is selecting an object that
            // is already selected
            if (selectedEntityList.contains(entity) == true) {
                // OWL issue #177: send an empty context event to ensure that
                // any visible context menus are hidden in this case
                MouseEvent mouseEvent = (MouseEvent) ((MouseEvent3D) event).getAwtEvent();
                inputManager.postEvent(new ContextEvent(new LinkedList(), mouseEvent));
               
                return;
            }

            // Clear out the list, add the new Entity and fire an event
            selectedEntityList.clear();
            selectedEntityList.add(entity);
            inputManager.postEvent(new SelectionEvent(new LinkedList(selectedEntityList)));
            return;
        }
        else if (policy.isMultiSelection(event) == true) {
            // If the Entity is already selected, then remove it from the
            // selection list. If not already present, then add it
            // Reset the selection possible. If the entity is not selected,
            // then select it (only). If the entity is selected, then do
            // nothing (if it is part of a group of selected entities)
            if (selectedEntityList.contains(entity) == false) {
                selectedEntityList.add(entity);
            }
            else {
                selectedEntityList.remove(entity);
            }
            inputManager.postEvent(new SelectionEvent(new LinkedList(selectedEntityList)));
            return;
        }
        else if (policy.isEnter(event) == true) {
            enterEntity = entity;
            inputManager.postEvent(new EnterExitEvent(entity, true));
        }
        else if (policy.isExit(event) == true) {
            Entity eventEntity = enterEntity;
            enterEntity = null;
            inputManager.postEvent(new EnterExitEvent(eventEntity, false));
        }
        else if (policy.isActivation(event) == true) {
            inputManager.postEvent(new ActivatedEvent(entity));
        }
        else if (policy.isContext(event) == true) {
            // We use the context event to clear the selected entity list first
            selectedEntityList.clear();

            // If there is an entity for the mouse event, then add the entity to
            // the list and pass a Selection event too.
            if (entity != null) {
                selectedEntityList.add(entity);
                LinkedList entityList = new LinkedList(selectedEntityList);
                inputManager.postEvent(new SelectionEvent(entityList));
            }

            // Pass the mouse event for now so we know where the event was
            // fired. We sent this even if the entity is null, so the context
            // menu can be cleared.
            LinkedList entityList = new LinkedList(selectedEntityList);
            MouseEvent mouseEvent = (MouseEvent) ((MouseEvent3D) event).getAwtEvent();
            inputManager.postEvent(new ContextEvent(entityList, mouseEvent));
        }
    }

    /**
     * Posts a Scene Manager event to this system. This method can be used to
     * 'fake' such an event. This method will re-post the event to the input
     * manager for others to handle.
     *
     * @param event The scene event to post to the system
     */
    public void postEvent(SceneEvent event) {
        InputManager inputManager = InputManager.inputManager();
        Entity entity = ((SceneEvent)event).getPrimaryEntity();

        // Much of this code duplicates the inputEvent() method -- there seems
        // to be no way around this duplication for now.
       
        // Implement hover. We check if the event interrupts hover and we
        // restart the timer. If we kill a timer, there may be an executing
        // hover task run() method. This may lead to a race condition where
        // the run() method has been called at the same time a hover interrupt
        // event happens. To fix this, we keep track of when we kicked off
        // the last timer event.
        if (event instanceof HoverEvent) {
            synchronized(this) {
                // Cancel the current timer (although a run() method of the timer
                // may still happen as a race condition
                if (hoverTimer != null) {
                    hoverTimer.cancel();
                }

                // If there is a currently hovering entity, then send a stop
                // event
                MouseEvent mouseEvent = (MouseEvent) ((HoverEvent) event).getMouseEvent();
                if (hoverEntity != null) {
                    inputManager.postEvent(new HoverEvent(hoverEntity, false, mouseEvent));
                    hoverEntity = null;
                }

                // Update the hover start time. This will cause any remaining
                // timer tasks that may have been run to ignore themselves
                hoverStartTime = System.currentTimeMillis();

                // Launch a new timer task, but not unless we are actually over
                // a non-null entity
                if (entity != null) {
                    HoverTimerTask task = new HoverTimerTask(entity, hoverStartTime, mouseEvent);
                    hoverTimer = new Timer();
                    hoverTimer.schedule(task, policy.getHoverDelay());
                }
            }
        }

        // If a selection event, then set the list of entities and re-post the
        // event.
        if (event instanceof SelectionEvent) {
            // If a selection event, then set the list of entities and re-port
            // the event
            selectedEntityList.clear();
            selectedEntityList.addAll(event.getEntityList());
            inputManager.postEvent(new SelectionEvent(new LinkedList(selectedEntityList)));
            return;
        }

        // If an enter/exit event, then note the Entity we are entering or
        // exiting and repost the event.
        if (event instanceof EnterExitEvent) {
            if (((EnterExitEvent)event).isEnter() == true) {
                enterEntity = entity;
                inputManager.postEvent(new EnterExitEvent(entity, true));
            }
            else {
                Entity eventEntity = enterEntity;
                enterEntity = null;
                inputManager.postEvent(new EnterExitEvent(eventEntity, false));
            }
            return;
        }

        // If an activation event, the simply repost the event
        if (event instanceof ActivatedEvent) {
            inputManager.postEvent(new ActivatedEvent(entity));
            return;
        }

        // If a context event, then set the list of entities associated with
        // the context and re-post the event.
        if (event instanceof ContextEvent) {
            Logger.getLogger(SceneManager.class.getName()).warning("RECEIVED CONTEXT EVENT " +
                    entity.getName());
            // We use the context event to clear the selected entity list first
            selectedEntityList.clear();

            // If there is an entity for the mouse event, then add the entity to
            // the list and pass a Selection event too.
            if (entity != null) {
                selectedEntityList.add(entity);
                LinkedList entityList = new LinkedList(selectedEntityList);
                inputManager.postEvent(new SelectionEvent(entityList));
            }

            // Pass the mouse event for now so we know where the event was
            // fired. We sent this even if the entity is null, so the context
            // menu can be cleared.
            LinkedList entityList = new LinkedList(selectedEntityList);
            MouseEvent mouseEvent = (MouseEvent) ((ContextEvent) event).getMouseEvent();
            inputManager.postEvent(event);
            return;
        }
    }

    /**
     * Clears out the currently selection set of entities.
     */
    public void clearSelection() {
        selectedEntityList.clear();
        InputManager.inputManager().postEvent(new SelectionEvent(new LinkedList(selectedEntityList)));
    }
   
    /**
     * Returns the list of currently selected entitities in the order they were
     * selected, or null if no entity is currently selected.
     *
     * @return The currently selected list of Entity objects
     */
    public List<Entity> getSelectedEntities() {
        return new LinkedList(this.selectedEntityList);
    }

    /**
     * Sets the Entity over which there is hover.
     *
     * @param hoverEntity The hover Entity, null to reset to none
     */
    protected void setHoverEntity(Entity hoverEntity) {
        this.hoverEntity = hoverEntity;
    }
   
    /**
     * Returns the Entity over which there is a hover, null if there is no
     * hover.
     *
     * @return The Entity object over which there is a hover
     */
    public Entity getHoverEntity() {
        return this.hoverEntity;
    }
   
    /**
     * Convienence method that returns the cell associated with the Entity.
     *
     * @return The Cell associated with the currently selected Entity
     */
    public static Cell getCellForEntity(Entity entity) {
       Cell ret = null;
        while(ret==null && entity!=null) {
            CellRefComponent ref = (CellRefComponent) entity.getComponent(CellRefComponent.class);
            if (ref!=null)
                ret = ((CellRefComponent)ref).getCell();
            else
                entity = entity.getParent();
        }
        return ret;
    }

    /**
     * Adds a listener for scene events.
     *
     * @param listener The scene event listener to add
     */
    public void addSceneListener(EventListener listener) {
        InputManager.inputManager().addGlobalEventListener(listener);
    }

    /**
     * Removes a listener for scene events.
     *
     * @param listener The scene event listener to remove
     */
    public void removeSceneListener(EventListener listener) {
        InputManager.inputManager().removeGlobalEventListener(listener);
    }
   
    /**
     * Timer task for hover events. This task updates the currently hovered-
     * over Entity
     */
    class HoverTimerTask extends TimerTask {
        /* The entity over which the last event occurred */
        private Entity lastEventEntity = null;
       
        /* The time this task was started (roughly) */
        private long thisStartTime = -1;

        /* The Mouse Event that caused the hover event */
        private MouseEvent mouseEvent = null;

        /** Constructor, takes the Entity we are intereted in */
        public HoverTimerTask(Entity entity, long time, MouseEvent mouseEvent) {
            lastEventEntity = entity;
            this.thisStartTime = time;
            this.mouseEvent = mouseEvent;
        }
       
        @Override
        public void run() {
            // We have reached a timeout, so we set the Entity that is being
            // hovered over and we send an event. There is a possible race
            // condition here. This run() may be called after the event handling
            // mechanism of the Selection Manager tries to cancel any existing
            // tasks. Therefore, we synchronize on the SceneManager object
            // and check to see if the time this task was started is the same
            // time the Selection Manager thinks the hover task should be.
            synchronized(SceneManager.this) {
                if (thisStartTime == hoverStartTime) {
                    hoverEntity = lastEventEntity;
                    HoverEvent ev = new HoverEvent(lastEventEntity, true, mouseEvent);
                    InputManager.inputManager().postEvent(ev);
                    hoverTimer = null;
                }
            }
        }
    }
   
    /**
     * Global mouse listener for selection events. Reports back to the Selection
     * Manager on any updates.
     */
    class MouseEventListener extends EventClassFocusListener {
        @Override
        public Class[] eventClassesToConsume() {
            return new Class[] { MouseEvent3D.class };
        }

        // Note: we don't override computeEvent because we don't do any computation in this listener.

        @Override
        public void commitEvent(Event event) {
            inputEvent(event);
        }
    }
   
    class MySelectionListener extends EventClassListener {

        @Override
        public Class[] eventClassesToConsume() {
            return new Class[] {
                        ActivatedEvent.class, ContextEvent.class,
                        EnterExitEvent.class, HoverEvent.class,
                        SelectionEvent.class
            };
        }
       
        @Override
        public void commitEvent(Event event) {
            Logger logger = Logger.getLogger(MySelectionListener.class.getName());
            SceneEvent se = (SceneEvent)event;
            if (event instanceof ActivatedEvent) {
                logger.warning("SELECTION: ACTIVATED EVENT " +
                        se.getEntityList().get(0));
            }
            else if (event instanceof SelectionEvent) {
                List<Entity> selected = SceneManager.getSceneManager().getSelectedEntities();
                ListIterator<Entity> it = selected.listIterator();
                logger.warning("SELECTION: SELECTION EVENT " + selected.size());
                while (it.hasNext() == true) {
                    Entity entity = it.next();
                    logger.warning("SELECTION: SELETION EVENT " +
                            entity.getName());
                }
            }
            else if (event instanceof ContextEvent) {
                logger.warning("SELECTION: CONTEXT EVENT " +
                        se.getEntityList().size());
            }
            else if (event instanceof HoverEvent) {
                logger.warning("SELECTION: HOVER EVENT " +
                        se.getEntityList().get(0) + " " +
                        ((HoverEvent)event).isStart() + " @ " +
                        ((HoverEvent)event).getMouseEvent().getX() + " " +
                        ((HoverEvent)event).getMouseEvent().getY());
            }
            else if (event instanceof EnterExitEvent) {
                logger.warning("SELECTION: ENTER EXIT EVENT " +
                        se.getEntityList().get(0) + " " +
                        ((EnterExitEvent)event).isEnter());
            }
        }
    }
}
TOP

Related Classes of org.jdesktop.wonderland.client.scenemanager.SceneManager$SelectionManagerHolder

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.