blob: f9462f5616da8ef411a52b227e6c0594e6720845 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2012 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
******************************************************************************/
qx.Class.define( "org.eclipse.rwt.KeyEventSupport", {
type : "singleton",
extend : qx.core.Object,
construct : function() {
this.base( arguments );
org.eclipse.rwt.EventHandler.setKeyDomEventFilter( this._onKeyDomEvent, this );
org.eclipse.rwt.EventHandler.setKeyEventFilter( this._onKeyEvent, this );
this._keyBindings = {};
this._cancelKeys = {};
this._currentKeyCode = -1;
this._bufferedEvents = [];
this._keyEventRequestRunning = false;
this._ignoreNextKeypress = false;
var req = rwt.remote.Server.getInstance();
req.addEventListener( "received", this._onRequestReceived, this );
},
destruct : function() {
var req = rwt.remote.Server.getInstance();
req.removeEventListener( "received", this._onRequestReceived, this );
},
members : {
//////
// API
setKeyBindings : function( value ) {
this._keyBindings = value;
},
setCancelKeys : function( value ) {
this._cancelKeys = value;
},
////////////
// Internals
_onKeyDomEvent : function( eventType, keyCode, charCode, domEvent ) {
if( eventType === "keydown" ) {
this._currentKeyCode = keyCode;
}
var control = this._getTargetControl();
if( this._shouldCancel( this._currentKeyCode, charCode, domEvent, control ) ) {
org.eclipse.rwt.EventHandlerUtil.stopDomEvent( domEvent );
domEvent._noProcess = true;
}
},
_onKeyEvent : function( eventType, keyCode, charCode, domEvent ) {
var control = this._getTargetControl();
if( this._shouldSend( eventType, this._currentKeyCode, charCode, domEvent, control ) ) {
this._sendKeyEvent( control, this._currentKeyCode, charCode, domEvent );
}
if( eventType === "keypress" || eventType === "keyup" ) {
this._ignoreNextKeypress = false;
}
return !domEvent._noProcess;
},
/////////////
// send event
_shouldSend : function( eventType, keyCode, charCode, domEvent, control ) {
var result = false;
if( this._isRelevant( keyCode, eventType, domEvent ) ) {
result = this._shouldSendTraverse( keyCode, charCode, domEvent, control )
|| this._shouldSendKeyDown( keyCode, charCode, domEvent, control );
}
return result;
},
_shouldSendTraverse : function( keyCode, charCode, domEvent, control ) {
return this._hasTraverseListener( control ) && this._isTraverseKey( keyCode );
},
_shouldSendKeyDown : function( keyCode, charCode, domEvent, control ) {
var result = false;
if( this._hasKeyListener( control ) ) {
var activeKeys = control.getUserData( "activeKeys" );
if( activeKeys ) {
result = this._isActive( activeKeys, domEvent, keyCode, charCode );
} else {
result = true;
}
}
if( !result ) {
result = this._isActive( this._keyBindings, domEvent, keyCode, charCode );
}
return result;
},
_isRelevant : function( keyCode, eventType, domEvent ) {
var result;
if( eventType === "keypress" ) {
// NOTE : modifier don't repeat
result = !this._isModifier( keyCode ) && !this._ignoreNextKeypress;
} else if( eventType === "keydown" ) {
// NOTE : Prefered when keypress might not be fired, e.g. browser shortcuts that
// are not or can not be prevented/canceled. Key might not repeat in that case.
// Not to be used when charcode might be unkown (e.g. shift + char, special char)-
var EventHandlerUtil = org.eclipse.rwt.EventHandlerUtil;
result = EventHandlerUtil.isNonPrintableKeyCode( keyCode )
|| EventHandlerUtil.isSpecialKeyCode( keyCode );
if( !result && ( domEvent.altKey || domEvent.ctrlKey ) ) {
result = this._isAlphaNumeric( keyCode );
}
if( result ) {
this._ignoreNextKeypress = true;
}
}
if( domEvent.ctrlKey && keyCode === 9 ) {
// Used by the browser to switch tabs, not useable
result = false;
}
return result;
},
_onRequestReceived : function( evt ) {
if( this._keyEventRequestRunning ) {
this._keyEventRequestRunning = false;
this._checkBufferedEvents();
}
},
_checkBufferedEvents : function() {
while( this._bufferedEvents.length > 0 && !this._keyEventRequestRunning ) {
var oldEvent = this._bufferedEvents.shift();
this._sendKeyEvent.apply( this, oldEvent );
}
},
_sendKeyEvent : function( widget, keyCode, charCode, domEvent ) {
if( this._keyEventRequestRunning ) {
this._bufferedEvents.push( [ widget, keyCode, charCode, domEvent ] );
} else {
this._attachKeyEvent( widget, keyCode, charCode, domEvent );
this._keyEventRequestRunning = true;
this._sendRequestAsync();
}
},
_sendRequestAsync : function() {
window.setTimeout( function() {
rwt.remote.Server.getInstance().sendImmediate( true );
}, 0 );
},
_attachKeyEvent : function( widget, keyCode, charCode, domEvent ) {
var server = rwt.remote.Server.getInstance();
var serverObject;
if( widget === null ) {
serverObject = server.getServerObject( rwt.widgets.Display.getCurrent() );
} else {
serverObject = server.getServerObject( widget );
}
var finalCharCode = this._getCharCode( keyCode, charCode, domEvent );
var properties = {
"keyCode" : keyCode,
"charCode" : finalCharCode
};
org.eclipse.swt.EventUtil.addModifierToProperties( properties );
if( this._shouldSendTraverse( keyCode, charCode, domEvent, widget ) ) {
serverObject.notify( "Traverse", properties, true );
}
if( this._shouldSendKeyDown( keyCode, charCode, domEvent, widget ) ) {
serverObject.notify( "KeyDown", properties, true );
}
},
///////////////
// cancel event
_shouldCancel : function( keyCode, charCode, domEvent, control ) {
var result = this._isActive( this._cancelKeys, domEvent, keyCode, charCode );
if( !result ) {
var cancelKeys = control ? control.getUserData( "cancelKeys" ) : null;
if( cancelKeys ) {
result = this._isActive( cancelKeys, domEvent, keyCode, charCode );
}
}
return result;
},
/////////
// helper
_getTargetControl : function() {
var result = org.eclipse.rwt.EventHandler.getCaptureWidget();
if( !result ) {
var focusRoot = org.eclipse.rwt.EventHandler.getFocusRoot();
result = focusRoot === null ? null : focusRoot.getActiveChild();
}
var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
while( result !== null && !widgetManager.isControl( result ) ) {
result = result.getParent ? result.getParent() : null;
}
return result;
},
_isActive : function( activeKeys, domEvent, keyCode, charCode ) {
var result = false;
var identifier = this._getKeyBindingIdentifier( domEvent, "keydown", keyCode, charCode );
result = activeKeys[ identifier ] === true;
if( !result ) {
identifier = this._getKeyBindingIdentifier( domEvent, "keypress", keyCode, charCode );
result = activeKeys[ identifier ] === true;
}
return result;
},
_getKeyBindingIdentifier : function( domEvent, eventType, keyCode, charCode ) {
var result = [];
if( eventType === "keydown" && !isNaN( keyCode ) && keyCode > 0 ) {
if( domEvent.altKey ) {
result.push( "ALT" );
}
if( domEvent.ctrlKey ) {
result.push( "CTRL" ); //TODO Command @ apple?
}
if( domEvent.shiftKey ) {
result.push( "SHIFT" );
}
result.push( "#" + keyCode.toString() );
} else if( eventType === "keypress" && !isNaN( charCode ) && charCode > 0 ) {
result.push( String.fromCharCode( charCode ) );
}
return result.join( "+" );
},
_getCharCode : function( keyCode, charCode, domEvent ) {
var result = charCode;
if( result === 0 && this._isAlphaNumeric( keyCode ) ) {
if( domEvent.shiftKey && !this._isNumeric( keyCode ) ) {
result = keyCode;
} else {
result = String.fromCharCode( keyCode ).toLowerCase().charCodeAt( 0 );
}
}
return result;
},
_isModifier : function( keyCode ) {
return keyCode >= 16 && keyCode <= 20 && keyCode !== 19;
},
_isAlphaNumeric : function( keyCode ) {
return ( keyCode >= 65 && keyCode <= 90 ) || this._isNumeric( keyCode );
},
_isNumeric : function( keyCode ) {
return keyCode >= 48 && keyCode <= 57;
},
_hasKeyListener : function( widget ) {
return widget !== null && widget.getUserData( "keyListener" ) === true;
},
_hasTraverseListener : function( widget ) {
return widget !== null && widget.getUserData( "traverseListener" ) === true;
},
_isTraverseKey : function( keyCode ) {
var result = false;
if( keyCode === 27 || keyCode === 13 || keyCode === 9 ) {
result = true;
}
return result;
}
}
} );
// force instance:
org.eclipse.rwt.KeyEventSupport.getInstance();