/******************************************************************************* * Copyright (c) 2000, 2005 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; } }