/*******************************************************************************
* Copyright (c) 2013, 2016 EclipseSource 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:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.rap.rwt.internal.protocol;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetUtil.getId;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_ACTIVATE;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_DEACTIVATE;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_FOCUS_IN;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_FOCUS_OUT;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_HELP;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_KEY_DOWN;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_MENU_DETECT;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_MOUSE_DOUBLE_CLICK;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_MOUSE_DOWN;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_MOUSE_UP;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_BUTTON;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_CHAR_CODE;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_KEY_CODE;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_TIME;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_X;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_PARAM_Y;
import static org.eclipse.rap.rwt.internal.protocol.ClientMessageConst.EVENT_TRAVERSE;
import static org.eclipse.rap.rwt.internal.protocol.ProtocolUtil.wasEventSent;
import static org.eclipse.swt.internal.events.EventLCAUtil.translateButton;
import static org.eclipse.swt.internal.widgets.ControlUtil.getControlAdapter;
import org.eclipse.rap.json.JsonArray;
import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.json.JsonValue;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
public abstract class ControlOperationHandler<T extends Control> extends WidgetOperationHandler<T> {
private static final String PROP_FOREGROUND = "foreground";
private static final String PROP_BACKGROUND = "background";
private static final String PROP_VISIBILITY = "visibility";
private static final String PROP_ENABLED = "enabled";
private static final String PROP_TOOL_TIP = "toolTip";
private static final String PROP_CURSOR = "cursor";
public ControlOperationHandler( T control ) {
super( control );
}
@Override
public void handleSet( T control, JsonObject properties ) {
handleSetForeground( control, properties );
handleSetBackground( control, properties );
handleSetVisibility( control, properties );
handleSetEnabled( control, properties );
handleSetToolTip( control, properties );
handleSetCursor( control, properties );
}
@Override
public void handleNotify( T control, String eventName, JsonObject properties ) {
if( EVENT_FOCUS_IN.equals( eventName ) ) {
handleNotifyFocusIn( control, properties );
} else if( EVENT_FOCUS_OUT.equals( eventName ) ) {
handleNotifyFocusOut( control, properties );
} else if( EVENT_MOUSE_DOWN.equals( eventName ) ) {
handleNotifyMouseDown( control, properties );
} else if( EVENT_MOUSE_DOUBLE_CLICK.equals( eventName ) ) {
handleNotifyMouseDoubleClick( control, properties );
} else if( EVENT_MOUSE_UP.equals( eventName ) ) {
handleNotifyMouseUp( control, properties );
} else if( EVENT_TRAVERSE.equals( eventName ) ) {
handleNotifyTraverse( control, properties );
} else if( EVENT_KEY_DOWN.equals( eventName ) ) {
handleNotifyKeyDown( control, properties );
} else if( EVENT_MENU_DETECT.equals( eventName ) ) {
handleNotifyMenuDetect( control, properties );
} else if( EVENT_HELP.equals( eventName ) ) {
handleNotifyHelp( control, properties );
} else if( EVENT_ACTIVATE.equals( eventName ) ) {
handleNotifyActivate( control, properties );
} else if( EVENT_DEACTIVATE.equals( eventName ) ) {
handleNotifyDeactivate( control, properties );
} else {
super.handleNotify( control, eventName, properties );
}
}
/*
* PROTOCOL SET foreground
*
* @param foreground ([int]) the foreground color of the control as RGB array or null
*/
public void handleSetForeground( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_FOREGROUND );
if( value != null ) {
Color foreground = null;
if( !value.isNull() ) {
JsonArray arrayValue = value.asArray();
foreground = new Color( control.getDisplay(),
arrayValue.get( 0 ).asInt(),
arrayValue.get( 1 ).asInt(),
arrayValue.get( 2 ).asInt() );
}
getControlAdapter( control ).setForeground( foreground );
}
}
/*
* PROTOCOL SET background
*
* @param foreground ([int]) the background color of the control as RGB array or null
*/
public void handleSetBackground( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_BACKGROUND );
if( value != null ) {
Color background = null;
if( !value.isNull() ) {
JsonArray arrayValue = value.asArray();
background = new Color( control.getDisplay(),
arrayValue.get( 0 ).asInt(),
arrayValue.get( 1 ).asInt(),
arrayValue.get( 2 ).asInt() );
}
getControlAdapter( control ).setBackground( background );
}
}
/*
* PROTOCOL SET visibility
*
* @param visibility (boolean) true if control is visible, false otherwise
*/
public void handleSetVisibility( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_VISIBILITY );
if( value != null ) {
getControlAdapter( control ).setVisible( value.asBoolean() );
}
}
/*
* PROTOCOL SET enabled
*
* @param enabled (boolean) true if control is enabled, false otherwise
*/
public void handleSetEnabled( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_ENABLED );
if( value != null ) {
getControlAdapter( control ).setEnabled( value.asBoolean() );
}
}
/*
* PROTOCOL SET toolTip
*
* @param toolTip (String) the new toolTip text
*/
public void handleSetToolTip( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_TOOL_TIP );
if( value != null ) {
String toolTipText = value.isNull() ? null : value.asString();
getControlAdapter( control ).setToolTipText( toolTipText );
}
}
/*
* PROTOCOL SET cursor
*
* @param cursor (String) the new cursor as defined in CSS specification
*/
public void handleSetCursor( T control, JsonObject properties ) {
JsonValue value = properties.get( PROP_CURSOR );
if( value != null ) {
Cursor cursor = null;
if( !value.isNull() ) {
cursor = new Cursor( control.getDisplay(), translateCursor( value.asString() ) );
}
getControlAdapter( control ).setCursor( cursor );
}
}
/*
* PROTOCOL NOTIFY FocusIn
*/
@SuppressWarnings( "unused" )
public void handleNotifyFocusIn( T control, JsonObject properties ) {
control.notifyListeners( SWT.FocusIn, new Event() );
}
/*
* PROTOCOL NOTIFY FocusOut
*/
@SuppressWarnings( "unused" )
public void handleNotifyFocusOut( T control, JsonObject properties ) {
control.notifyListeners( SWT.FocusOut, new Event() );
}
/*
* PROTOCOL NOTIFY MouseDown
*
* @param altKey (boolean) true if the ALT key was pressed
* @param ctrlKey (boolean) true if the CTRL key was pressed
* @param shiftKey (boolean) true if the SHIFT key was pressed
* @param button (int) the number of the mouse button as in Event.button
* @param x (int) the x coordinate of the pointer
* @param y (int) the y coordinate of the pointer
* @param time (int) the time when the event occurred
*/
public void handleNotifyMouseDown( T control, JsonObject properties ) {
Event event = createMouseEvent( SWT.MouseDown, control, properties );
if( allowMouseEvent( control, event.x, event.y ) ) {
control.notifyListeners( event.type, event );
}
}
/*
* PROTOCOL NOTIFY MouseDoubleClick
*
* @param altKey (boolean) true if the ALT key was pressed
* @param ctrlKey (boolean) true if the CTRL key was pressed
* @param shiftKey (boolean) true if the SHIFT key was pressed
* @param button (int) the number of the mouse button as in Event.button
* @param x (int) the x coordinate of the pointer
* @param y (int) the y coordinate of the pointer
* @param time (int) the time when the event occurred
*/
public void handleNotifyMouseDoubleClick( T control, JsonObject properties ) {
Event event = createMouseEvent( SWT.MouseDoubleClick, control, properties );
if( allowMouseEvent( control, event.x, event.y ) ) {
control.notifyListeners( event.type, event );
}
}
/*
* PROTOCOL NOTIFY MouseUp
*
* @param altKey (boolean) true if the ALT key was pressed
* @param ctrlKey (boolean) true if the CTRL key was pressed
* @param shiftKey (boolean) true if the SHIFT key was pressed
* @param button (int) the number of the mouse button as in Event.button
* @param x (int) the x coordinate of the pointer
* @param y (int) the y coordinate of the pointer
* @param time (int) the time when the event occurred
*/
public void handleNotifyMouseUp( T control, JsonObject properties ) {
Event event = createMouseEvent( SWT.MouseUp, control, properties );
if( allowMouseEvent( control, event.x, event.y ) ) {
control.notifyListeners( event.type, event );
}
}
/*
* PROTOCOL NOTIFY Traverse
*
* @param altKey (boolean) true if the ALT key was pressed
* @param ctrlKey (boolean) true if the CTRL key was pressed
* @param shiftKey (boolean) true if the SHIFT key was pressed
* @param keyCode (int) the key code of the key that was typed
* @param charCode (int) the char code of the key that was typed
*/
public void handleNotifyTraverse( T control, JsonObject properties ) {
processTraverseEvent( control, properties );
}
/*
* PROTOCOL NOTIFY KeyDown
*
* @param altKey (boolean) true if the ALT key was pressed
* @param ctrlKey (boolean) true if the CTRL key was pressed
* @param shiftKey (boolean) true if the SHIFT key was pressed
* @param keyCode (int) the key code of the key that was typed
* @param charCode (int) the char code of the key that was typed
*/
public void handleNotifyKeyDown( T control, JsonObject properties ) {
control.notifyListeners( SWT.KeyDown, createKeyEvent( properties ) );
control.notifyListeners( SWT.KeyUp, createKeyEvent( properties ) );
}
/*
* PROTOCOL NOTIFY MenuDetect
*
* @param x (int) the x coordinate of the pointer
* @param y (int) the y coordinate of the pointer
*/
public void handleNotifyMenuDetect( T control, JsonObject properties ) {
control.notifyListeners( SWT.MenuDetect, createMenuDetectEvent( properties ) );
}
/*
* PROTOCOL NOTIFY Help
*/
@SuppressWarnings( "unused" )
public void handleNotifyHelp( T control, JsonObject properties ) {
control.notifyListeners( SWT.Help, new Event() );
}
/*
* PROTOCOL NOTIFY Activate
*
* ignored, Activate event is fired when set activeControl
*/
@SuppressWarnings( "unused" )
public void handleNotifyActivate( T control, JsonObject properties ) {
}
/*
* PROTOCOL NOTIFY Deactivate
*
* ignored, Deactivate event is fired when set activeControl
*/
@SuppressWarnings( "unused" )
public void handleNotifyDeactivate( T control, JsonObject properties ) {
}
static Event createMouseEvent( int eventType, Control control, JsonObject properties ) {
Event event = new Event();
event.type = eventType;
event.widget = control;
event.button = properties.get( EVENT_PARAM_BUTTON ).asInt();
int x = properties.get( EVENT_PARAM_X ).asInt();
int y = properties.get( EVENT_PARAM_Y ).asInt();
Point point = control.getDisplay().map( null, control, x, y );
event.x = point.x;
event.y = point.y;
event.time = properties.get( EVENT_PARAM_TIME ).asInt();
event.stateMask = readStateMask( properties ) | translateButton( event.button );
// TODO: send count by the client
event.count = determineCount( eventType, control );
return event;
}
protected boolean allowMouseEvent( T control, int x, int y ) {
Point size = control.getSize();
int borderWidth = control.getBorderWidth();
Rectangle outerBounds = new Rectangle( - borderWidth, - borderWidth, size.x, size.y );
Rectangle innerBounds = new Rectangle( 0, 0, size.x - 2 * borderWidth, size.y - 2 * borderWidth );
return !outerBounds.contains( x, y ) || innerBounds.contains( x, y );
}
private static int determineCount( int eventType, Control control ) {
if( eventType == SWT.MouseDoubleClick
|| wasEventSent( getId( control ), EVENT_MOUSE_DOUBLE_CLICK ) )
{
return 2;
}
return 1;
}
private static void processTraverseEvent( Control control, JsonObject properties ) {
int keyCode = properties.get( EVENT_PARAM_KEY_CODE ).asInt();
int charCode = properties.get( EVENT_PARAM_CHAR_CODE ).asInt();
int stateMask = readStateMask( properties );
int traverseKey = getTraverseKey( keyCode, stateMask );
if( traverseKey != SWT.TRAVERSE_NONE ) {
Event event = createKeyEvent( keyCode, charCode, stateMask );
event.detail = traverseKey;
control.notifyListeners( SWT.Traverse, event );
}
}
static Event createKeyEvent( JsonObject properties ) {
int keyCode = properties.get( EVENT_PARAM_KEY_CODE ).asInt();
int charCode = properties.get( EVENT_PARAM_CHAR_CODE ).asInt();
int stateMask = readStateMask( properties );
return createKeyEvent( keyCode, charCode, stateMask );
}
static Event createMenuDetectEvent( JsonObject properties ) {
Event event = new Event();
event.x = properties.get( EVENT_PARAM_X ).asInt();
event.y = properties.get( EVENT_PARAM_Y ).asInt();
return event;
}
static Event createKeyEvent( int keyCode, int charCode, int stateMask ) {
Event event = new Event();
event.keyCode = translateKeyCode( keyCode );
if( charCode == 0 ) {
if( ( event.keyCode & SWT.KEYCODE_BIT ) == 0 ) {
event.character = translateCharacter( event.keyCode );
}
} else {
event.character = translateCharacter( charCode );
if( Character.isLetter( charCode ) ) {
// NOTE : keycodes from browser are the upper-case character, in SWT it is the lower-case
event.keyCode = Character.toLowerCase( charCode );
}
}
event.stateMask = stateMask;
return event;
}
static int getTraverseKey( int keyCode, int stateMask ) {
switch( keyCode ) {
case 27:
return SWT.TRAVERSE_ESCAPE;
case 13:
return SWT.TRAVERSE_RETURN;
case 9:
if( ( stateMask & SWT.MODIFIER_MASK ) == 0 ) {
return SWT.TRAVERSE_TAB_NEXT;
}
if( stateMask == SWT.SHIFT ) {
return SWT.TRAVERSE_TAB_PREVIOUS;
}
return SWT.TRAVERSE_NONE;
default:
return SWT.TRAVERSE_NONE;
}
}
static int translateKeyCode( int keyCode ) {
switch( keyCode ) {
case 16:
return SWT.SHIFT;
case 17:
return SWT.CONTROL;
case 18:
return SWT.ALT;
case 20:
return SWT.CAPS_LOCK;
case 38:
return SWT.ARROW_UP;
case 37:
return SWT.ARROW_LEFT;
case 39:
return SWT.ARROW_RIGHT;
case 40:
return SWT.ARROW_DOWN;
case 33:
return SWT.PAGE_UP;
case 34:
return SWT.PAGE_DOWN;
case 35:
return SWT.END;
case 36:
return SWT.HOME;
case 45:
return SWT.INSERT;
case 46:
return SWT.DEL;
case 112:
return SWT.F1;
case 113:
return SWT.F2;
case 114:
return SWT.F3;
case 115:
return SWT.F4;
case 116:
return SWT.F5;
case 117:
return SWT.F6;
case 118:
return SWT.F7;
case 119:
return SWT.F8;
case 120:
return SWT.F9;
case 121:
return SWT.F10;
case 122:
return SWT.F11;
case 123:
return SWT.F12;
case 144:
return SWT.NUM_LOCK;
case 44:
return SWT.PRINT_SCREEN;
case 145:
return SWT.SCROLL_LOCK;
case 19:
return SWT.PAUSE;
default:
return keyCode;
}
}
private static char translateCharacter( int keyCode ) {
char result = ( char )0;
if( Character.isDefined( ( char )keyCode ) ) {
result = ( char )keyCode;
}
return result;
}
private static int translateCursor( String cursor ) {
int result;
if( "default".equals( cursor ) ) {
result = SWT.CURSOR_ARROW;
} else if( "wait".equals( cursor ) ) {
result = SWT.CURSOR_WAIT;
} else if( "progress".equals( cursor ) ) {
result = SWT.CURSOR_APPSTARTING;
} else if( "crosshair".equals( cursor ) ) {
result = SWT.CURSOR_CROSS;
} else if( "help".equals( cursor ) ) {
result = SWT.CURSOR_HELP;
} else if( "move".equals( cursor ) ) {
result = SWT.CURSOR_SIZEALL;
} else if( "row-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZENS;
} else if( "col-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZEWE;
} else if( "n-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZEN;
} else if( "s-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZES;
} else if( "e-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZEE;
} else if( "w-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZEW;
} else if( "ne-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZENE;
} else if( "se-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZESE;
} else if( "sw-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZESW;
} else if( "nw-resize".equals( cursor ) ) {
result = SWT.CURSOR_SIZENW;
} else if( "text".equals( cursor ) ) {
result = SWT.CURSOR_IBEAM;
} else if( "pointer".equals( cursor ) ) {
result = SWT.CURSOR_HAND;
} else if( "not-allowed".equals( cursor ) ) {
result = SWT.CURSOR_NO;
} else {
throw new IllegalArgumentException( "Unsupported cursor: " + cursor );
}
return result;
}
}