Package Framework

Source Code of Framework.ForteKeyboardFocusManager

/*
Copyright (c) 2003-2008 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 Framework;

import java.awt.AWTEvent;
import java.awt.AWTKeyStroke;
import java.awt.Component;
import java.awt.Container;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.FocusTraversalPolicy;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;

import org.apache.log4j.Logger;

import DisplayProject.Constants;
import DisplayProject.GridField;
import DisplayProject.actions.Enabled;
import DisplayProject.actions.Focusable;
import DisplayProject.actions.Parent;
import DisplayProject.controls.ListViewTable;
import DisplayProject.table.ArrayFieldCellHelper;

/**
* This class allows Java to mirror the Reason codes for focus traversal that Forte has.
* This includes knowing if the focus change was due to the mouse, the keyboard, the
* application and so on. This is necessary because some applications rely on this reason
* code to determine where the next field is.
* @author Tim
*
*/
public class ForteKeyboardFocusManager extends DefaultKeyboardFocusManager {
    private Logger _log = Logger.getLogger(ForteKeyboardFocusManager.class);

    private int reason = Constants.FC_SUPRESS;

    public ForteKeyboardFocusManager() {
        super();
//        Set<AWTKeyStroke> upKeySet = new HashSet<AWTKeyStroke>();
//        Set<AWTKeyStroke> downKeySet = new HashSet<AWTKeyStroke>();

//        upKeySet.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_UP, 0, false));
//        setDefaultFocusTraversalKeys(UP_CYCLE_TRAVERSAL_KEYS, upKeySet);

//        downKeySet.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_DOWN, 0, false));
//        setDefaultFocusTraversalKeys(DOWN_CYCLE_TRAVERSAL_KEYS, downKeySet);

        _log.debug("Forte keyboard manager installed");
        if (_log.isDebugEnabled()){
            addPropertyChangeListener("focusOwner", new PropertyChangeListener(){

                public void propertyChange(PropertyChangeEvent evt) {
                    String old = (evt.getOldValue()==null) ? "null" : ((Component)evt.getOldValue()).getName();
                    String neww = (evt.getNewValue()==null) ? "null" : ((Component)evt.getNewValue()).getName();
                    _log.debug("Focus moved from " + old  + " to " + neww);
                }

            });
        }
    }

    /**
     * Get the reason code for the last focus traversal
     * @return
     */
    private int getReasonCode() {
        return reason;
    }

