Merge branch 'Text' into features/protocol-event-renaming
diff --git a/bundles/org.eclipse.rap.rwt/js/rwt/protocol/adapter/TextAdapter.js b/bundles/org.eclipse.rap.rwt/js/rwt/protocol/adapter/TextAdapter.js
index a1c3da0..89c87e3 100644
--- a/bundles/org.eclipse.rap.rwt/js/rwt/protocol/adapter/TextAdapter.js
+++ b/bundles/org.eclipse.rap.rwt/js/rwt/protocol/adapter/TextAdapter.js
@@ -63,8 +63,7 @@
 
   listeners : rwt.protocol.AdapterUtil.extendControlListeners( [
     "DefaultSelection",
-    "modify",
-    "verify"
+    "Modify"
   ] ),
 
   listenerHandler : rwt.protocol.AdapterUtil.extendControlListenerHandler( {} ),
diff --git a/bundles/org.eclipse.rap.rwt/js/rwt/remote/Server.js b/bundles/org.eclipse.rap.rwt/js/rwt/remote/Server.js
index 64475fb..5e51973 100644
--- a/bundles/org.eclipse.rap.rwt/js/rwt/remote/Server.js
+++ b/bundles/org.eclipse.rap.rwt/js/rwt/remote/Server.js
@@ -35,9 +35,15 @@
     this._sendTimer.addEventListener( "interval", function() {
       this.sendImmediate( true );
      }, this );
+    this._delayTimer = new Timer();
+    this._delayTimer.addEventListener( "interval", function() {
+      this._delayTimer.stop();
+      this.send();
+    }, this );
     this._waitHintTimer = new Timer( 500 );
     this._waitHintTimer.addEventListener( "interval", this._showWaitHint, this );
     this._retryHandler = null;
+    this._sendListeners = [];
   },
 
   destruct : function() {
@@ -106,6 +112,11 @@
         this._event = null;
       }
     },
+    
+    sendDelayed : function( time ) {
+      this._delayTimer.setInterval( time );
+      this._delayTimer.start();
+    },
 
     /**
      * Sends an asynchronous request within 60 milliseconds
@@ -140,6 +151,7 @@
         this._writer = null;
         this._waitHintTimer.start();
         request.send();
+        this._removeSendListeners();
       }
     },
 
@@ -154,6 +166,19 @@
       return rwt.protocol.ServerObjectFactory.getServerObject( target );
     },
 
+    onNextSend : function( func, context ) {
+      this._sendListeners.push( [ func, context ] );
+      this.addEventListener( "send", func, context );
+    },
+
+    _removeSendListeners : function() {
+      for( var i = 0; i < this._sendListeners.length; i++ ) {
+        var item = this._sendListeners[ i ];
+        this.removeEventListener( "send", item[ 0 ], item[ 1 ] );
+      }
+      this._sendListeners = [];
+    },
+
     ////////////
     // Internals
 
diff --git a/bundles/org.eclipse.rap.rwt/js/rwt/widgets/Text.js b/bundles/org.eclipse.rap.rwt/js/rwt/widgets/Text.js
index 83abeba..46e7081 100644
--- a/bundles/org.eclipse.rap.rwt/js/rwt/widgets/Text.js
+++ b/bundles/org.eclipse.rap.rwt/js/rwt/widgets/Text.js
@@ -9,6 +9,8 @@
  *    EclipseSource - initial API and implementation
  ******************************************************************************/
 
