/*
* Ext GWT 2.2.4 - Ext for GWT
* Copyright(c) 2007-2010, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.aria;
import java.util.ArrayList;
import java.util.List;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.PreviewEvent;
import com.extjs.gxt.ui.client.util.BaseEventPreview;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ComponentManager;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.WindowManager;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* The FocusManager is used to control keyboard navigation among and within
* components.
*/
public class FocusManager {
/**
* The TabNext event type.
*/
public static final EventType TabNext = new EventType();
/**
* The TabPrevious event type.
*/
public static final EventType TabPrevious = new EventType();
/**
* Returns the singleton instance.
*
* @return the focus manager instance
*/
public static FocusManager get() {
if (instance == null) {
instance = new FocusManager();
}
return instance;
}
private List<FocusHandler> handlers = new ArrayList<FocusHandler>();
private List<NavigationHandler> navigationHandlers = new ArrayList<NavigationHandler>();
private BaseEventPreview preview;
private static FocusManager instance;
private boolean managed = true;
private boolean insertExitFocusElement = true;
private Component focusComponent;
private WindowManager mgr = WindowManager.get();
private FocusManager() {
preview = new BaseEventPreview() {
protected void onClick(PreviewEvent pe) {
Component c = ComponentManager.get().find(pe.getTarget());
if (c != null) {
if (isWindow(c) == null && focusComponent == null) {
focusComponent = c;
}
}
};
@Override
protected void onPreviewKeyPress(PreviewEvent pe) {
super.onPreviewKeyPress(pe);
int key = pe.getKeyCode();
Component c = ComponentManager.get().find(pe.getTarget());
if (key == 117) {
onToggle(c, pe);
}
Window w = null;
if (c != null) {
w = isWindow(c);
if (w == null) {
focusComponent = c;
}
for (int i = 0; i < handlers.size(); i++) {
FocusHandler handler = handlers.get(i);
if (handler.canHandleKeyPress(c, pe)) {
switch (key) {
case KeyCodes.KEY_TAB:
if (!pe.isControlKey()) {
handler.onTab(c, pe);
}
break;
case KeyCodes.KEY_LEFT:
handler.onLeft(c, pe);
break;
case KeyCodes.KEY_RIGHT:
handler.onRight(c, pe);
break;
case KeyCodes.KEY_ESCAPE:
handler.onEscape(c, pe);
break;
case KeyCodes.KEY_ENTER:
handler.onEnter(c, pe);
break;
}
return;
}
}
}
}
};
preview.setAutoHide(false);
initHandlers();
}
/**
* Disables the focus manager.
*/
public void disable() {
preview.remove();
}
/**
* Enables the focus manager.
*/
public void enable() {
preview.add();
}
/**
* Returns the first matching navigation handler given the target component.
*
* @param comp the target component
* @return the handler or null if no matches
*/
public NavigationHandler findNavigationHandler(Component comp) {
for (int i = 0, len = navigationHandlers.size(); i < len; i++) {
NavigationHandler h = navigationHandlers.get(i);
if (h.canHandleTabKey(comp)) {
return h;
}
}
return null;
}
public boolean isInsertExitFocusElement() {
return insertExitFocusElement;
}
/**
* Returns true if focus manager is managed.
*
* @return true if managed
*/
public boolean isManaged() {
return managed;
}
/**
* Registers a focus handler.
*
* @param handler the handler
*/
public void register(FocusHandler handler) {
if (!handlers.contains(handler)) {
handlers.add(handler);
}
}
/**
* Registers a navigation handler.
*
* @param handler the handler
*/
public void register(NavigationHandler handler) {
navigationHandlers.add(handler);
}
/**
* True to insert a dummy element to allow the user to tab out of the
* application (defaults to true).
*
* @param insertExitFocusElement true to insert exit element
*/
public void setInsertExitFocusElement(boolean insertExitFocusElement) {
this.insertExitFocusElement = insertExitFocusElement;
}
/**
* True to let focus manager control navigation keys, false to use natural tab
* indexes (defaults to true).
*
* @param managed true if managed
*/
public void setManaged(boolean managed) {
this.managed = managed;
}
/**
* Unregisters a focus handler.
*
* @param handler the focus handler
*/
public void unregister(FocusHandler handler) {
handlers.remove(handler);
}
/**
* Unregisters a navigation handler.
*
* @param handler the navigation handler
*/
public void unregister(NavigationHandler handler) {
navigationHandlers.remove(handler);
}
protected void initHandlers() {
register(new FieldSetHandler());
register(new HtmlEditorHandler());
register(new ToolButtonHandler());
register(new ToolBarHandler());
register(new MenuHandler());
register(new TabPanelHandler());
register(new InputSliderHandler());
register(new DualListFieldHandler());
register(new FieldHandler());
register(new HeaderHandler());
// always last
register(new DefaultHandler());
register(new ContentPanelNavigationHandler());
}
protected void onToggle(Component c, PreviewEvent pe) {
pe.stopEvent();
if (c != null) {
Window w = isWindow(c);
if (w != null) {
onWindowToggle(w, pe);
} else {
onComponentToggle(c, pe);
}
} else {
List<Window> windows = mgr.getWindows();
if (windows.size() > 0) {
mgr.bringToFront(windows.get(0));
}
}
}
private Window isWindow(Component c) {
Widget w = c;
while (w != null && !(w instanceof Window)) {
w = w.getParent();
}
if (w instanceof Window) {
return (Window) w;
}
return null;
}
private void moveToApp() {
if (focusComponent != null) {
focusComponent.focus();
} else {
FocusFrame.get().unframe();
if (RootPanel.get().getWidgetCount() > 0) {
Widget w = RootPanel.get().getWidget(0);
El.fly(w.getElement()).focus();
} else {
El.fly(RootPanel.get().getElement()).focus();
}
}
}
private void onComponentToggle(Component c, PreviewEvent pe) {
List<Window> windows = mgr.getWindows();
if (windows.size() > 0) {
if (pe.isShiftKey()) {
mgr.bringToFront(windows.get(windows.size() - 1));
} else {
mgr.bringToFront(windows.get(0));
}
}
}
private void onWindowToggle(Window w, PreviewEvent pe) {
if (pe.isShiftKey()) {
if (!mgr.activatePrevious(w)) {
moveToApp();
}
} else {
if (!mgr.activateNext(w)) {
moveToApp();
}
}
}
}