    /**
     * This method returns the traversal reason on the last focus change. This can be any of the FC_... constants in
     * the Constants class.
     * @return
     */
    public static int getTraversalReason() {
        KeyboardFocusManager currentMgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (currentMgr instanceof ForteKeyboardFocusManager)
            return ((ForteKeyboardFocusManager)currentMgr).getReasonCode();
        else {
            IllegalStateException errorVar = new IllegalStateException("A ForteKeyboardFocusManager has not been installed for this thread, so the traveral reason cannot be determined: " + Thread.currentThread());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }

    public static void setSurpress() {
        KeyboardFocusManager currentMgr = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (currentMgr instanceof ForteKeyboardFocusManager)
            ((ForteKeyboardFocusManager)currentMgr).setReason(Constants.FC_SUPRESS);
        else {
            IllegalStateException errorVar = new IllegalStateException("A ForteKeyboardFocusManager has not been installed for this thread, so the traveral reason cannot be determined: " + Thread.currentThread());
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
    }
    /**
     * Set the reason code for the last focus traversal
     * @param pReason
     */
    protected void setReason(int pReason) {
        switch (pReason) {
            case Constants.FC_APPLICATION:  _log.debug("FC_APPLICATION"); break;
            case Constants.FC_KEYDOWN:      _log.debug("FC_KEYDOWN"); break;
            case Constants.FC_KEYUP:        _log.debug("FC_KEYUP"); break;
            case Constants.FC_KEYNEXT:      _log.debug("FC_KEYNEXT"); break;
            case Constants.FC_KEYPREV:      _log.debug("FC_KEYPREV"); break;
            case Constants.FC_MOUSECLICK:   _log.debug("FC_MOUSECLICK"); break;
            case Constants.FC_SCROLL:       _log.debug("FC_SCROLL"); break;
            case Constants.FC_STATECHANGE:  _log.debug("FC_STATECHANGE"); break;
            case Constants.FC_SUPRESS:      _log.debug("FC_SUPRESS"); break;
        }
        this.reason = pReason;
    }

    /**
     * Set the next focus traversal reason to APPLICATION. This is necessary because there is nothing
     * in the event information to indicate the reason why the focus has changed, so the Focus action
     * needs to notify us that it's a programmatic action.
     *
     * CraigM:06/08/2008 - Changed to be a static method that gets the keyboard focus manager.
     */
    public static void setApplicationTraversal() {
        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (kfm instanceof ForteKeyboardFocusManager) {
          ((ForteKeyboardFocusManager)kfm).setReason(Constants.FC_APPLICATION);
        }
    }

    /**
     * Set the next focus traversal reason to STATECHANGE. This is necessary because there is nothing
     * in the event information to indicate the reason why the focus has changed, so the Focus action
     * needs to notify us that it's a programmatic action.
     */
    public static void setStateChangedTraversal() {
        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (kfm instanceof ForteKeyboardFocusManager) {
          ((ForteKeyboardFocusManager)kfm).setReason(Constants.FC_STATECHANGE);
        }
    }

    /**
     * Set the traversal reason to tab key.  This is handy for things like MultiLineTextField, where we
     * capture the tab traversal and repost it.  CraigM 05/10/2007.
     */
    public static void setTabTraversal(boolean pForward) {
        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (kfm instanceof ForteKeyboardFocusManager) {
            if (pForward) {
                ((ForteKeyboardFocusManager)kfm).setReason(Constants.FC_KEYNEXT);
            }
            else {
                ((ForteKeyboardFocusManager)kfm).setReason(Constants.FC_KEYPREV);
            }
        }
    }

    /**
     * Get the first editable component in pPanel.  Will recuse into sub panels. Return null if none.
     * @param pPanel
     * @return
     *
     * Written by Craig Mitchell
     * @since 30/11/2007
     */
    private Component getComponentTopLeft(JPanel pPanel) {
        for (int x=0; x<pPanel.getWidth(); x++) {
            for (int y=0; y<pPanel.getHeight(); y++) {
                Component comp = pPanel.getComponentAt(x, y);

                if (comp != pPanel) {
                    if (comp instanceof JPanel) {
                        return this.getComponentTopLeft((JPanel)comp);
                    }
                    if (comp != null && Enabled.is(comp)) {
                        return comp;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Listen for the up / down arrow keys, and move the focus like Forte did on
     * JTextFields, JButtons, JCheckBox, and JTabbedPane.
     * @param pComp
     * @param pKeyEvent
     *
     * Written by Craig Mitchell
     * @since 30/11/2007
     */
    private void handleArrowKeys(Component pComp, KeyEvent pKeyEvent) {
        // Was an arrow key pressed
        if (pKeyEvent.getKeyCode() == KeyEvent.VK_UP || pKeyEvent.getKeyCode() == KeyEvent.VK_DOWN) {

            // Are we a widget that listens to arrow keys
            if pComp instanceof JTextField ||
                    pComp instanceof JButton ||
                    pComp instanceof JCheckBox ||
                    pComp instanceof JTabbedPane ||
                    pComp instanceof GridField) {

                Container cntr = Parent.get(pComp);
                Component comp = null;

                // If we are a tab field, go into it
                if (pComp instanceof JTabbedPane && pKeyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
                    pComp = ((JTabbedPane)pComp).getSelectedComponent();

                    if (pComp instanceof JPanel) {
                        comp = this.getComponentTopLeft((JPanel)pComp);
                    }
                }

                // If our container is a GridField, we can work out exactly which field to move to
                else if (cntr instanceof GridField) {
                    while (true) {
                        GridField gf = (GridField)cntr;
                        comp = gf.getComponentNeighbour(pComp, (pKeyEvent.getKeyCode() == KeyEvent.VK_UP));

                        // Keep going until we get nothing, a grid, or something that is focusable and enabled
                        while comp != null &&
                                comp instanceof GridField == false &&
                                (Enabled.is(comp) == false || Focusable.is(comp) == false)) {
                            comp = gf.getComponentNeighbour(comp, (pKeyEvent.getKeyCode() == KeyEvent.VK_UP));
                        }

                        // If our neighbour is a grid, recuse into it
                        if (comp instanceof GridField) {
                            pComp = null;
                            cntr = (GridField)comp;
                        }
                        else {
                            break;
                        }
                    }
                }
               
                // CraigM:18/08/2008 - In that case we have an editor of JCombobox focus should not be changed by key UP/DOWN
                else if (cntr instanceof JComboBox) {
                  comp = pComp;
                }

                // Didn't find a component above or below in this grid
                if (comp == null) {
                    Container cntrParent = Parent.get(cntr);

                    // If we are in another grid field, recuse out of it
                    if (cntrParent instanceof GridField) {
                        this.handleArrowKeys(cntr, pKeyEvent);
                    }

                    // Otherwise act like a tab
                    else if (pKeyEvent.getKeyCode() == KeyEvent.VK_UP) {
                        this.setReason(Constants.FC_KEYPREV);
                        pComp.transferFocusBackward();
                    }
                    else {
                        this.setReason(Constants.FC_KEYNEXT);
                        pComp.transferFocus();
                    }
                }

                // Found a component so move the focus there
                else {
                    if (pKeyEvent.getKeyCode() == KeyEvent.VK_UP) {
                        this.setReason(Constants.FC_KEYPREV);
                    }
                    else {
                        this.setReason(Constants.FC_KEYNEXT);
                    }
                    comp.requestFocus();
                }
            }
        }
    }

    /**
     * Set the reason code based on the key event. This sees if it's a KEY_UP / KEY_DOWN / etc key and sets
     * the appropriate flag.
     */
    @Override
    public void processKeyEvent(Component focusedComponent, KeyEvent e) {
        if (e.getID() == KeyEvent.KEY_PRESSED && focusedComponent.getFocusTraversalKeysEnabled() && !e.isConsumed())
        {
            this.handleArrowKeys(focusedComponent, e);

            AWTKeyStroke stroke = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
            AWTKeyStroke oppStroke = AWTKeyStroke.getAWTKeyStroke(stroke.getKeyCode(),
                                                    stroke.getModifiers(),
                                                    !stroke.isOnKeyRelease());
            Set<AWTKeyStroke> toTest;
            boolean reasonSet = false;

            toTest = focusedComponent.getFocusTraversalKeys(FORWARD_TRAVERSAL_KEYS);
            if (toTest.contains(stroke) || toTest.contains(oppStroke)) {
                setReason(Constants.FC_KEYNEXT);
                reasonSet = true;
            }

            toTest = focusedComponent.getFocusTraversalKeys(BACKWARD_TRAVERSAL_KEYS);
            if (toTest.contains(stroke) || toTest.contains(oppStroke)) {
                setReason(Constants.FC_KEYPREV);
                reasonSet = true;
            }

            toTest = focusedComponent.getFocusTraversalKeys(UP_CYCLE_TRAVERSAL_KEYS);
            if (toTest.contains(stroke) || toTest.contains(oppStroke)) {
                setReason(Constants.FC_KEYUP);
                reasonSet = true;
            }

            toTest = focusedComponent.getFocusTraversalKeys(DOWN_CYCLE_TRAVERSAL_KEYS);
            if (toTest.contains(stroke) || toTest.contains(oppStroke)) {
                setReason(Constants.FC_KEYDOWN);
                reasonSet = true;
            }

            // TF:17/07/07: If we're on a jTable and we hit tab or shift-tab and we haven't
            // already set the reason code, we set the reason code appropriately. JTables
            // define Ctrl TAB and Ctrl-Shift-Tab for navigation but this is normally overridden
            if (!reasonSet) {

                if (focusedComponent instanceof JTable ||
                        (focusedComponent instanceof JComponent && ArrayFieldCellHelper.getArrayField(focusedComponent)!= null)) {

                  if (e.getKeyCode() == KeyEvent.VK_TAB && !e.isAltDown() && !e.isControlDown() & !e.isMetaDown() && !e.isConsumed()) {
                      if (!e.isShiftDown()) {
                        setReason(Constants.FC_KEYNEXT);
                      }
                      else {
                            setReason(Constants.FC_KEYPREV);
                      }

                        // CraigM:15/10/2008 - If we are a grid field in an array field
                        if (focusedComponent.getParent() instanceof GridField && ArrayFieldCellHelper.getArrayField(focusedComponent.getParent()) != null) {

                          // We should have a FocusTraversalPolicy somewhere on a gridfield (setup in GridFieldCellEditor)
                          FocusTraversalPolicy traversal = null;
                          Component componentWithTraveralPolicy = focusedComponent;
                          while (componentWithTraveralPolicy != null && traversal == null) {
                            componentWithTraveralPolicy = componentWithTraveralPolicy.getParent();
                            if (componentWithTraveralPolicy instanceof Container) {
                              traversal = ((Container)componentWithTraveralPolicy).getFocusTraversalPolicy();
                            }
                          }

                          // If this is null, it means we didn't find the FocusTraversalPolicy manager.
                          if (traversal != null && componentWithTraveralPolicy instanceof Container) {
                            if (!e.isShiftDown()) {
                              Component comp = traversal.getComponentAfter((Container)componentWithTraveralPolicy, focusedComponent);
 
                              // If we can transfer the focus inside the GridField, do it
                              if (comp != null && comp != traversal.getFirstComponent((Container)componentWithTraveralPolicy)) {
                                comp.requestFocus();
                                e.consume();
                              }
                            }
                            else {
                              Component comp = traversal.getComponentBefore((Container)componentWithTraveralPolicy, focusedComponent);
                             
                              // If we can transfer the focus inside the GridField, do it
                              if (comp != null && comp != traversal.getLastComponent((Container)componentWithTraveralPolicy)) {
                                comp.requestFocus();
                                e.consume();
                              }
                            }
                          }
                        }
                    }

                    // Check arrow keys as well. CraigM 08/10/2007.
                    else if (e.getKeyCode() == KeyEvent.VK_UP && !e.isAltDown() && !e.isMetaDown() && !e.isConsumed()) {
                        setReason(Constants.FC_KEYUP);
                    }
                    else if (e.getKeyCode() == KeyEvent.VK_DOWN && !e.isAltDown() && !e.isMetaDown() && !e.isConsumed()) {
                        setReason(Constants.FC_KEYDOWN);
                    }
                }
            }
        }
        _log.debug("Focus: " + focusedComponent.toString());
        super.processKeyEvent(focusedComponent, e);
    }

    private boolean isRedispatching = false;
    /**
     * Set the focus reason code based on an AWT event. Also retarget any mouse wheel
     * events to the focussed component providing the focussed component is a ComboBox
     */
    @Override
    public boolean dispatchEvent(AWTEvent e) {
        switch (e.getID()) {
            case MouseEvent.MOUSE_PRESSED:
                setReason(Constants.FC_MOUSECLICK);
                break;

            case WindowEvent.WINDOW_GAINED_FOCUS:
            case WindowEvent.WINDOW_LOST_FOCUS:
                setReason(Constants.FC_SUPRESS);
                break;

            case WindowEvent.WINDOW_OPENED:
                setReason(Constants.FC_APPLICATION);
                break;
               
            case MouseWheelEvent.WHEEL_UNIT_SCROLL:
            case MouseWheelEvent.WHEEL_BLOCK_SCROLL:
            case MouseWheelEvent.MOUSE_WHEEL:
                // If we get a mouse wheel event, retarget the event to the one that has focus
          if (e.getSource() instanceof JComboBox  && ((JComboBox)e.getSource()).isPopupVisible()) {
            // TF:07/05/2009:Forte would do nothing with a wheel event when the combo box was showing
            // its popup, so squash this event here
//            return true;
          }

              Component comp = getFocusOwner();
              // TF:07/05/2009:Cater for the case where we're using a fillin field, and the
              // cursor is actually in the editor.
              if (comp.getParent() instanceof JComboBox) {
                comp = comp.getParent();
              }
              if (!isRedispatching && comp != null && comp != e.getSource() && (
                  comp instanceof JComboBox ||
                  comp instanceof ListViewTable ||
                  comp instanceof JScrollPane)
              ) {
               
                e.setSource(comp);
                isRedispatching = true;
                comp.dispatchEvent(e);
                isRedispatching = false;
                return true;
              }
              break;
        }
        return super.dispatchEvent(e);
    }

    /**
     * This method will simulate a focus owner change by posting 2 permanentFocusOwner events -- one
     * from the currently active component to the window, and another from the window back to the
     * currently focused component. Note that this component does not actually change the focus, it
     * just pretends to. This can be useful in situations where event listeners are waiting for a
     * permanent focus owner change to post Forte style events, but java does not fire the focus
     * change at the same time as forte did. For example, if there is an AfterValueChange listener
     * on a grid field, it should fire if the "X" is pressed to close the window, but doesn't in java.
     */
    public void simulateFocusOwnerChange() {
      Component currentFocus = this.getFocusOwner();
      Component dummyFocus = this.getFocusedWindow();
      this.firePropertyChange("permanentFocusOwner", currentFocus, dummyFocus);
      this.firePropertyChange("permanentFocusOwner", dummyFocus, currentFocus);
    }
}
TOP

Related Classes of Framework.ForteKeyboardFocusManager

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.