+(function(){
+
 qx.Class.define( "rwt.widgets.Text", {
 
   extend : rwt.widgets.base.BasicText,
@@ -25,8 +27,7 @@
     }
     this._hasDefaultSelectionListener = false;
     this._hasModifyListener = false;
-    this._hasVerifyListener = false;
-    this._requestScheduled = false;
+    this._modifyScheduled = false;
     this._message = null;
     this._messageElement = null;
     this._searchIconElement = null;
@@ -105,14 +106,6 @@
       return this._hasModifyListener;
     },
 
-    setHasVerifyListener : function( value ) {
-      this._hasVerifyListener = value;
-    },
-
-    hasVerifyListener : function() {
-      return this._hasVerifyListener;
-    },
-
     ////////////////
     // event handler
 
@@ -166,34 +159,21 @@
     },
 
     _handleModification : function() {
-      if( !this._requestScheduled ) {
-        this._requestScheduled = true;
-        var req = rwt.remote.Server.getInstance();
-        req.addEventListener( "send", this._onSend, this );
-        if( this.hasModifyListener() || this.hasVerifyListener() ) {
-          var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
-          var id = widgetManager.findIdByWidget( this );
-          req.addEvent( "org.eclipse.swt.events.modifyText", id );
-          rwt.client.Timer.once( this._delayedSend, this, 500 );
-        }
+      var server = rwt.remote.Server.getInstance();
+      if( !this._modifyScheduled && this.hasModifyListener() ) {
+        this._modifyScheduled = true;
+        server.sendDelayed( 500 );
+        server.onNextSend( this._onSend, this );
       }
-    },
-
-    _delayedSend : function( event ) {
-      if( this._requestScheduled ) {
-        var req = rwt.remote.Server.getInstance();
-        req.send();
-      }
-    },
-
-    _onSend : function( event ) {
-      var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
-      var id = widgetManager.findIdByWidget( this );
-      var req = rwt.remote.Server.getInstance();
-      req.addParameter( id + ".text", this.getComputedValue() );
+      server.getServerObject( this ).set( "text", this.getComputedValue() );
       this._detectSelectionChange();
-      req.removeEventListener( "send", this._onSend, this );
-      this._requestScheduled = false;
+    },
+
+    _onSend : function() {
+      if( this._modifyScheduled ) {
+        rwt.remote.Server.getInstance().getServerObject( this ).notify( "Modify", null, true );
+        this._modifyScheduled = false;
+      }
     },
 
     /*
@@ -571,3 +551,6 @@
   }
 
 } );
+
+}());
+
diff --git a/bundles/org.eclipse.rap.rwt/widgetkits/org/eclipse/swt/internal/widgets/textkit/TextLCAUtil.java b/bundles/org.eclipse.rap.rwt/widgetkits/org/eclipse/swt/internal/widgets/textkit/TextLCAUtil.java
index 6a15793..1927b25 100644
--- a/bundles/org.eclipse.rap.rwt/widgetkits/org/eclipse/swt/internal/widgets/textkit/TextLCAUtil.java
+++ b/bundles/org.eclipse.rap.rwt/widgetkits/org/eclipse/swt/internal/widgets/textkit/TextLCAUtil.java
@@ -64,8 +64,7 @@
   static final String PROP_EDITABLE = "editable";
   static final String PROP_ECHO_CHAR = "echoChar";
   static final String PROP_MESSAGE = "message";
-  static final String PROP_MODIFY_LISTENER = "modify";
-  static final String PROP_VERIFY_LISTENER = "verify";
+  static final String PROP_MODIFY_LISTENER = "Modify";
   static final String PROP_DEFAULT_SELECTION_LISTENER = "DefaultSelection";
 
   private static final Point ZERO_SELECTION = new Point( 0, 0 );
@@ -83,8 +82,7 @@
     preserveProperty( text, PROP_EDITABLE, text.getEditable() );
     preserveProperty( text, PROP_ECHO_CHAR, getEchoChar( text ) );
     preserveProperty( text, PROP_MESSAGE, text.getMessage() );
-    preserveListener( text, PROP_MODIFY_LISTENER, text.isListening( SWT.Modify ) );
-    preserveListener( text, PROP_VERIFY_LISTENER, text.isListening( SWT.Verify ) );
+    preserveListener( text, PROP_MODIFY_LISTENER, hasModifyListener( text ) );
     preserveListener( text,
                       PROP_DEFAULT_SELECTION_LISTENER,
                       text.isListening( SWT.DefaultSelection ) );
@@ -106,8 +104,7 @@
     renderProperty( text, PROP_TEXT_LIMIT, getTextLimit( text ), null );
     renderProperty( text, PROP_ECHO_CHAR, getEchoChar( text ), null );
     renderProperty( text, PROP_MESSAGE, text.getMessage(), "" );
-    renderListener( text, PROP_MODIFY_LISTENER, text.isListening( SWT.Modify ), false );
-    renderListener( text, PROP_VERIFY_LISTENER, text.isListening( SWT.Verify ), false );
+    renderListener( text, PROP_MODIFY_LISTENER, hasModifyListener( text ), false );
     renderListener( text,
                     PROP_DEFAULT_SELECTION_LISTENER,
                     text.isListening( SWT.DefaultSelection ),
@@ -196,4 +193,10 @@
   private static String getEchoChar( Text text ) {
     return text.getEchoChar() == 0 ? null : String.valueOf( text.getEchoChar() );
   }
+
+  private static boolean hasModifyListener( Text text ) {
+    // NOTE : Client does not support Verify, it is created server-side from Modify
+    return text.isListening( SWT.Modify ) || text.isListening( SWT.Verify );
+  }
+
 }
diff --git a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/Startup.js b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/Startup.js
index c708f7e..3ef465a 100644
--- a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/Startup.js
+++ b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/Startup.js
@@ -12,7 +12,8 @@
   org.eclipse.rwt.KeyEventSupport.getInstance()._sendRequestAsync = function() {
     rwt.remote.Server.getInstance().sendImmediate( true );
   };
-  rwt.remote.Server.getInstance().send = function() {
+  var server = rwt.remote.Server.getInstance();
+  server.send = function() {
     if( !this._sendTimer.isEnabled() ) {
       this._sendTimer.start();
       if( this._requestCounter === -1 ) {
@@ -22,6 +23,12 @@
       this.sendImmediate( true ); // omit timer
     }
   };
+  server._delayTimer = new rwt.client.Timer();
+  server._delayTimer.addEventListener( "interval", function() {
+    this._delayTimer.stop();
+    this.send();
+  }, server );
+
   rwt.protocol.MessageProcessor.processMessage( {
     "head": {},
     "operations": [
diff --git a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/fixture/TestUtil.js b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/fixture/TestUtil.js
index d24f989..ef81bc2 100644
--- a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/fixture/TestUtil.js
+++ b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/fixture/TestUtil.js
@@ -974,6 +974,19 @@
 
   clearXMLHttpRequests : function() {
     org.eclipse.rwt.test.fixture.NativeRequestMock.history = [];
+  },
+
+  getLogger : function() {
+    var result = {
+      _log : [],
+      log : function( item ) {
+        this._log.push( item );
+      },
+      getLog : function() {
+        return this._log;
+      }
+    };
+    return result;
   }
 
 };
\ No newline at end of file
diff --git a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/ServerTest.js b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/ServerTest.js
index a6f678f..edec76e 100644
--- a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/ServerTest.js
+++ b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/ServerTest.js
@@ -142,6 +142,61 @@
       TestUtil.forceInterval( fakeServer._timer );
       assertEquals( 1, TestUtil.getRequestsSend() );
       org.eclipse.rwt.test.fixture.FakeServer.getInstance().setUseAsync( false );
+    },
+
+    testOnNextSend : function() {
+      var logger = TestUtil.getLogger();
+      server.onNextSend( logger.log, logger );
+
+      server.send();
+
+      assertEquals( 1, logger.getLog().length );
+    },
+
+    testOnNextSendAttachTwice : function() {
+      var logger = TestUtil.getLogger();
+      server.onNextSend( logger.log, logger );
+      server.onNextSend( logger.log, logger );
+
+      server.send();
+
+      assertEquals( 1, logger.getLog().length );
+    },
+
+    testOnNextSendSendTwice : function() {
+      var logger = TestUtil.getLogger();
+      server.onNextSend( logger.log, logger );
+
+      server.send();
+      server.send();
+
+      assertEquals( 1, logger.getLog().length );
+    },
+
+    testDelayedSend : function() {
+      var logger = TestUtil.getLogger();
+      server.addEventListener( "send", logger.log, logger );
+      server.sendDelayed( 500 );
+
+      assertEquals( 500, server._delayTimer.getInterval() );
+      TestUtil.forceInterval( server._delayTimer );
+
+      assertEquals( 1, logger.getLog().length );
+    },
+
+    testDelayedSendAborted : function() {
+      var logger = TestUtil.getLogger();
+      server.addEventListener( "send", logger.log, logger );
+
+      server.sendDelayed( 500 );
+      server.send();
+      try {
+        TestUtil.forceInterval( server._delayTimer );
+      } catch( ex ) {
+        // expected
+      }
+
+      assertEquals( 1, logger.getLog().length );
     }
 
   }
diff --git a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TestUtilTest.js b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TestUtilTest.js
index 912a57c..521702f 100644
--- a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TestUtilTest.js
+++ b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TestUtilTest.js
@@ -754,6 +754,16 @@
       assertIdentical( requestTwo, TestUtil.getXMLHttpRequests()[ 1 ] );
     },
 
+    testLogger : function() {
+      var logger = TestUtil.getLogger();
+
+      logger.log( 1 );
+      logger.log( 2 );
+      logger.log( 3 );
+
+      assertEquals( [ 1, 2, 3 ], logger.getLog() );
+    },
+
     /////////
     // helper
 
diff --git a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TextTest.js b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TextTest.js
index 1d807bc..d15376f 100644
--- a/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TextTest.js
+++ b/tests/org.eclipse.rap.rwt.jstest/js/org/eclipse/rwt/test/tests/TextTest.js
@@ -352,26 +352,11 @@
           "parent" : "w2"
         }
       } );
-      TestUtil.protocolListen( "w3", { "modify" : true } );
+      TestUtil.protocolListen( "w3", { "Modify" : true } );
       text = ObjectManager.getObject( "w3" );
       assertTrue( text.hasModifyListener() );
     },
 
-    testSetHasVerifyListenerByProtocol : function() {
-      Processor.processOperation( {
-        "target" : "w3",
-        "action" : "create",
-        "type" : "rwt.widgets.Text",
-        "properties" : {
-          "style" : [ "SINGLE" ],
-          "parent" : "w2"
-        }
-      } );
-      TestUtil.protocolListen( "w3", { "verify" : true } );
-      text = ObjectManager.getObject( "w3" );
-      assertTrue( text.hasVerifyListener() );
-    },
-
     testSetTextByProtocol : function() {
       Processor.processOperation( {
         "target" : "w3",
@@ -801,30 +786,24 @@
       text.setHasModifyListener( true );
 
       text.setValue( "foobar" );
-      TestUtil.forceTimerOnce();
+      TestUtil.forceInterval( Server.getInstance()._delayTimer );
 
-      assertNotNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "modifyText" ) );
-    },
-
-    testSendTextModifyEventWithVerifyListener : function() {
-      createText();
-      text.setHasVerifyListener( true );
-
-      text.setValue( "foobar" );
-      TestUtil.forceTimerOnce();
-
-      assertNotNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "modifyText" ) );
+      assertNotNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "Modify" ) );
     },
 
     testSendNoModifyEvent : function() {
       createText();
 
       text.setValue( "foobar" );
-      TestUtil.forceTimerOnce();
+      try {
+        TestUtil.forceInterval( Server.getInstance()._delayTimer );
+      } catch( ex ) {
+        // expected
+      }
 
       assertEquals( 0, TestUtil.getRequestsSend() );
       Server.getInstance().send();
-      assertNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "modifyText" ) );
+      assertNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "Modify" ) );
     },
 
     testDontSendTextModifyEventTwice : function() {
@@ -833,10 +812,10 @@
 
       text.setValue( "foobar" );
       text.setValue( "barfoo" );
-      TestUtil.forceTimerOnce();
+      TestUtil.forceInterval( Server.getInstance()._delayTimer );
 
       assertEquals( 1, TestUtil.getRequestsSend() );
-      assertNotNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "modifyText" ) );
+      assertNotNull( TestUtil.getMessageObject().findNotifyOperation( "w3", "Modify" ) );
       assertEquals( "barfoo", TestUtil.getMessageObject().findSetProperty( "w3", "text" ) );
     },
 
diff --git a/tests/org.eclipse.rap.rwt.test/src/org/eclipse/swt/internal/widgets/textkit/TextLCA_Test.java b/tests/org.eclipse.rap.rwt.test/src/org/eclipse/swt/internal/widgets/textkit/TextLCA_Test.java
index 431cddd..8815347 100644
--- a/tests/org.eclipse.rap.rwt.test/src/org/eclipse/swt/internal/widgets/textkit/TextLCA_Test.java
+++ b/tests/org.eclipse.rap.rwt.test/src/org/eclipse/swt/internal/widgets/textkit/TextLCA_Test.java
@@ -325,12 +325,12 @@
 
   public void testWriteModifyListenerWhenReadOnly() throws IOException {
     Text text = new Text( shell, SWT.READ_ONLY );
-    text.addModifyListener( createModifyListener() );
+    text.addListener( SWT.Modify, mock( Listener.class ) );
 
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertEquals( Boolean.TRUE, message.findListenProperty( text, "modify" ) );
+    assertEquals( Boolean.TRUE, message.findListenProperty( text, "Modify" ) );
   }
 
   public void testRenderCreate() throws IOException {
@@ -656,31 +656,25 @@
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.addModifyListener( new ModifyListener() {
-      public void modifyText( ModifyEvent event ) {
-      }
-    } );
+    text.addListener( SWT.Modify, mock( Listener.class ) );
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertEquals( Boolean.TRUE, message.findListenProperty( text, "modify" ) );
+    assertEquals( Boolean.TRUE, message.findListenProperty( text, "Modify" ) );
   }
 
   public void testRenderRemoveModifyListener() throws Exception {
-    ModifyListener listener = new ModifyListener() {
-      public void modifyText( ModifyEvent event ) {
-      }
-    };
-    text.addModifyListener( listener );
+    Listener listener = mock( Listener.class );
+    text.addListener( SWT.Modify, listener );
     Fixture.markInitialized( display );
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.removeModifyListener( listener );
+    text.removeListener( SWT.Modify, listener );
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertEquals( Boolean.FALSE, message.findListenProperty( text, "modify" ) );
+    assertEquals( Boolean.FALSE, message.findListenProperty( text, "Modify" ) );
   }
 
   public void testRenderModifyListenerUnchanged() throws Exception {
@@ -688,15 +682,12 @@
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.addModifyListener( new ModifyListener() {
-      public void modifyText( ModifyEvent event ) {
-      }
-    } );
+    text.addListener( SWT.Modify, mock( Listener.class ) );
     Fixture.preserveWidgets();
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertNull( message.findListenOperation( text, "modify" ) );
+    assertNull( message.findListenOperation( text, "Modify" ) );
   }
 
   public void testRenderAddVerifyListener() throws Exception {
@@ -704,31 +695,25 @@
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.addVerifyListener( new VerifyListener() {
-      public void verifyText( VerifyEvent event ) {
-      }
-    } );
+    text.addListener( SWT.Verify, mock( Listener.class ) );
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertEquals( Boolean.TRUE, message.findListenProperty( text, "verify" ) );
+    assertEquals( Boolean.TRUE, message.findListenProperty( text, "Modify" ) );
   }
 
-  public void testRenderRemoveVerifyListener() throws Exception {
-    VerifyListener listener = new VerifyListener() {
-      public void verifyText( VerifyEvent event ) {
-      }
-    };
-    text.addVerifyListener( listener );
+  public void testRenderVerifyModifyListener() throws Exception {
+    Listener listener = mock( Listener.class );
+    text.addListener( SWT.Verify, listener );
     Fixture.markInitialized( display );
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.removeVerifyListener( listener );
+    text.removeListener( SWT.Verify, listener );
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertEquals( Boolean.FALSE, message.findListenProperty( text, "verify" ) );
+    assertEquals( Boolean.FALSE, message.findListenProperty( text, "Modify" ) );
   }
 
   public void testRenderVerifyListenerUnchanged() throws Exception {
@@ -736,15 +721,12 @@
     Fixture.markInitialized( text );
     Fixture.preserveWidgets();
 
-    text.addVerifyListener( new VerifyListener() {
-      public void verifyText( VerifyEvent event ) {
-      }
-    } );
+    text.addListener( SWT.Verify, mock( Listener.class ) );
     Fixture.preserveWidgets();
     lca.renderChanges( text );
 
     Message message = Fixture.getProtocolMessage();
-    assertNull( message.findListenOperation( text, "verify" ) );
+    assertNull( message.findListenOperation( text, "Modify" ) );
   }
 
   public void testRenderInitialText() throws IOException {