| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.draw2d; |
| |
| import java.util.List; |
| |
| /** |
| * This class is a helper to the {@link SWTEventDispatcher}. It handles the task |
| * of determining which Figure will gain focus upon a tab/shift-tab. It also |
| * keeps track of the Figure with current focus. |
| * <p> |
| * Note: When a Canvas with a {@link LightweightSystem} gains focus, it gives |
| * focus to the child Figure who had focus when this Canvas lost focus. If the |
| * canvas is gaining focus for the first time, focus is given to its first child |
| * Figure. |
| */ |
| public class FocusTraverseManager { |
| |
| IFigure currentFocusOwner; |
| |
| /** |
| * Default constructor. |
| */ |
| public FocusTraverseManager() { |
| } |
| |
| private IFigure findDeepestRightmostChildOf(IFigure fig) { |
| while (fig.getChildren().size() != 0) { |
| fig = (IFigure) fig.getChildren().get(fig.getChildren().size() - 1); |
| } |
| return fig; |
| } |
| |
| /** |
| * Returns the IFigure that will receive focus upon a 'tab' traverse event. |
| * |
| * @param root |
| * the {@link LightweightSystem LightweightSystem's} root figure |
| * @param prevFocus |
| * the IFigure who currently owns focus |
| * @return the next focusable figure |
| */ |
| public IFigure getNextFocusableFigure(IFigure root, IFigure prevFocus) { |
| boolean found = false; |
| IFigure nextFocus = prevFocus; |
| |
| /* |
| * If no Figure currently has focus, apply focus to root's first |
| * focusable child. |
| */ |
| if (prevFocus == null) { |
| if (root.getChildren().size() != 0) { |
| nextFocus = ((IFigure) root.getChildren().get(0)); |
| if (isFocusEligible(nextFocus)) |
| return nextFocus; |
| } else |
| return null; |
| } |
| |
| int siblingPos = nextFocus.getParent().getChildren().indexOf(nextFocus); |
| while (!found) { |
| IFigure parent = nextFocus.getParent(); |
| |
| /* |
| * Figure traversal is implemented using the pre-order left to right |
| * tree traversal algorithm. |
| * |
| * If the focused sibling has children, traverse to its leftmost |
| * child. If the focused sibling has no children, traverse to the |
| * sibling to its right. If there is no sibling to the right, go up |
| * the tree until a node with un-traversed siblings is found. |
| */ |
| List siblings = parent.getChildren(); |
| |
| if (nextFocus.getChildren().size() != 0) { |
| nextFocus = (IFigure) nextFocus.getChildren().get(0); |
| siblingPos = 0; |
| if (isFocusEligible(nextFocus)) |
| found = true; |
| } else if (siblingPos < siblings.size() - 1) { |
| nextFocus = ((IFigure) (siblings.get(++siblingPos))); |
| if (isFocusEligible(nextFocus)) |
| found = true; |
| } else { |
| boolean untraversedSiblingFound = false; |
| while (!untraversedSiblingFound) { |
| IFigure p = nextFocus.getParent(); |
| IFigure gp = p.getParent(); |
| |
| if (gp != null) { |
| int parentSiblingCount = gp.getChildren().size(); |
| int parentIndex = gp.getChildren().indexOf(p); |
| if (parentIndex < parentSiblingCount - 1) { |
| nextFocus = ((IFigure) p.getParent().getChildren() |
| .get(parentIndex + 1)); |
| siblingPos = parentIndex + 1; |
| untraversedSiblingFound = true; |
| if (isFocusEligible(nextFocus)) |
| found = true; |
| } else |
| nextFocus = p; |
| } else { |
| nextFocus = null; |
| untraversedSiblingFound = true; |
| found = true; |
| } |
| } |
| } |
| } |
| return nextFocus; |
| } |
| |
| /** |
| * Returns the IFigure that will receive focus upon a 'shift-tab' traverse |
| * event. |
| * |
| * @param root |
| * The {@link LightweightSystem LightweightSystem's} root figure |
| * @param prevFocus |
| * The IFigure who currently owns focus |
| * @return the previous focusable figure |
| */ |
| public IFigure getPreviousFocusableFigure(IFigure root, IFigure prevFocus) { |
| if (prevFocus == null) |
| return null; |
| |
| boolean found = false; |
| IFigure nextFocus = prevFocus; |
| while (!found) { |
| IFigure parent = nextFocus.getParent(); |
| |
| /* |
| * At root, return null to indicate traversal is complete. |
| */ |
| if (parent == null) |
| return null; |
| |
| List siblings = parent.getChildren(); |
| int siblingPos = siblings.indexOf(nextFocus); |
| |
| /* |
| * Figure traversal is implemented using the post-order right to |
| * left tree traversal algorithm. |
| * |
| * Find the rightmost child. If this child is focusable, return it |
| * If not focusable, traverse to its sibling and repeat. If there is |
| * no sibling, traverse its parent. |
| */ |
| if (siblingPos != 0) { |
| IFigure child = findDeepestRightmostChildOf((IFigure) siblings |
| .get(siblingPos - 1)); |
| if (isFocusEligible(child)) { |
| found = true; |
| nextFocus = child; |
| } else if (child.equals(nextFocus)) { |
| if (isFocusEligible(nextFocus)) |
| found = true; |
| } else |
| nextFocus = child; |
| } else { |
| nextFocus = parent; |
| if (isFocusEligible(nextFocus)) |
| found = true; |
| } |
| } |
| return nextFocus; |
| } |
| |
| /** |
| * @return the figure that currently has focus |
| */ |
| public IFigure getCurrentFocusOwner() { |
| return currentFocusOwner; |
| } |
| |
| private boolean isFocusEligible(IFigure fig) { |
| return (fig != null && fig.isFocusTraversable() && fig.isShowing()); |
| } |
| |
| /** |
| * Sets the currently focused figure. |
| * |
| * @param fig |
| * the figure to get focus |
| */ |
| public void setCurrentFocusOwner(IFigure fig) { |
| currentFocusOwner = fig; |
| } |
| |
| } |