Implement forceFocus on widget proxy for controls
diff --git a/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/CustomBehaviors.java b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/CustomBehaviors.java
index 33adb24..477787b 100644
--- a/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/CustomBehaviors.java
+++ b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/CustomBehaviors.java
@@ -11,6 +11,8 @@
 package org.eclipse.rap.clientscripting.demo;
 
 import org.eclipse.rap.clientscripting.ClientListener;
+import org.eclipse.rap.clientscripting.WidgetDataWhiteList;
+import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
@@ -93,6 +95,25 @@
     canvas.redraw();
   }
 
+
+  public static void addFocusNextBehavior( Text text, Control next ) {
+    text.setTextLimit( 4 );
+    String scriptCode = ResourceLoaderUtil.readTextContent( RESOURCES_PREFIX + "FocusSwitch.js" );
+    ClientListener listener = new ClientListener( scriptCode );
+    text.addListener( SWT.Modify, listener );
+    WidgetDataWhiteList.addKey( "next" );
+    text.setData( "next", WidgetUtil.getId( next ) );
+  }
+
+  public static void addFocusPreviousBehavior( Text text, Control previous ) {
+    text.setTextLimit( 4 );
+    String scriptCode = ResourceLoaderUtil.readTextContent( RESOURCES_PREFIX + "FocusSwitch.js" );
+    ClientListener listener = new ClientListener( scriptCode );
+    text.addListener( SWT.Modify, listener );
+    WidgetDataWhiteList.addKey( "previous" );
+    text.setData( "previous", WidgetUtil.getId( previous ) );
+  }
+
   public static Color getRandomColor( Device device ) {
     RGB rgb = new RGB(
       ( int )Math.round(  Math.random() * 255 ),
diff --git a/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/Demo.java b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/Demo.java
index f5b0641..4596373 100644
--- a/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/Demo.java
+++ b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/Demo.java
@@ -41,6 +41,7 @@
     addUpperCaseExample( left );
     addDigitsOnlyExample( left );
     addDateFieldExample( left );
+    addFocusSwitchExample( left );
     addCounterExample( left );
     addCanvasExample( left );
     addListExample( left );
@@ -57,6 +58,22 @@
     text.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
   }
 
+  private void addFocusSwitchExample( Composite parent ) {
+    addHeaderLabel( parent, "Switch focus while typing:" );
+    Composite area = new Composite( parent, SWT.NONE );
+    area.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
+    GridLayout layout = new GridLayout( 2, false );
+    layout.marginHeight = 0;
+    layout.marginWidth = 0;
+    area.setLayout( layout );
+    Text text = new Text( area, SWT.BORDER );
+    text.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
+    Text next = new Text( area, SWT.BORDER );
+    next.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, true, false ) );
+    CustomBehaviors.addFocusNextBehavior( text, next );
+    CustomBehaviors.addFocusPreviousBehavior( next, text );
+  }
+
   private void addDigitsOnlyExample( Composite parent ) {
     addHeaderLabel( parent, "Digits only, validation on client:" );
     Text text = new Text( parent, SWT.BORDER );
diff --git a/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/FocusSwitch.js b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/FocusSwitch.js
new file mode 100644
index 0000000..4cf4ff9
--- /dev/null
+++ b/bundles/org.eclipse.rap.clientscripting.demo/src/org/eclipse/rap/clientscripting/demo/FocusSwitch.js
@@ -0,0 +1,9 @@
+var handleEvent = function( event ) {
+  var text = event.widget;
+  var str = text.getText();
+  if( str.length === 4 && text.getData( "next" ) ) {
+    rap.getObject( text.getData( "next" ) ).forceFocus();
+  } else if( str.length === 0 && text.getData( "previous" ) ) {
+    rap.getObject( text.getData( "previous" ) ).forceFocus();
+  }
+};
diff --git a/bundles/org.eclipse.rap.clientscripting/js/org/eclipse/rap/clientscripting/ClientScriptingUtil.js b/bundles/org.eclipse.rap.clientscripting/js/org/eclipse/rap/clientscripting/ClientScriptingUtil.js
index 5b562f9..2fdeb23 100644
--- a/bundles/org.eclipse.rap.clientscripting/js/org/eclipse/rap/clientscripting/ClientScriptingUtil.js
+++ b/bundles/org.eclipse.rap.clientscripting/js/org/eclipse/rap/clientscripting/ClientScriptingUtil.js
@@ -153,8 +153,18 @@
   },
 
   attachControlMethods : function( proxy, source ) {
+    var id = ObjectRegistry.getId( source );
+    var ClientScriptingUtil = org.eclipse.rap.clientscripting.ClientScriptingUtil;
     proxy.redraw = function() {
-      org.eclipse.rap.clientscripting.ClientScriptingUtil._initGC( source );
+      ClientScriptingUtil._initGC( source );
+    };
+    proxy.forceFocus = function() {
+      var result = false;
+      if( source.getEnabled() && ClientScriptingUtil._isVisible( source ) ) {
+        rwt.widgets.Display.getCurrent().setFocusControl( id );
+        result = true;
+      }
+      return result;
     };
   },
 
@@ -331,6 +341,16 @@
     return result;
   },
 
+  _isVisible : function( widget ) {
+    var result = true;
+    var current = widget;
+    while( current && result ) {
+      result = current.getVisibility();
+      current = current.getParent();
+    }
+    return result;
+  },
+
   _initVerifyEvent : function( event, originalEvent ) {
     var text = originalEvent.getTarget();
     if( text.classname === "rwt.widgets.Text" ) {
diff --git a/tests/org.eclipse.rap.clientscripting.jstest/js/org/eclipse/rap/clientscripting/WidgetProxy_Test.js b/tests/org.eclipse.rap.clientscripting.jstest/js/org/eclipse/rap/clientscripting/WidgetProxy_Test.js
index 7101903..acc8b37 100644
--- a/tests/org.eclipse.rap.clientscripting.jstest/js/org/eclipse/rap/clientscripting/WidgetProxy_Test.js
+++ b/tests/org.eclipse.rap.clientscripting.jstest/js/org/eclipse/rap/clientscripting/WidgetProxy_Test.js
@@ -153,6 +153,50 @@
       assertEquals( [ 1, 2 ], value );
     },
 
+    testTextForceFocus : function() {
+      var widgetProxy = WidgetProxy.getInstance( text );
+      text.blur();
+
+      var value = widgetProxy.forceFocus();
+
+      assertTrue( text.isFocused() );
+      assertTrue( value );
+    },
+
+    testTextForceFocus_NotVisible : function() {
+      var widgetProxy = WidgetProxy.getInstance( text );
+      text.blur();
+      text.setVisibility( false );
+
+      var value = widgetProxy.forceFocus();
+
+      assertFalse( text.isFocused() );
+      assertFalse( value );
+    },
+
+    testTextForceFocus_ParentNotVisible : function() {
+      var widgetProxy = WidgetProxy.getInstance( text );
+      text.blur();
+      text.getParent().setVisibility( false );
+      TestUtil.flush();
+
+      var value = widgetProxy.forceFocus();
+
+      assertFalse( text.isFocused() );
+      assertFalse( value );
+    },
+
+    testTextForceFocus_NotEnabled : function() {
+      var widgetProxy = WidgetProxy.getInstance( text );
+      text.blur();
+      text.setEnabled( false );
+
+      var value = widgetProxy.forceFocus();
+
+      assertFalse( text.isFocused() );
+      assertFalse( value );
+    },
+
     testListGetSelection : function() {
       Processor.processOperation( {
         "target" : "w4",