/*******************************************************************************
* Copyright (c) 2002, 2010 Innoopract Informationssysteme GmbH.
* 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.rwt.lifecycle;
import java.io.IOException;
import java.lang.reflect.Field;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.rwt.RWT;
import org.eclipse.rwt.internal.lifecycle.JSConst;
import org.eclipse.rwt.internal.service.ContextProvider;
import org.eclipse.rwt.service.IServiceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.events.ActivateEvent;
import org.eclipse.swt.internal.events.EventLCAUtil;
import org.eclipse.swt.internal.graphics.ResourceFactory;
import org.eclipse.swt.internal.widgets.*;
import org.eclipse.swt.widgets.*;
/**
* Utility class that provides a number of useful static methods to support the
* implementation of life cycle adapters for {@link Control}s.
*
* @see WidgetLCAUtil
* @since 1.0
*/
public class ControlLCAUtil {
private static final JSListenerInfo FOCUS_GAINED_LISTENER_INFO
= new JSListenerInfo( "focusin",
"org.eclipse.swt.EventUtil.focusGained",
JSListenerType.ACTION );
private static final JSListenerInfo FOCUS_LOST_LISTENER_INFO
= new JSListenerInfo( "focusout",
"org.eclipse.swt.EventUtil.focusLost",
JSListenerType.ACTION );
private static final JSListenerInfo MOUSE_DOWN_LISTENER_INFO
= new JSListenerInfo( "mousedown",
"org.eclipse.swt.EventUtil.mouseDown",
JSListenerType.ACTION );
private static final JSListenerInfo MOUSE_UP_LISTENER_INFO
= new JSListenerInfo( "mouseup",
"org.eclipse.swt.EventUtil.mouseUp",
JSListenerType.ACTION );
private static final JSListenerInfo MENU_DETECT_LISTENER_INFO_MOUSE
= new JSListenerInfo( "keydown",
"org.eclipse.swt.EventUtil.menuDetectedByKey",
JSListenerType.ACTION );
private static final JSListenerInfo MENU_DETECT_LISTENER_INFO_KEY
= new JSListenerInfo( "mouseup",
"org.eclipse.swt.EventUtil.menuDetectedByMouse",
JSListenerType.ACTION );
private static final String JS_FUNC_ADD_ACTIVATE_LISTENER_WIDGET
= "addActivateListenerWidget";
private static final String JS_FUNC_REMOVE_ACTIVATE_LISTENER_WIDGET
= "removeActivateListenerWidget";
// Property names to preserve widget property values
private static final String PROP_ACTIVATE_LISTENER = "activateListener";
private static final String PROP_FOCUS_LISTENER = "focusListener";
private static final String PROP_MOUSE_LISTENER = "mouseListener";
private static final String PROP_KEY_LISTENER = "keyListener";
private static final String PROP_TRAVERSE_LISTENER = "traverseListener";
private static final String PROP_MENU_DETECT_LISTENER = "menuDetectListener";
private static final String PROP_TAB_INDEX = "tabIndex";
private static final String PROP_CURSOR = "cursor";
private static final String PROP_BACKGROUND_IMAGE = "backgroundImage";
private static final String USER_DATA_KEY_LISTENER = "keyListener";
private static final String USER_DATA_TRAVERSE_LISTENER = "traverseListener";
private static final String USER_DATA_BACKGROUND_IMAGE_SIZE
= "backgroundImageSize";
private static final String ATT_CANCEL_KEY_EVENT
= ControlLCAUtil.class.getName() + "#cancelKeyEvent";
private static final String ATT_ALLOW_KEY_EVENT
= ControlLCAUtil.class.getName() + "#allowKeyEvent";
static final String JSFUNC_CANCEL_EVENT
= "org.eclipse.rwt.KeyEventUtil.getInstance().cancelEvent";
static final String JSFUNC_ALLOW_EVENT
= "org.eclipse.rwt.KeyEventUtil.getInstance().allowEvent";
static final int MAX_STATIC_ZORDER = 300;
private static final String CURSOR_UPARROW = "widget/cursors/up_arrow.cur";
private ControlLCAUtil() {
// prevent instance creation
}
/**
* Preserves the values of the following properties of the specified control:
* <ul>
* <li>bounds</li>
* <li>z-index (except for Shells)</li>
* <li>tab index</li>
* <li>tool tip text</li>
* <li>menu</li>
* <li>visible</li>
* <li>enabled</li>
* <li>foreground</li>
* <li>background</li>
* <li>background image</li>
* <li>font</li>
* <li>cursor</li>
* <li>whether ControlListeners are registered</li>
* <li>whether ActivateListeners are registered</li>
* <li>whether MouseListeners are registered</li>
* <li>whether FocusListeners are registered</li>
* <li>whether KeyListeners are registered</li>
* <li>whether TraverseListeners are registered</li>
* <li>whether HelpListeners are registered</li>
* <li>whether MenuDetectListeners are registered</li>
* </ul>
*
* @param control the control whose parameters to preserve
* @see #writeChanges(Control)
*/
public static void preserveValues( final Control control ) {
IWidgetAdapter adapter = WidgetUtil.getAdapter( control );
WidgetLCAUtil.preserveBounds( control, control.getBounds() );
// TODO [rh] revise this (see also writeZIndex)
if( !( control instanceof Shell ) ) {
adapter.preserve( Props.Z_INDEX, new Integer( getZIndex( control ) ) );
}
adapter.preserve( PROP_TAB_INDEX, new Integer( getTabIndex( control ) ) );
WidgetLCAUtil.preserveToolTipText( control, control.getToolTipText() );
adapter.preserve( Props.MENU, control.getMenu() );
adapter.preserve( Props.VISIBLE, Boolean.valueOf( getVisible( control ) ) );
WidgetLCAUtil.preserveEnabled( control, control.getEnabled() );
IControlAdapter controlAdapter
= ( IControlAdapter )control.getAdapter( IControlAdapter.class );
WidgetLCAUtil.preserveForeground( control,
controlAdapter.getUserForeground() );
WidgetLCAUtil.preserveBackground( control,
controlAdapter.getUserBackground(),
controlAdapter.getBackgroundTransparency() );
preserveBackgroundImage( control );
WidgetLCAUtil.preserveFont( control, controlAdapter.getUserFont() );
adapter.preserve( PROP_CURSOR, control.getCursor() );
adapter.preserve( Props.CONTROL_LISTENERS,
Boolean.valueOf( ControlEvent.hasListener( control ) ) );
adapter.preserve( PROP_ACTIVATE_LISTENER,
Boolean.valueOf( ActivateEvent.hasListener( control ) ) );
adapter.preserve( PROP_MOUSE_LISTENER,
Boolean.valueOf( MouseEvent.hasListener( control ) ) );
if( ( control.getStyle() & SWT.NO_FOCUS ) == 0 ) {
adapter.preserve( PROP_FOCUS_LISTENER,
Boolean.valueOf( FocusEvent.hasListener( control ) ) );
}
adapter.preserve( PROP_KEY_LISTENER,
Boolean.valueOf( KeyEvent.hasListener( control ) ) );
adapter.preserve( PROP_TRAVERSE_LISTENER,
Boolean.valueOf( TraverseEvent.hasListener( control ) ) );
WidgetLCAUtil.preserveHelpListener( control );
preserveMenuDetectListener( control );
}
/**
* Reads the bounds of the specified control from the current request and
* applies it to the control. If no bounds are not submitted for the control,
* it remains unchanged.
*
* @param control the control whose bounds to read and set
*/
// TODO [rst] Revise: This seems to unnecessarily call getter and setter even
// when no bounds are submitted.
public static void readBounds( final Control control ) {
Rectangle current = control.getBounds();
Rectangle newBounds = WidgetLCAUtil.readBounds( control, current );
control.setBounds( newBounds );
}
/**
* Determines whether the bounds of the given control have changed during the
* processing of the current request and if so, writes JavaScript code to the
* response that updates the client-side bounds.
*
* @param control the control whose bounds to write
* @throws IOException
*/
public static void writeBounds( final Control control ) throws IOException {
Composite parent = control.getParent();
WidgetLCAUtil.writeBounds( control, parent, control.getBounds() );
}
/**
* Determines whether the z-index of the given control has changed during the
* processing of the current request and if so, writes JavaScript code to the
* response that updates the client-side z-index.
*
* @param control the control whose z-index to write
* @throws IOException
*/
public static void writeZIndex( final Control control ) throws IOException {
// TODO [rst] remove surrounding if statement as soon as z-order on shells
// is completely implemented
if( !( control instanceof Shell ) ) {
JSWriter writer = JSWriter.getWriterFor( control );
Integer newValue = new Integer( getZIndex( control ) );
writer.set( Props.Z_INDEX, JSConst.QX_FIELD_Z_INDEX, newValue, null );
}
}
/**
* Determines whether the visibility of the given control has changed during
* the processing of the current request and if so, writes JavaScript code to
* the response that updates the client-side visibility.
*
* @param control the control whose visibility to write
* @throws IOException
*/
// TODO [rh] there seems to be a qooxdoo problem when trying to change the
// visibility of a newly created widget (no flushGlobalQueues was called)
// MSG: Modification of property "visibility" failed with exception:
// Error - Element must be created previously!
public static void writeVisible( final Control control )
throws IOException
{
// we only need getVisible here (not isVisible), as qooxdoo also hides/shows
// contained controls
Boolean newValue = Boolean.valueOf( getVisible( control ) );
Boolean defValue = control instanceof Shell ? Boolean.FALSE : Boolean.TRUE;
JSWriter writer = JSWriter.getWriterFor( control );
writer.set( Props.VISIBLE, JSConst.QX_FIELD_VISIBLE, newValue, defValue );
}
// [if] Fix for bug 263025, 297466, 223873 and more
// some qooxdoo widget with size (0,0) are not invisible
private static boolean getVisible( final Control control ) {
Point size = control.getSize();
return control.getVisible() && size.x > 0 && size.y > 0;
}
/**
* Determines whether the property <code>enabled</code> of the given control
* has changed during the processing of the current request and if so, writes
* JavaScript code to the response that updates the client-side enabled
* property.
*
* @param control the control whose enabled property to write
* @throws IOException
*/
public static void writeEnabled( final Control control )
throws IOException
{
// Using isEnabled() would result in unnecessarily updating child widgets of
// enabled/disabled controls.
WidgetLCAUtil.writeEnabled( control, control.getEnabled() );
}
/**
* Determines for all of the following properties of the specified control
* whether the property has changed during the processing of the current
* request and if so, writes JavaScript code to the response that updates the
* corresponding client-side property.
* <ul>
* <li>bounds</li>
* <li>z-index (except for Shells)</li>
* <li>tab index</li>
* <li>tool tip text</li>
* <li>menu</li>
* <li>visible</li>
* <li>enabled</li>
* <li>foreground</li>
* <li>background</li>
* <li>background image</li>
* <li>font</li>
* <li>cursor</li>
* <!--li>whether ControlListeners are registered</li-->
* <li>whether ActivateListeners are registered</li>
* <li>whether MouseListeners are registered</li>
* <li>whether FocusListeners are registered</li>
* <li>whether KeyListeners are registered</li>
* <li>whether TraverseListeners are registered</li>
* <li>whether HelpListeners are registered</li>
* </ul>
*
* @param control the control whose properties to set
* @throws IOException
* @see #preserveValues(Control)
*/
public static void writeChanges( final Control control ) throws IOException {
writeBounds( control );
writeZIndex( control );
writeTabIndex( control );
writeToolTip( control );
writeMenu( control );
writeVisible( control );
writeEnabled( control );
writeForeground( control );
writeBackground( control );
writeBackgroundImage( control );
writeFont( control );
writeCursor( control );
// TODO [rst] missing: writeControlListener( control );
writeActivateListener( control );
writeFocusListener( control );
writeMouseListener( control );
writeKeyListener( control );
writeTraverseListener( control );
writeKeyEventResponse( control );
writeMenuDetectListener( control );
WidgetLCAUtil.writeHelpListener( control );
}
/**
* Determines whether the property <code>menu</code> of the given control
* has changed during the processing of the current request and if so, writes
* JavaScript code to the response that updates the client-side menu
* property.
*
* @param control the control whose menu property to write
* @throws IOException
*/
public static void writeMenu( final Control control ) throws IOException {
WidgetLCAUtil.writeMenu( control, control.getMenu() );
}
/**
* Determines whether the tool tip of the given control has changed during the
* processing of the current request and if so, writes JavaScript code to the
* response that updates the client-side tool tip.
*
* @param control the control whose tool tip to write
* @throws IOException
*/
public static void writeToolTip( final Control control )
throws IOException
{
WidgetLCAUtil.writeToolTip( control, control.getToolTipText() );
}
/**
* Determines whether the property <code>foreground</code> of the given
* control has changed during the processing of the current request and if so,
* writes JavaScript code to the response that updates the client-side
* foreground property.
*
* @param control the control whose foreground property to write
* @throws IOException
*/
public static void writeForeground( final Control control )
throws IOException
{
IControlAdapter controlAdapter
= ( IControlAdapter )control.getAdapter( IControlAdapter.class );
WidgetLCAUtil.writeForeground( control,
controlAdapter.getUserForeground() );
}
/**
* Determines whether the property <code>background</code> of the given
* control has changed during the processing of the current request and if so,
* writes JavaScript code to the response that updates the client-side
* background property.
*
* @param control the control whose background property to write
* @throws IOException
*/
public static void writeBackground( final Control control ) throws IOException
{
IControlAdapter controlAdapter
= ( IControlAdapter )control.getAdapter( IControlAdapter.class );
WidgetLCAUtil.writeBackground( control,
controlAdapter.getUserBackground(),
controlAdapter.getBackgroundTransparency() );
}
/**
* Preserves the value of the specified widget's background image.
*
* @param control the control whose background image property to preserve
* @see #writeBackgroundImage(Control)
*/
public static void preserveBackgroundImage( final Control control ) {
IControlAdapter controlAdapter
= ( IControlAdapter )control.getAdapter( IControlAdapter.class );
Image image = controlAdapter.getUserBackgroundImage();
IWidgetAdapter adapter = WidgetUtil.getAdapter( control );
adapter.preserve( PROP_BACKGROUND_IMAGE, image );
}
/**
* Determines whether the background image of the given control has changed
* during the processing of the current request and if so, writes JavaScript
* code to the response that updates the client-side background image
* property.
*
* @param control the control whose background image property to write
* @throws IOException
*/
public static void writeBackgroundImage( final Control control )
throws IOException
{
IControlAdapter controlAdapter
= ( IControlAdapter )control.getAdapter( IControlAdapter.class );
Image image = controlAdapter.getUserBackgroundImage();
if( WidgetLCAUtil.hasChanged( control, PROP_BACKGROUND_IMAGE, image, null ) )
{
JSWriter writer = JSWriter.getWriterFor( control );
if( image != null ) {
String imagePath = ResourceFactory.getImagePath( image );
Rectangle bounds = image.getBounds();
Object[] args = new Object[]{
USER_DATA_BACKGROUND_IMAGE_SIZE,
new Integer[]{
new Integer( bounds.width ),
new Integer( bounds.height )
}
};
writer.call( "setUserData", args );
writer.set( "backgroundImage", imagePath );
} else {
Object[] args = new Object[]{ USER_DATA_BACKGROUND_IMAGE_SIZE, null };
writer.call( "setUserData", args );
writer.reset( "backgroundImage" );
}
}
}
/**
* Checks the given control for common SWT style flags (e.g.
* <code>SWT.BORDER</code>) and if present, writes code to pass the according
* states to the client.
*
* @param control
* @throws IOException
*/
public static void writeStyleFlags( final Control control ) throws IOException
{
WidgetLCAUtil.writeStyleFlag( control, SWT.BORDER, "BORDER" );
}
/**
* Determines whether the property <code>font</code> of the given control
* has changed during the processing of the current request and if so, writes
* JavaScript code to the response that updates the client-side font property.
*
* @param control the control whose font property to write
* @throws IOException
*/
public static void writeFont( final Control control ) throws IOException {
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
Font newValue = controlAdapter.getUserFont();
WidgetLCAUtil.writeFont( control, newValue );
}
static void writeCursor( final Control control ) throws IOException {
Cursor newValue = control.getCursor();
if( WidgetLCAUtil.hasChanged( control, PROP_CURSOR, newValue, null ) ) {
String qxCursor = getQxCursor( newValue );
JSWriter writer = JSWriter.getWriterFor( control );
if( qxCursor == null ) {
writer.reset( JSConst.QX_FIELD_CURSOR );
} else {
writer.set( JSConst.QX_FIELD_CURSOR, qxCursor );
}
}
}
public static void writeActivateListener( final Control control )
throws IOException
{
if( !control.isDisposed() ) {
Boolean newValue
= Boolean.valueOf( ActivateEvent.hasListener( control ) );
Boolean defValue = Boolean.FALSE;
String prop = PROP_ACTIVATE_LISTENER;
Shell shell = control.getShell();
if( !shell.isDisposed()
&& WidgetLCAUtil.hasChanged( control, prop, newValue, defValue ) )
{
String function = newValue.booleanValue()
? JS_FUNC_ADD_ACTIVATE_LISTENER_WIDGET
: JS_FUNC_REMOVE_ACTIVATE_LISTENER_WIDGET;
JSWriter writer = JSWriter.getWriterFor( control );
writer.call( shell, function, new Object[]{ control } );
}
}
}
static void resetActivateListener( final Control control )
throws IOException
{
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
Shell shell = controlAdapter.getShell();
if( !shell.isDisposed() && ActivateEvent.hasListener( control ) ) {
JSWriter writer = JSWriter.getWriterFor( control );
writer.call( shell,
JS_FUNC_REMOVE_ACTIVATE_LISTENER_WIDGET,
new Object[] { control } );
}
}
/**
* Note that there is no corresponding readData method to fire the focus
* events that are send by the JavaScript event listeners that are registered
* below.
* FocusEvents are thrown when the focus is changed programmatically and when
* it is change by the user.
* Therefore the methods in Display that maintain the current focusControl
* also fire FocusEvents. The current client-side focusControl is read in
* DisplayLCA#readData.
*/
private static void writeFocusListener( final Control control )
throws IOException
{
if( ( control.getStyle() & SWT.NO_FOCUS ) == 0 ) {
JSWriter writer = JSWriter.getWriterFor( control );
boolean hasListener = FocusEvent.hasListener( control );
writer.updateListener( FOCUS_GAINED_LISTENER_INFO,
PROP_FOCUS_LISTENER,
hasListener );
writer.updateListener( FOCUS_LOST_LISTENER_INFO,
PROP_FOCUS_LISTENER,
hasListener );
}
}
private static void writeMouseListener( final Control control )
throws IOException
{
boolean hasListener = MouseEvent.hasListener( control );
JSWriter writer = JSWriter.getWriterFor( control );
writer.updateListener( MOUSE_UP_LISTENER_INFO,
PROP_MOUSE_LISTENER,
hasListener );
writer.updateListener( MOUSE_DOWN_LISTENER_INFO,
PROP_MOUSE_LISTENER,
hasListener );
}
static void writeKeyListener( final Control control )
throws IOException
{
String prop = PROP_KEY_LISTENER;
Boolean hasListener = Boolean.valueOf( KeyEvent.hasListener( control ) );
Boolean defValue = Boolean.FALSE;
if( WidgetLCAUtil.hasChanged( control, prop, hasListener, defValue ) ) {
JSWriter writer = JSWriter.getWriterFor( control );
if( hasListener.booleanValue() ) {
Object[] args = new Object[] { USER_DATA_KEY_LISTENER, hasListener };
writer.call( "setUserData", args );
} else {
Object[] args = new Object[] { USER_DATA_KEY_LISTENER, null };
writer.call( "setUserData", args );
}
}
}
static void writeTraverseListener( final Control control )
throws IOException
{
String prop = PROP_TRAVERSE_LISTENER;
Boolean hasListener
= Boolean.valueOf( TraverseEvent.hasListener( control ) );
Boolean defValue = Boolean.FALSE;
if( WidgetLCAUtil.hasChanged( control, prop, hasListener, defValue ) ) {
JSWriter writer = JSWriter.getWriterFor( control );
if( hasListener.booleanValue() ) {
Object[] args = new Object[] {
USER_DATA_TRAVERSE_LISTENER,
hasListener
};
writer.call( "setUserData", args );
} else {
Object[] args = new Object[] { USER_DATA_TRAVERSE_LISTENER, null };
writer.call( "setUserData", args );
}
}
}
///////////////////////
// Menu Detect Listener
/**
* Preserves whether the given <code>widget</code> has one or more
* <code>MenuDetect</code>s attached.
*
* @param control the widget to preserve
* @since 1.3
*/
public static void preserveMenuDetectListener( final Control control ) {
IWidgetAdapter adapter = WidgetUtil.getAdapter( control );
boolean hasListener = MenuDetectEvent.hasListener( control );
adapter.preserve( PROP_MENU_DETECT_LISTENER,
Boolean.valueOf( hasListener ) );
}
/**
* Adds or removes client-side menu detect listeners for the the given
* <code>control</code> as necessary.
*
* @param control
* @since 1.3
*/
public static void writeMenuDetectListener( final Control control )
throws IOException
{
boolean hasListener = MenuDetectEvent.hasListener( control );
JSWriter writer = JSWriter.getWriterFor( control );
writer.updateListener( MENU_DETECT_LISTENER_INFO_MOUSE,
PROP_MENU_DETECT_LISTENER,
hasListener );
writer.updateListener( MENU_DETECT_LISTENER_INFO_KEY,
PROP_MENU_DETECT_LISTENER,
hasListener );
}
/**
* Process a <code>HelpEvent</code> if the current request specifies that
* there occured a help event for the given <code>widget</code>.
*
* @param control the control to process
* @since 1.3
*/
public static void processMenuDetect( final Control control ) {
if( WidgetLCAUtil.wasEventSent( control, JSConst.EVENT_MENU_DETECT ) ) {
MenuDetectEvent event = new MenuDetectEvent( control );
Point point = readXYParams( control,
JSConst.EVENT_MENU_DETECT_X,
JSConst.EVENT_MENU_DETECT_Y );
point = control.getDisplay().map( control, null, point );
event.x = point.x;
event.y = point.y;
event.processEvent();
}
}
//////////
// Z-Index
/**
* Determines the z-index to render for a given control.
* @param control the control whose z-index is requested
* @return the z-index
*/
// TODO [rst] also document the meaning of the returned number
public static int getZIndex( final Control control ) {
int max = MAX_STATIC_ZORDER;
if( control.getParent() != null ) {
// TODO [rh] revise: determining the childrenCount by getting all the
// children might be bad performance-wise. This was done in order to
// eliminate Composite#getChildrenCount() which is no API in SWT
max = Math.max( control.getParent().getChildren().length, max );
}
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
return max - controlAdapter.getZIndex();
}
////////////
// Tab index
private static void writeTabIndex( final Control control ) throws IOException
{
if( control instanceof Shell ) {
resetTabIndices( ( Shell )control );
// tabIndex must be a positive value
computeTabIndices( ( Shell )control, 1 );
}
int tabIndex = getTabIndex( control );
Integer newValue = new Integer( tabIndex );
JSWriter writer = JSWriter.getWriterFor( control );
// there is no reliable default value for all controls
writer.set( PROP_TAB_INDEX, JSConst.QX_FIELD_TAB_INDEX, newValue );
}
/**
* Recursively computes the tab indices for all child controls of a given
* composite and stores the resulting values in the control adapters.
*/
private static int computeTabIndices( final Composite composite,
final int startIndex )
{
Control[] tabList = composite.getTabList();
int nextIndex = startIndex;
for( int i = 0; i < tabList.length; i++ ) {
Control control = tabList[ i ];
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
controlAdapter.setTabIndex( nextIndex );
// for Links, leave a range out to be assigned to hrefs on the client
if( control instanceof Link ) {
nextIndex += 300;
} else {
nextIndex += 1;
}
if( control instanceof Composite ) {
nextIndex = computeTabIndices( ( Composite )control, nextIndex );
}
}
return nextIndex;
}
private static void resetTabIndices( final Composite comp ) {
Control[] children = comp.getChildren();
for( int i = 0; i < children.length; i++ ) {
Control control = children[ i ];
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
controlAdapter.setTabIndex( -1 );
if( control instanceof Composite ) {
resetTabIndices( ( Composite )control );
}
}
}
private static int getTabIndex( final Control control ) {
int result = -1;
if( takesFocus( control ) ) {
Object adapter = control.getAdapter( IControlAdapter.class );
IControlAdapter controlAdapter = ( IControlAdapter )adapter;
result = controlAdapter.getTabIndex();
}
return result;
}
// TODO [rh] Eliminate instance checks. Let the respective classes always return NO_FOCUS
private static boolean takesFocus( final Control control ) {
boolean result = true;
result &= ( control.getStyle() & SWT.NO_FOCUS ) == 0;
result &= control.getClass() != Composite.class;
result &= control.getClass() != SashForm.class;
return result;
}
/////////////////////
// Selection Listener
public static void processSelection( final Widget widget,
final Item item,
final boolean readBounds )
{
String eventId = JSConst.EVENT_WIDGET_SELECTED;
if( WidgetLCAUtil.wasEventSent( widget, eventId ) ) {
SelectionEvent event;
event = createSelectionEvent( widget,
item,
readBounds,
SelectionEvent.WIDGET_SELECTED );
event.processEvent();
}
eventId = JSConst.EVENT_WIDGET_DEFAULT_SELECTED;
if( WidgetLCAUtil.wasEventSent( widget, eventId ) ) {
SelectionEvent event;
event = createSelectionEvent( widget,
item,
readBounds,
SelectionEvent.WIDGET_DEFAULT_SELECTED );
event.processEvent();
}
}
private static SelectionEvent createSelectionEvent( final Widget widget,
final Item item,
final boolean readBounds,
final int type )
{
Rectangle bounds;
if( widget instanceof Control && readBounds ) {
Control control = ( Control )widget;
bounds = WidgetLCAUtil.readBounds( control, control.getBounds() );
} else {
bounds = new Rectangle( 0, 0, 0, 0 );
}
int stateMask
= EventLCAUtil.readStateMask( JSConst.EVENT_WIDGET_SELECTED_MODIFIER );
return new SelectionEvent( widget,
item,
type,
bounds,
stateMask,
null,
true,
SWT.NONE );
}
public static void processKeyEvents( final Control control ) {
if( WidgetLCAUtil.wasEventSent( control, JSConst.EVENT_KEY_DOWN ) ) {
final int keyCode = readIntParam( JSConst.EVENT_KEY_DOWN_KEY_CODE );
final int charCode = readIntParam( JSConst.EVENT_KEY_DOWN_CHAR_CODE );
final int stateMask
= EventLCAUtil.readStateMask( JSConst.EVENT_KEY_DOWN_MODIFIER );
final int traverseKey = getTraverseKey( keyCode, stateMask );
ProcessActionRunner.add( new Runnable() {
public void run() {
boolean allow = true;
if( traverseKey != SWT.TRAVERSE_NONE ) {
TraverseEvent traverseEvent = new TraverseEvent( control );
initializeKeyEvent( traverseEvent, keyCode, charCode, stateMask );
traverseEvent.detail = traverseKey;
traverseEvent.processEvent();
if( !traverseEvent.doit ) {
allow = false;
}
}
KeyEvent pressedEvent = new KeyEvent( control, KeyEvent.KEY_PRESSED );
initializeKeyEvent( pressedEvent, keyCode, charCode, stateMask );
pressedEvent.processEvent();
if( pressedEvent.doit ) {
KeyEvent releasedEvent
= new KeyEvent( control, KeyEvent.KEY_RELEASED );
initializeKeyEvent( releasedEvent, keyCode, charCode, stateMask );
releasedEvent.processEvent();
} else {
allow = false;
}
if( allow ) {
allowKeyEvent( control );
} else {
cancelKeyEvent( control );
}
}
} );
}
}
static int getTraverseKey( final int keyCode, final int stateMask ) {
int result = SWT.TRAVERSE_NONE;
switch( keyCode ) {
case 27:
result = SWT.TRAVERSE_ESCAPE;
break;
case 13:
result = SWT.TRAVERSE_RETURN;
break;
case 9:
if( ( stateMask & SWT.MODIFIER_MASK ) == 0 ) {
result = SWT.TRAVERSE_TAB_NEXT;
} else if( stateMask == SWT.SHIFT ) {
result = SWT.TRAVERSE_TAB_PREVIOUS;
}
break;
}
return result;
}
private static void initializeKeyEvent( final KeyEvent event,
final int keyCode,
final int charCode,
final int stateMask )
{
if( charCode == 0 ) {
event.keyCode = translateKeyCode( keyCode );
if( ( event.keyCode & SWT.KEYCODE_BIT ) == 0 ) {
event.character = translateCharacter( event.keyCode );
}
} else {
event.keyCode = charCode;
event.character = translateCharacter( charCode );
}
event.stateMask = stateMask;
}
static int translateKeyCode( final int keyCode ) {
int result;
switch( keyCode ) {
case 20:
result = SWT.CAPS_LOCK;
break;
case 38:
result = SWT.ARROW_UP;
break;
case 37:
result = SWT.ARROW_LEFT;
break;
case 39:
result = SWT.ARROW_RIGHT;
break;
case 40:
result = SWT.ARROW_DOWN;
break;
case 33:
result = SWT.PAGE_UP;
break;
case 34:
result = SWT.PAGE_DOWN;
break;
case 35:
result = SWT.END;
break;
case 36:
result = SWT.HOME;
break;
case 45:
result = SWT.INSERT;
break;
case 46:
result = SWT.DEL;
break;
case 112:
result = SWT.F1;
break;
case 113:
result = SWT.F2;
break;
case 114:
result = SWT.F3;
break;
case 115:
result = SWT.F4;
break;
case 116:
result = SWT.F5;
break;
case 117:
result = SWT.F6;
break;
case 118:
result = SWT.F7;
break;
case 119:
result = SWT.F8;
break;
case 120:
result = SWT.F9;
break;
case 121:
result = SWT.F10;
break;
case 122:
result = SWT.F11;
break;
case 123:
result = SWT.F12;
break;
case 144:
result = SWT.NUM_LOCK;
break;
case 44:
result = SWT.PRINT_SCREEN;
break;
case 145:
result = SWT.SCROLL_LOCK;
break;
case 19:
result = SWT.PAUSE;
break;
default:
result = keyCode;
}
return result;
}
private static char translateCharacter( final int keyCode ) {
char result = ( char )0;
if( Character.isDefined( ( char )keyCode ) ) {
result = ( char )keyCode;
}
return result;
}
private static void cancelKeyEvent( final Widget widget) {
RWT.getServiceStore().setAttribute( ATT_CANCEL_KEY_EVENT, widget );
}
private static void allowKeyEvent( final Widget widget ) {
RWT.getServiceStore().setAttribute( ATT_ALLOW_KEY_EVENT, widget );
}
private static void writeKeyEventResponse( final Control control )
throws IOException
{
IServiceStore serviceStore = RWT.getServiceStore();
if( serviceStore.getAttribute( ATT_ALLOW_KEY_EVENT ) == control ) {
JSWriter writer = JSWriter.getWriterFor( control );
writer.callStatic( JSFUNC_ALLOW_EVENT, null );
} else if( serviceStore.getAttribute( ATT_CANCEL_KEY_EVENT ) == control ) {
JSWriter writer = JSWriter.getWriterFor( control );
writer.callStatic( JSFUNC_CANCEL_EVENT, null );
}
}
public static void processMouseEvents( final Control control ) {
if( WidgetLCAUtil.wasEventSent( control, JSConst.EVENT_MOUSE_DOWN ) ) {
MouseEvent event = new MouseEvent( control, MouseEvent.MOUSE_DOWN );
event.button
= readIntParam( JSConst.EVENT_MOUSE_DOWN_BUTTON );
Point point = readXYParams( control,
JSConst.EVENT_MOUSE_DOWN_X,
JSConst.EVENT_MOUSE_DOWN_Y );
event.x = point.x;
event.y = point.y;
event.time = readIntParam( JSConst.EVENT_MOUSE_DOWN_TIME );
event.stateMask
= EventLCAUtil.readStateMask( JSConst.EVENT_MOUSE_DOWN_MODIFIER )
| EventLCAUtil.translateButton( event.button );
checkAndProcessMouseEvent( event );
}
String eventId = JSConst.EVENT_MOUSE_DOUBLE_CLICK;
if( WidgetLCAUtil.wasEventSent( control, eventId ) ) {
MouseEvent event
= new MouseEvent( control, MouseEvent.MOUSE_DOUBLE_CLICK );
event.button
= readIntParam( JSConst.EVENT_MOUSE_DOUBLE_CLICK_BUTTON );
Point point = readXYParams( control,
JSConst.EVENT_MOUSE_DOUBLE_CLICK_X,
JSConst.EVENT_MOUSE_DOUBLE_CLICK_Y );
event.x = point.x;
event.y = point.y;
event.time = readIntParam( JSConst.EVENT_MOUSE_DOUBLE_CLICK_TIME );
String stateMaskParam = JSConst.EVENT_MOUSE_DOUBLE_CLICK_MODIFIER;
event.stateMask = EventLCAUtil.readStateMask( stateMaskParam )
| EventLCAUtil.translateButton( event.button );
checkAndProcessMouseEvent( event );
}
if( WidgetLCAUtil.wasEventSent( control, JSConst.EVENT_MOUSE_UP ) ) {
MouseEvent event = new MouseEvent( control, MouseEvent.MOUSE_UP );
event.button = readIntParam( JSConst.EVENT_MOUSE_UP_BUTTON );
Point point = readXYParams( control,
JSConst.EVENT_MOUSE_UP_X,
JSConst.EVENT_MOUSE_UP_Y );
event.x = point.x;
event.y = point.y;
event.time = readIntParam( JSConst.EVENT_MOUSE_UP_TIME );
event.stateMask
= EventLCAUtil.readStateMask( JSConst.EVENT_MOUSE_UP_MODIFIER )
| EventLCAUtil.translateButton( event.button );
checkAndProcessMouseEvent( event );
}
}
private static void checkAndProcessMouseEvent( final MouseEvent event ) {
boolean pass = false;
Control control = ( Control )event.widget;
if( control instanceof Scrollable ) {
Scrollable scrollable = ( Scrollable )control;
Rectangle clientArea = scrollable.getClientArea();
pass = clientArea.contains( event.x, event.y );
} else {
pass = event.x >= 0 && event.y >= 0;
}
if( pass ) {
event.processEvent();
}
}
private static String readStringParam( final String paramName ) {
HttpServletRequest request = ContextProvider.getRequest();
String value = request.getParameter( paramName );
return value;
}
private static int readIntParam( final String paramName ) {
String value = readStringParam( paramName );
return Integer.parseInt( value );
}
private static Point readXYParams( final Control control,
final String paramNameX,
final String paramNameY )
{
int x = readIntParam( paramNameX );
int y = readIntParam( paramNameY );
return control.getDisplay().map( null, control, x, y );
}
private static String getQxCursor( final Cursor newValue ) {
String result = null;
if( newValue != null ) {
// TODO [rst] Find a better way of obtaining the Cursor value
int value = 0;
try {
Class cursorClass = Cursor.class;
Field field = cursorClass.getDeclaredField( "value" );
field.setAccessible( true );
value = field.getInt( newValue );
} catch( Exception e ) {
throw new RuntimeException( e );
}
switch( value ) {
case SWT.CURSOR_ARROW:
result = "default";
break;
case SWT.CURSOR_WAIT:
result = "wait";
break;
case SWT.CURSOR_APPSTARTING:
result = "progress";
break;
case SWT.CURSOR_CROSS:
result = "crosshair";
break;
case SWT.CURSOR_HELP:
result = "help";
break;
case SWT.CURSOR_SIZEALL:
result = "move";
break;
case SWT.CURSOR_SIZENS:
result = "row-resize";
break;
case SWT.CURSOR_SIZEWE:
result = "col-resize";
break;
case SWT.CURSOR_SIZEN:
result = "n-resize";
break;
case SWT.CURSOR_SIZES:
result = "s-resize";
break;
case SWT.CURSOR_SIZEE:
result = "e-resize";
break;
case SWT.CURSOR_SIZEW:
result = "w-resize";
break;
case SWT.CURSOR_SIZENE:
result = "ne-resize";
break;
case SWT.CURSOR_SIZESE:
result = "se-resize";
break;
case SWT.CURSOR_SIZESW:
result = "sw-resize";
break;
case SWT.CURSOR_SIZENW:
result = "nw-resize";
break;
case SWT.CURSOR_IBEAM:
result = "text";
break;
case SWT.CURSOR_HAND:
result = "pointer";
break;
case SWT.CURSOR_NO:
result = "not-allowed";
break;
case SWT.CURSOR_UPARROW:
result = CURSOR_UPARROW;
break;
}
}
return result;
}
/////////////////////////////////////
// deprecated pooling-related methods
/**
* Writes JavaScript code to the response that resets the bounds of a control.
* This method is intended to be used by implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetBounds() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the z-index property of
* a control. This method is intended to be used by implementations of the
* method {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetZIndex() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the property
* <code>visible</code> of a control. This method is intended to be used by
* implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetVisible() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the property
* <code>enabled</code> of a control. This method is intended to be used by
* implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetEnabled() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the following properties
* of a control.
* <ul>
* <li>bounds</li>
* <li>z-index (except for Shells)</li>
* <li>tab index</li>
* <li>tool tip text</li>
* <li>menu</li>
* <li>visible</li>
* <li>enabled</li>
* <li>foreground</li>
* <li>background</li>
* <li>font</li>
* <!--li>whether ControlListeners are registered</li>
* <li>whether ActivateListeners are registered</li>
* <li>whether FocusListeners are registered</li-->
* </ul>
* This method is intended to be used by implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetChanges() throws IOException {
}
/**
* Writes JavaScript code to the response that removes the client-side resize
* notification listeners from a control.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetResizeNotificator()
throws IOException
{
}
/**
* Writes JavaScript code to the response that removes the client-side move
* notification listeners from a control.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetMoveNotificator()
throws IOException
{
}
/**
* Writes JavaScript code to the response that resets the property
* <code>menu</code> of a control. This method is intended to be used by
* implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetMenu() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the tool tip of a
* control. This method is intended to be used by implementations of the
* method {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetToolTip() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the property
* <code>foreground</code> of a control. This method is intended to be used
* by implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetForeground() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the property
* <code>background</code> of a control. This method is intended to be used
* by implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetBackground() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the style flags.
* <p>This method is intended to be used by implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.</p>
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetStyleFlags() throws IOException {
}
/**
* Writes JavaScript code to the response that resets the property
* <code>font</code> of a control. This method is intended to be used by
* implementations of the method
* {@link AbstractWidgetLCA#createResetHandlerCalls(String)}.
*
* @throws IOException
* @deprecated As of 1.3, server-side widget pooling is no longer required.
* This method does nothing.
*/
public static void resetFont() throws IOException {
}
}