[Libreoffice-commits] core.git: 47 commits - android/Bootstrap android/experimental desktop/source include/LibreOfficeKit include/vcl libreofficekit/qa libreofficekit/source sc/inc sc/source sd/source sw/inc sw/source vcl/source

Siqi Liu me at siqi.fr
Sun Mar 1 23:52:51 PST 2015


 android/Bootstrap/src/org/libreoffice/kit/Document.java                                   |    8 
 android/Bootstrap/src/org/libreoffice/kit/Office.java                                     |    8 
 android/experimental/LOAndroid3/res/drawable/text_cursor.xml                              |    6 
 android/experimental/LOAndroid3/res/layout/text_selection_handles.xml                     |    5 
 android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java         |  224 ++++++++++
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java                     |   50 +-
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java                  |   34 +
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java                 |   60 ++
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java           |  159 ++-----
 android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java     |   13 
 android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java            |   23 +
 android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java        |   62 ++
 android/experimental/LOAndroid3/src/java/org/libreoffice/TextCursorLayer.java             |   12 
 android/experimental/LOAndroid3/src/java/org/libreoffice/TextCursorView.java              |  109 +++-
 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java                |   17 
 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProviderFactory.java         |    5 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java       |   40 -
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/DrawTimingQueue.java       |   95 ----
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java |    8 
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/PanningPerfAPI.java        |   93 ----
 desktop/source/lib/init.cxx                                                               |   58 +-
 desktop/source/lib/lokandroid.cxx                                                         |   14 
 include/LibreOfficeKit/LibreOfficeKit.h                                                   |    8 
 include/LibreOfficeKit/LibreOfficeKit.hxx                                                 |   26 -
 include/vcl/window.hxx                                                                    |    2 
 libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx                                       |   26 +
 libreofficekit/source/gtk/lokdocview.c                                                    |   20 
 sc/inc/docuno.hxx                                                                         |   22 
 sc/source/ui/app/inputhdl.cxx                                                             |    9 
 sc/source/ui/inc/gridwin.hxx                                                              |    3 
 sc/source/ui/inc/inputhdl.hxx                                                             |    2 
 sc/source/ui/inc/tabvwsh.hxx                                                              |    1 
 sc/source/ui/unoobj/docuno.cxx                                                            |   12 
 sc/source/ui/view/gridwin4.cxx                                                            |   44 +
 sc/source/ui/view/tabvwsh4.cxx                                                            |   95 ++--
 sd/source/ui/inc/ViewShell.hxx                                                            |    5 
 sd/source/ui/inc/unomodel.hxx                                                             |    2 
 sd/source/ui/unoidl/unomodel.cxx                                                          |   24 +
 sd/source/ui/view/sdwindow.cxx                                                            |   13 
 sd/source/ui/view/viewshel.cxx                                                            |   32 +
 sw/inc/viscrs.hxx                                                                         |   10 
 sw/source/core/crsr/viscrs.cxx                                                            |   48 +-
 sw/source/core/view/viewsh.cxx                                                            |   13 
 sw/source/uibase/docvw/edtwin.cxx                                                         |    4 
 vcl/source/window/mouse.cxx                                                               |    9 
 45 files changed, 976 insertions(+), 557 deletions(-)

New commits:
commit ef990c71e4f4eb703bc8175e26e3853b0d6486ca
Author: Siqi Liu <me at siqi.fr>
Date:   Sat Feb 28 22:07:36 2015 +0100

    remove unused boolean (propagating touchEvent instead of intercepting it)
    
    Change-Id: If05cf04414572fea809104c7fed309afb2438bfd

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
index 1dc33f6..b1ee37e 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
@@ -27,7 +27,6 @@ public class OnSlideSwipeListener implements OnTouchListener {
 
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
-            boolean result = false;
             try {
                 float diffY = e2.getY() - e1.getY();
                 float diffX = e2.getX() - e1.getX();
@@ -40,7 +39,6 @@ public class OnSlideSwipeListener implements OnTouchListener {
                             onSwipeLeft();
                         }
                     }
-                    result = true;
                 }
             } catch (Exception exception) {
                 exception.printStackTrace();
commit 75f5168c031916a8dec7d158e2b36fa983fff10f
Author: Siqi Liu <me at siqi.fr>
Date:   Sat Feb 28 21:50:12 2015 +0100

    implement swiping gesture as a generic event
    
    Change-Id: I024db339bada34a544fdcc1def107c7b06332847

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
index a046988..18b821e 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
@@ -21,6 +21,8 @@ public class LOEvent implements Comparable<LOEvent> {
     public static final int TOUCH = 8;
     public static final int KEY_EVENT = 9;
     public static final int CHANGE_HANDLE_POSITION = 10;
+    public static final int SWIPE_RIGHT = 11;
+    public static final int SWIPE_LEFT = 12;
 
     public final int mType;
     public int mPriority = 0;
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
index 68ab523..84123a5 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
@@ -102,6 +102,14 @@ public class LOKitShell {
         LOKitShell.sendEvent(new LOEvent(LOEvent.SIZE_CHANGED));
     }
 
+    public static void sendSwipeRightEvent() {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.SWIPE_RIGHT));
+    }
+
+    public static void sendSwipeLeftEvent() {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.SWIPE_LEFT));
+    }
+
     public static void sendChangePartEvent(int part) {
         LOKitShell.sendEvent(new LOEvent(LOEvent.CHANGE_PART, part));
     }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 334cd44..099d0f2 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -208,6 +208,12 @@ public class LOKitThread extends Thread {
             case LOEvent.CHANGE_HANDLE_POSITION:
                 changeHandlePosition(event.mHandleType, event.mDocumentCoordinate);
                 break;
+            case LOEvent.SWIPE_LEFT:
+                onSwipeLeft();
+                break;
+            case LOEvent.SWIPE_RIGHT:
+                onSwipeRight();
+                break;
         }
     }
 
@@ -237,6 +243,14 @@ public class LOKitThread extends Thread {
         mTileProvider.sendKeyEvent(keyEvent);
     }
 
+    private void onSwipeLeft() {
+        mTileProvider.onSwipeLeft();
+    }
+
+    private void onSwipeRight() {
+        mTileProvider.onSwipeRight();
+    }
+
     /**
      * Processes touch events.
      */
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 146b761..f9fe8c3 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -94,30 +94,6 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
 
         // Writer documents always have one part, so hide the navigation drawer.
         if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) {
-
-            // Set left/right swipe listener for presentation only.
-            if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION) {
-                LayerView layerView = mLayerClient.getView();
-                layerView.setOnTouchListener(new OnSlideSwipeListener(LibreOfficeMainActivity.mAppContext) {
-                    @Override
-                    public void onSwipeRight() {
-                        if (getCurrentPartNumber() < mDocument.getParts()-1) {
-                            LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1);
-                        }
-                    }
-
-                @Override
-                public void onSwipeLeft() {
-                    if (getCurrentPartNumber() > 0) {
-                        LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1);
-                    }
-                }
-                });
-            } else {
-                // Unregister touch listener when loading another document.
-                mLayerClient.getView().setOnTouchListener(null);
-            }
-
             for (int i = 0; i < parts; i++) {
                 String partName = mDocument.getPartName(i);
                 if (partName.isEmpty()) {
@@ -170,6 +146,24 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
         return (input / dpi) * 1440.0f;
     }
 
+    @Override
+    public void onSwipeLeft() {
+        Log.d(LOGTAG, "onSwipeLeft received");
+        if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION
+                && getCurrentPartNumber() < mDocument.getParts()-1) {
+            LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1);
+        }
+    }
+
+    @Override
+    public void onSwipeRight() {
+        Log.d(LOGTAG, "onSwipeRight received");
+        if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION
+                && getCurrentPartNumber() > 0) {
+            LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1);
+        }
+    }
+
     private boolean checkDocument() {
         String error = null;
         boolean ret;
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
index 43f2e6a..753faba 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -144,6 +144,19 @@ public class LibreOfficeMainActivity extends LOAbout {
         LayerView layerView = (LayerView) findViewById(R.id.layer_view);
         mLayerClient.setView(layerView);
         layerView.setInputConnectionHandler(new LOKitInputConnectionHandler());
+        layerView.setOnTouchListener(new OnSlideSwipeListener(this) {
+            @Override
+            public void onSwipeRight() {
+                Log.d(LOGTAG, "onSwipeRight");
+                LOKitShell.sendSwipeRightEvent();
+            }
+
+            @Override
+            public void onSwipeLeft() {
+                Log.d(LOGTAG, "onSwipeLeft");
+                LOKitShell.sendSwipeLeftEvent();
+            }
+        });
         mLayerClient.notifyReady();
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index 66ad101..09e12ef 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -127,4 +127,12 @@ public class MockTileProvider implements TileProvider {
     public int getCurrentPartNumber() {
         return 0;
     }
+
+    @Override
+    public void onSwipeLeft() {
+    }
+
+    @Override
+    public void onSwipeRight() {
+    }
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
index 82d18a0e..1dc33f6 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
@@ -22,7 +22,7 @@ public class OnSlideSwipeListener implements OnTouchListener {
 
         @Override
         public boolean onDown(MotionEvent e) {
-            return true;
+            return false;
         }
 
         @Override
@@ -39,13 +39,13 @@ public class OnSlideSwipeListener implements OnTouchListener {
                         } else {
                             onSwipeLeft();
                         }
-                            }
+                    }
                     result = true;
                 }
             } catch (Exception exception) {
                 exception.printStackTrace();
             }
-            return result;
+            return false;
         }
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
index 6c284ce..7fb51d2 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
@@ -75,6 +75,17 @@ public interface TileProvider {
      */
     void mouseButtonDown(PointF documentCoordinate, int numberOfClicks);
 
+
+    /**
+     * Trigger a swipe left event.
+     */
+    void onSwipeLeft();
+
+    /**
+     * Trigger a swipe left event.
+     */
+    void onSwipeRight();
+
     /**
      * Trigger a mouse button up event.
      *
commit fd6129b3a0a7a7f46b39c0abdd94a2a6fccaee48
Author: Siqi Liu <me at siqi.fr>
Date:   Sat Feb 28 19:04:25 2015 +0100

    add swipe support for presentation
    
    Change-Id: I650d661a1656388e50e5d4e3a003b8eb4f032382

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 7a90382..146b761 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -10,12 +10,14 @@ import org.libreoffice.kit.DirectBufferAllocator;
 import org.libreoffice.kit.Document;
 import org.libreoffice.kit.LibreOfficeKit;
 import org.libreoffice.kit.Office;
+
 import org.mozilla.gecko.TextSelection;
 import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.BufferedCairoImage;
 import org.mozilla.gecko.gfx.CairoImage;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.IntSize;
+import org.mozilla.gecko.gfx.LayerView;
 
 import java.nio.ByteBuffer;
 
@@ -92,6 +94,30 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
 
         // Writer documents always have one part, so hide the navigation drawer.
         if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) {
+
+            // Set left/right swipe listener for presentation only.
+            if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION) {
+                LayerView layerView = mLayerClient.getView();
+                layerView.setOnTouchListener(new OnSlideSwipeListener(LibreOfficeMainActivity.mAppContext) {
+                    @Override
+                    public void onSwipeRight() {
+                        if (getCurrentPartNumber() < mDocument.getParts()-1) {
+                            LOKitShell.sendChangePartEvent(getCurrentPartNumber()+1);
+                        }
+                    }
+
+                @Override
+                public void onSwipeLeft() {
+                    if (getCurrentPartNumber() > 0) {
+                        LOKitShell.sendChangePartEvent(getCurrentPartNumber()-1);
+                    }
+                }
+                });
+            } else {
+                // Unregister touch listener when loading another document.
+                mLayerClient.getView().setOnTouchListener(null);
+            }
+
             for (int i = 0; i < parts; i++) {
                 String partName = mDocument.getPartName(i);
                 if (partName.isEmpty()) {
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
new file mode 100644
index 0000000..82d18a0e
--- /dev/null
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/OnSlideSwipeListener.java
@@ -0,0 +1,64 @@
+package org.libreoffice;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+public class OnSlideSwipeListener implements OnTouchListener {
+
+    private final GestureDetector mGestureDetector;
+
+    public OnSlideSwipeListener(Context ctx){
+        mGestureDetector = new GestureDetector(ctx, new GestureListener());
+    }
+
+    private final class GestureListener extends SimpleOnGestureListener {
+
+        private static final int SWIPE_THRESHOLD = 100;
+        private static final int SWIPE_VELOCITY_THRESHOLD = 100;
+
+        @Override
+        public boolean onDown(MotionEvent e) {
+            return true;
+        }
+
+        @Override
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
+            boolean result = false;
+            try {
+                float diffY = e2.getY() - e1.getY();
+                float diffX = e2.getX() - e1.getX();
+                if (Math.abs(diffX) > Math.abs(diffY)) {
+                    if (Math.abs(diffX) > SWIPE_THRESHOLD
+                            && Math.abs(velX) > SWIPE_VELOCITY_THRESHOLD) {
+                        if (diffX > 0) {
+                            onSwipeRight();
+                        } else {
+                            onSwipeLeft();
+                        }
+                            }
+                    result = true;
+                }
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            return result;
+        }
+    }
+
+    public void onSwipeRight() {
+    }
+
+    public void onSwipeLeft() {
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent me) {
+        return mGestureDetector.onTouchEvent(me);
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 8c086b9f1401ef987435097b02e43a8dcb21bae3
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Feb 27 16:44:23 2015 +0100

    SwEditWin::SetCursorLogicPosition: use SwCrsrShell::getShellCrsr
    
    Using raw GetCrsr() would return the shell cursor even if there is a
    table cursor. With this, if there is a table selection, the selection
    start handle can properly shrink and expand the start of the selection.
    
    Change-Id: Ic525071639333a00bb5a03a3dd74c4f8052e24f7

diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx
index c9cda8a..bf0b28d 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -6263,10 +6263,10 @@ void SwEditWin::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool
     // If the mark is to be updated, then exchange the point and mark before
     // and after, as we can't easily set the mark.
     if (!bPoint)
-        rShell.GetCrsr()->Exchange();
+        rShell.getShellCrsr(/*bBlock=*/false)->Exchange();
     rShell.SetCrsr(rPosition);
     if (!bPoint)
-        rShell.GetCrsr()->Exchange();
+        rShell.getShellCrsr(/*bBlock=*/false)->Exchange();
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 6650fe947d41bc43cb05b572acb50180799d5b91
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Feb 27 15:38:30 2015 +0100

    LOK: move Office::postKeyEvent to Document
    
    The implementation still sends them to the currently active VCL frame,
    not to the given document, though.
    
    Change-Id: I6fa2decdea3f949c55287e802cb3373c85664207

diff --git a/android/Bootstrap/src/org/libreoffice/kit/Document.java b/android/Bootstrap/src/org/libreoffice/kit/Document.java
index f410693..c692099 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/Document.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/Document.java
@@ -107,6 +107,14 @@ public class Document {
     public native void initializeForRendering();
 
     /**
+     * Post a key event to LibreOffice.
+     * @param type - type of key event
+     * @param charCode - the Unicode character generated by this event or 0.
+     * @param keyCode - the integer code representing the key of the event (non-zero for control keys).
+     */
+    public native void postKeyEvent(int type, int charCode, int keyCode);
+
+    /**
      * Post a mouse event to LOK
      * @param type - mouse event type
      * @param x - x coordinate
diff --git a/android/Bootstrap/src/org/libreoffice/kit/Office.java b/android/Bootstrap/src/org/libreoffice/kit/Office.java
index 8bd780b..86eda5c 100644
--- a/android/Bootstrap/src/org/libreoffice/kit/Office.java
+++ b/android/Bootstrap/src/org/libreoffice/kit/Office.java
@@ -34,14 +34,6 @@ public class Office {
         return document;
     }
 
-    /**
-     * Post a key event to LibreOffice.
-     * @param type - type of key event
-     * @param charCode - the Unicode character generated by this event or 0.
-     * @param keyCode - the integer code representing the key of the event (non-zero for control keys).
-     */
-    public native void postKeyEvent(int type, int charCode, int keyCode);
-
     public native void destroy();
     public native void destroyAndExit();
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index c2d59e0..7a90382 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -313,12 +313,12 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
             String keyString = keyEvent.getCharacters();
             for (int i = 0; i < keyString.length(); i++) {
                 int codePoint = keyString.codePointAt(i);
-                mOffice.postKeyEvent(Office.KEY_PRESS, codePoint, getKeyCode(keyEvent));
+                mDocument.postKeyEvent(Office.KEY_PRESS, codePoint, getKeyCode(keyEvent));
             }
         } else if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
-            mOffice.postKeyEvent(Office.KEY_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent));
+            mDocument.postKeyEvent(Office.KEY_PRESS, getCharCode(keyEvent), getKeyCode(keyEvent));
         } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
-            mOffice.postKeyEvent(Office.KEY_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent));
+            mDocument.postKeyEvent(Office.KEY_RELEASE, getCharCode(keyEvent), getKeyCode(keyEvent));
         }
     }
 
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 4aea3d0..0c2f4a9 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -203,6 +203,10 @@ static void doc_initializeForRendering(LibreOfficeKitDocument* pThis);
 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
                                 LibreOfficeKitCallback pCallback,
                                 void* pData);
+static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
+                             int nType,
+                             int nCharCode,
+                             int nKeyCode);
 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
                                 int nType,
                                 int nX,
@@ -239,6 +243,7 @@ struct LibLODocument_Impl : public _LibreOfficeKitDocument
             m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
             m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
             m_pDocumentClass->registerCallback = doc_registerCallback;
+            m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
             m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
             m_pDocumentClass->setTextSelection = doc_setTextSelection;
 
@@ -266,7 +271,6 @@ static char *                  lo_getError      (LibreOfficeKit* pThis);
 static LibreOfficeKitDocument* lo_documentLoadWithOptions  (LibreOfficeKit* pThis,
                                                            const char* pURL,
                                                            const char* pOptions);
-static void                    lo_postKeyEvent  (LibreOfficeKit* pThis, int nType, int nCharCode, int nKeyCode);
 
 
 struct LibLibreOffice_Impl : public _LibreOfficeKit
@@ -286,7 +290,6 @@ struct LibLibreOffice_Impl : public _LibreOfficeKit
             m_pOfficeClass->documentLoad = lo_documentLoad;
             m_pOfficeClass->getError = lo_getError;
             m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
-            m_pOfficeClass->postKeyEvent = lo_postKeyEvent;
 
             gOfficeClass = m_pOfficeClass;
         }
@@ -693,6 +696,25 @@ static void doc_registerCallback(LibreOfficeKitDocument* pThis,
     pDoc->registerCallback(pCallback, pData);
 }
 
+static void doc_postKeyEvent(LibreOfficeKitDocument* /*pThis*/, int nType, int nCharCode, int nKeyCode)
+{
+#if defined(UNX) && !defined(MACOSX) && !defined(ENABLE_HEADLESS)
+    if (SalFrame *pFocus = SvpSalFrame::GetFocusFrame())
+    {
+        KeyEvent aEvent(nCharCode, nKeyCode, 0);
+        switch (nType)
+        {
+        case LOK_KEYEVENT_KEYINPUT:
+            Application::PostKeyEvent(VCLEVENT_WINDOW_KEYINPUT, pFocus->GetWindow(), &aEvent);
+            break;
+        case LOK_KEYEVENT_KEYUP:
+            Application::PostKeyEvent(VCLEVENT_WINDOW_KEYUP, pFocus->GetWindow(), &aEvent);
+            break;
+        }
+    }
+#endif
+}
+
 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount)
 {
     ITiledRenderable* pDoc = getTiledRenderable(pThis);
@@ -726,25 +748,6 @@ static char* lo_getError (LibreOfficeKit *pThis)
     return pMemory;
 }
 
-static void lo_postKeyEvent(LibreOfficeKit* /*pThis*/, int nType, int nCharCode, int nKeyCode)
-{
-#if defined(UNX) && !defined(MACOSX) && !defined(ENABLE_HEADLESS)
-    if (SalFrame *pFocus = SvpSalFrame::GetFocusFrame())
-    {
-        KeyEvent aEvent(nCharCode, nKeyCode, 0);
-        switch (nType)
-        {
-        case LOK_KEYEVENT_KEYINPUT:
-            Application::PostKeyEvent(VCLEVENT_WINDOW_KEYINPUT, pFocus->GetWindow(), &aEvent);
-            break;
-        case LOK_KEYEVENT_KEYUP:
-            Application::PostKeyEvent(VCLEVENT_WINDOW_KEYUP, pFocus->GetWindow(), &aEvent);
-            break;
-        }
-    }
-#endif
-}
-
 static void force_c_locale(void)
 {
     // force locale (and resource files loaded) to en-US
diff --git a/desktop/source/lib/lokandroid.cxx b/desktop/source/lib/lokandroid.cxx
index a4f7297..528ffe1 100644
--- a/desktop/source/lib/lokandroid.cxx
+++ b/desktop/source/lib/lokandroid.cxx
@@ -75,13 +75,6 @@ extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_destroyAn
     _exit(0);
 }
 
-extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Office_postKeyEvent
-    (JNIEnv* pEnv, jobject aObject, jint nType, jint nCharCode, jint nKeyCode)
-{
-    LibreOfficeKit* pLibreOfficeKit = getHandle<LibreOfficeKit>(pEnv, aObject);
-    pLibreOfficeKit->pClass->postKeyEvent(pLibreOfficeKit, nType, nCharCode, nKeyCode);
-}
-
 namespace
 {
 
@@ -277,6 +270,13 @@ extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Office_saveAs
     return result;
 }
 
+extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_postKeyEvent
+    (JNIEnv* pEnv, jobject aObject, jint nType, jint nCharCode, jint nKeyCode)
+{
+    LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject);
+    pDocument->pClass->postKeyEvent(pDocument, nType, nCharCode, nKeyCode);
+}
+
 extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_postMouseEvent
     (JNIEnv* pEnv, jobject aObject, jint type, jint x, jint y, jint count)
 {
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h
index f8a62b2..ea9846a 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -49,9 +49,6 @@ struct _LibreOfficeKitClass
   LibreOfficeKitDocument* (*documentLoadWithOptions)  (LibreOfficeKit* pThis,
                                                       const char* pURL,
                                                       const char* pOptions);
-#ifdef LOK_USE_UNSTABLE_API
-  void                    (*postKeyEvent)  (LibreOfficeKit* pThis, int nType, int nCharCode, int nKeyCode);
-#endif // LOK_USE_UNSTABLE_API
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
@@ -125,6 +122,11 @@ struct _LibreOfficeKitDocumentClass
                               LibreOfficeKitCallback pCallback,
                               void* pData);
 
+  /// @see lok::Document::postKeyEvent
+  void (*postKeyEvent)(LibreOfficeKitDocument* pThis,
+                       int nType,
+                       int nCharCode,
+                       int nKeyCode);
   /// @see lok::Document::postMouseEvent
   void (*postMouseEvent)(LibreOfficeKitDocument* pThis,
                          int nType,
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx
index f8d6e19..a442382 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -106,6 +106,18 @@ public:
     }
 
     /**
+     * Posts a keyboard event to the focused frame.
+     *
+     * @param nType Event type, like press or release.
+     * @param nCharCode contains the Unicode character generated by this event or 0
+     * @param nKeyCode contains the integer code representing the key of the event (non-zero for control keys)
+     */
+    inline void postKeyEvent(int nType, int nCharCode, int nKeyCode)
+    {
+        mpDoc->pClass->postKeyEvent(mpDoc, nType, nCharCode, nKeyCode);
+    }
+
+    /**
      * Posts a mouse event to the document.
      *
      * @param nType Event type, like down, move or up.
@@ -167,20 +179,6 @@ public:
     {
         return mpThis->pClass->getError(mpThis);
     }
-
-#ifdef LOK_USE_UNSTABLE_API
-    /**
-     * Posts a keyboard event to the focused frame.
-     *
-     * @param nType Event type, like press or release.
-     * @param nCharCode contains the Unicode character generated by this event or 0
-     * @param nKeyCode contains the integer code representing the key of the event (non-zero for control keys)
-     */
-    inline void postKeyEvent(int nType, int nCharCode, int nKeyCode)
-    {
-        mpThis->pClass->postKeyEvent(mpThis, nType, nCharCode, nKeyCode);
-    }
-#endif // LOK_USE_UNSTABLE_API
 };
 
 inline Office* lok_cpp_init(const char* pInstallPath)
diff --git a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
index c840c8c..cfa108e 100644
--- a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
@@ -186,9 +186,9 @@ static void signalKey(GtkWidget* /*pWidget*/, GdkEventKey* pEvent, gpointer /*pD
         nKeyCode |= KEY_SHIFT;
 
     if (pEvent->type == GDK_KEY_RELEASE)
-        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYUP, nCharCode, nKeyCode);
+        pLOKDocView->pDocument->pClass->postKeyEvent(pLOKDocView->pDocument, LOK_KEYEVENT_KEYUP, nCharCode, nKeyCode);
     else
-        pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYINPUT, nCharCode, nKeyCode);
+        pLOKDocView->pDocument->pClass->postKeyEvent(pLOKDocView->pDocument, LOK_KEYEVENT_KEYINPUT, nCharCode, nKeyCode);
 }
 
 // GtkComboBox requires gtk 2.24 or later
commit 645bebd9f6090b1c617bd95ea990a7dac4afaabc
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Feb 27 13:02:24 2015 +0100

    SwSelPaintRects::FillStartEnd(): missing const
    
    Change-Id: I644535d94894e51bc134ba8394238d3044eab488

diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx
index 7de57a2..db3a964 100644
--- a/sw/inc/viscrs.hxx
+++ b/sw/inc/viscrs.hxx
@@ -90,7 +90,7 @@ public:
 
     virtual void FillRects() = 0;
     /// Fill rStart and rEnd with a rectangle that represents the start and end for selection handles.
-    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) = 0;
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) const = 0;
 
     // #i75172# in SwCrsrShell::CreateCrsr() the content of SwSelPaintRects is exchanged. To
     // make a complete swap access to mpCursorOverlay is needed there
@@ -129,7 +129,7 @@ public:
 
     virtual void FillRects() SAL_OVERRIDE;   // For Table- und normal cursors.
     /// @see SwSelPaintRects::FillStartEnd(), override for text selections.
-    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) SAL_OVERRIDE;
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) const SAL_OVERRIDE;
 
     void Show();            // Update and display all selections.
     void Hide();            // Hide all selections.
@@ -180,7 +180,7 @@ public:
 
     virtual void FillRects() SAL_OVERRIDE;   // For table and normal cursor.
     /// @see SwSelPaintRects::FillStartEnd(), override for table selections.
-    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) SAL_OVERRIDE;
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) const SAL_OVERRIDE;
 
     // Check if SPoint is within table SSelection.
     bool IsInside( const Point& rPt ) const;
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 3d0d883..1422f3d 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -276,7 +276,7 @@ static SwRect lcl_getLayoutRect(const Point& rPoint, const SwPosition& rPosition
     return aRect;
 }
 
-void SwShellCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd)
+void SwShellCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd) const
 {
     const SwShellCrsr* pCursor = GetShell()->getShellCrsr(false);
     rStart = lcl_getLayoutRect(pCursor->GetSttPos(), *pCursor->Start());
@@ -773,7 +773,7 @@ void SwShellTableCrsr::FillRects()
     insert( begin(), aReg.begin(), aReg.end() );
 }
 
-void SwShellTableCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd)
+void SwShellTableCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd) const
 {
     rStart = m_aStart;
     rEnd = m_aEnd;
commit ee8ee08b4dfc698fa4e21ea28ff961e4c67cb160
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Fri Feb 27 10:33:10 2015 +0100

    SwSelPaintRects: add FillStartEnd() interface
    
    Previously we always assumed non-table selections in SwSelPaintRects,
    but that is not always true, resulting in incorrect handles for table
    selections. Add a FillStartEnd() interface to fill the passed SwRects
    with the rectangle of the start and end handle, and implement it in both
    SwShellCrsr and SwShellTableCrsr.
    
    This makes adjusting the end of table selections using the end selection
    handle work (be it shrinking or extending).
    
    Change-Id: Iba8657466b102ec60c41829f00660dfe295643ab

diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx
index b24bea5..7de57a2 100644
--- a/sw/inc/viscrs.hxx
+++ b/sw/inc/viscrs.hxx
@@ -89,6 +89,8 @@ public:
     virtual ~SwSelPaintRects();
 
     virtual void FillRects() = 0;
+    /// Fill rStart and rEnd with a rectangle that represents the start and end for selection handles.
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) = 0;
 
     // #i75172# in SwCrsrShell::CreateCrsr() the content of SwSelPaintRects is exchanged. To
     // make a complete swap access to mpCursorOverlay is needed there
@@ -126,6 +128,8 @@ public:
     virtual ~SwShellCrsr();
 
     virtual void FillRects() SAL_OVERRIDE;   // For Table- und normal cursors.
+    /// @see SwSelPaintRects::FillStartEnd(), override for text selections.
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) SAL_OVERRIDE;
 
     void Show();            // Update and display all selections.
     void Hide();            // Hide all selections.
@@ -159,6 +163,10 @@ public:
 
 class SwShellTableCrsr : public virtual SwShellCrsr, public virtual SwTableCursor
 {
+    /// Left edge of the selection start (top left cell).
+    SwRect m_aStart;
+    /// Right edge of the selection end (bottom right cell).
+    SwRect m_aEnd;
     // The Selection has the same order as the table boxes, i.e.
     // if something is deleted from the one array at a certain position
     // it has to be deleted from the other one as well!!
@@ -171,6 +179,8 @@ public:
     virtual ~SwShellTableCrsr();
 
     virtual void FillRects() SAL_OVERRIDE;   // For table and normal cursor.
+    /// @see SwSelPaintRects::FillStartEnd(), override for table selections.
+    virtual void FillStartEnd(SwRect& rStart, SwRect& rEnd) SAL_OVERRIDE;
 
     // Check if SPoint is within table SSelection.
     bool IsInside( const Point& rPt ) const;
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 89bcc28..3d0d883 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -276,6 +276,13 @@ static SwRect lcl_getLayoutRect(const Point& rPoint, const SwPosition& rPosition
     return aRect;
 }
 
+void SwShellCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd)
+{
+    const SwShellCrsr* pCursor = GetShell()->getShellCrsr(false);
+    rStart = lcl_getLayoutRect(pCursor->GetSttPos(), *pCursor->Start());
+    rEnd = lcl_getLayoutRect(pCursor->GetEndPos(), *pCursor->End());
+}
+
 #endif
 
 void SwSelPaintRects::Show()
@@ -345,13 +352,20 @@ void SwSelPaintRects::Show()
                 // events, if there is a real selection.
                 // This can be used to easily show selection handles on the
                 // client side.
-                const SwShellCrsr* pCursor = GetShell()->getShellCrsr(false);
-                SwRect aStartRect = lcl_getLayoutRect(pCursor->GetSttPos(), *pCursor->Start());
-                OString sRect = aStartRect.SVRect().toString();
-                GetShell()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_START, sRect.getStr());
-                SwRect aEndRect = lcl_getLayoutRect(pCursor->GetEndPos(), *pCursor->End());
-                sRect = aEndRect.SVRect().toString();
-                GetShell()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_END, sRect.getStr());
+                SwRect aStartRect;
+                SwRect aEndRect;
+                FillStartEnd(aStartRect, aEndRect);
+
+                if (aStartRect.Height())
+                {
+                    OString sRect = aStartRect.SVRect().toString();
+                    GetShell()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_START, sRect.getStr());
+                }
+                if (aEndRect.Height())
+                {
+                    OString sRect = aEndRect.SVRect().toString();
+                    GetShell()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_END, sRect.getStr());
+                }
             }
 
             std::stringstream ss;
@@ -703,10 +717,12 @@ void SwShellTableCrsr::FillRects()
     if (m_SelectedBoxes.empty() || bParked || !GetPoint()->nNode.GetIndex())
         return;
 
+    bool bStart = true;
     SwRegionRects aReg( GetShell()->VisArea() );
     if (GetShell()->isTiledRendering())
         aReg = GetShell()->getIDocumentLayoutAccess()->GetCurrentLayout()->Frm();
     SwNodes& rNds = GetDoc()->GetNodes();
+    SwFrm* pEndFrm = 0;
     for (size_t n = 0; n < m_SelectedBoxes.size(); ++n)
     {
         const SwStartNode* pSttNd = m_SelectedBoxes[n]->GetSttNd();
@@ -738,15 +754,31 @@ void SwShellTableCrsr::FillRects()
         while ( pFrm )
         {
             if( aReg.GetOrigin().IsOver( pFrm->Frm() ) )
+            {
                 aReg -= pFrm->Frm();
+                if (bStart)
+                {
+                    bStart = false;
+                    m_aStart = SwRect(pFrm->Frm().Left(), pFrm->Frm().Top(), 0, pFrm->Frm().Height());
+                }
+            }
 
+            pEndFrm = pFrm;
             pFrm = pFrm->GetNextCellLeaf( MAKEPAGE_NONE );
         }
     }
+    if (pEndFrm)
+        m_aEnd = SwRect(pEndFrm->Frm().Right(), pEndFrm->Frm().Top(), 0, pEndFrm->Frm().Height());
     aReg.Invert();
     insert( begin(), aReg.begin(), aReg.end() );
 }
 
+void SwShellTableCrsr::FillStartEnd(SwRect& rStart, SwRect& rEnd)
+{
+    rStart = m_aStart;
+    rEnd = m_aEnd;
+}
+
 // Check if the SPoint is within the Table-SSelection.
 bool SwShellTableCrsr::IsInside( const Point& rPt ) const
 {
commit f52920ad25ae142bb49beaa7a7353240f143b0b7
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Feb 26 15:59:17 2015 +0100

    SwShellTableCrsr::FillRects: ignore visual area for tiled rendering
    
    With this, if you have a 2x2 empty table and you are on A1, pressing
    shift-right results in a proper table selection of A1:B1. Still need to
    fix selection handles, though.
    
    Change-Id: Ifb4598f715991cc7b88828d505c3af008c744676

diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 74b5ee6..89bcc28 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -704,6 +704,8 @@ void SwShellTableCrsr::FillRects()
         return;
 
     SwRegionRects aReg( GetShell()->VisArea() );
+    if (GetShell()->isTiledRendering())
+        aReg = GetShell()->getIDocumentLayoutAccess()->GetCurrentLayout()->Frm();
     SwNodes& rNds = GetDoc()->GetNodes();
     for (size_t n = 0; n < m_SelectedBoxes.size(); ++n)
     {
commit 135bc56f87c563ce5bb2c5d2a7faf35e7ecfec59
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Feb 26 14:29:32 2015 +0100

    libreofficekit: pretty-print LibreOfficeKitCallbackType
    
    Change-Id: I15a6b265c6fa0d0c62277814fa119d20b311625b

diff --git a/libreofficekit/source/gtk/lokdocview.c b/libreofficekit/source/gtk/lokdocview.c
index ce81eb8..525bf84 100644
--- a/libreofficekit/source/gtk/lokdocview.c
+++ b/libreofficekit/source/gtk/lokdocview.c
@@ -589,6 +589,24 @@ static GList* lcl_payloadToRectangles(const char* pPayload)
     return pRet;
 }
 
+static const gchar* lcl_LibreOfficeKitCallbackTypeToString(int nType)
+{
+    switch (nType)
+    {
+    case LOK_CALLBACK_INVALIDATE_TILES:
+        return "LOK_CALLBACK_INVALIDATE_TILES";
+    case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+        return "LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR";
+    case LOK_CALLBACK_TEXT_SELECTION:
+        return "LOK_CALLBACK_TEXT_SELECTION";
+    case LOK_CALLBACK_TEXT_SELECTION_START:
+        return "LOK_CALLBACK_TEXT_SELECTION_START";
+    case LOK_CALLBACK_TEXT_SELECTION_END:
+        return "LOK_CALLBACK_TEXT_SELECTION_END";
+    }
+    return 0;
+}
+
 /// Invoked on the main thread if lok_docview_callback_worker() requests so.
 static gboolean lok_docview_callback(gpointer pData)
 {
@@ -665,7 +683,7 @@ static void lok_docview_callback_worker(int nType, const char* pPayload, void* p
     pCallback->m_nType = nType;
     pCallback->m_pPayload = g_strdup(pPayload);
     pCallback->m_pDocView = pDocView;
-    g_info("lok_docview_callback_worker: %d, '%s'", nType, pPayload);
+    g_info("lok_docview_callback_worker: %s, '%s'", lcl_LibreOfficeKitCallbackTypeToString(nType), pPayload);
 #if GTK_CHECK_VERSION(2,12,0)
     gdk_threads_add_idle(lok_docview_callback, pCallback);
 #else
commit a2446dca88b53462025777d8b468d2b0d5aea737
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Feb 26 14:17:07 2015 +0100

    gtktiledviewer: map shift key
    
    Change-Id: Id7e995ef3b6eb117dc74a0a0258417d9dc1291e6

diff --git a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
index 2392bd6..c840c8c 100644
--- a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
@@ -21,6 +21,7 @@
 #include "../lokdocview_quad/lokdocview_quad.h"
 
 #include <com/sun/star/awt/Key.hpp>
+#include <rsc/rsc-vcl-shared-types.hxx>
 
 #ifndef g_info
 #define g_info(...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
@@ -178,6 +179,12 @@ static void signalKey(GtkWidget* /*pWidget*/, GdkEventKey* pEvent, gpointer /*pD
             nCharCode = gdk_keyval_to_unicode(pEvent->keyval);
     }
 
+    // rsc is not public API, but should be good enough for debugging purposes.
+    // If this is needed for real, then probably a new param of type
+    // css::awt::KeyModifier is needed in postKeyEvent().
+    if (pEvent->state & GDK_SHIFT_MASK)
+        nKeyCode |= KEY_SHIFT;
+
     if (pEvent->type == GDK_KEY_RELEASE)
         pLOKDocView->pOffice->pClass->postKeyEvent(pLOKDocView->pOffice, LOK_KEYEVENT_KEYUP, nCharCode, nKeyCode);
     else
commit 4cbfcfe3d6199158d717e07b0932a7108b7ffd36
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Thu Feb 26 13:00:41 2015 +0100

    gtktiledviewer: map down/up/left/right arrow keys
    
    Change-Id: I2a3c729d06d73ff7a7b7cedff27d9fb4a3a88d73

diff --git a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
index 5337cc4..2392bd6 100644
--- a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx
@@ -159,6 +159,18 @@ static void signalKey(GtkWidget* /*pWidget*/, GdkEventKey* pEvent, gpointer /*pD
     case GDK_Tab:
         nKeyCode = com::sun::star::awt::Key::TAB;
         break;
+    case GDK_Down:
+        nKeyCode = com::sun::star::awt::Key::DOWN;
+        break;
+    case GDK_Up:
+        nKeyCode = com::sun::star::awt::Key::UP;
+        break;
+    case GDK_Left:
+        nKeyCode = com::sun::star::awt::Key::LEFT;
+        break;
+    case GDK_Right:
+        nKeyCode = com::sun::star::awt::Key::RIGHT;
+        break;
     default:
         if (pEvent->keyval >= GDK_F1 && pEvent->keyval <= GDK_F26)
             nKeyCode = com::sun::star::awt::Key::F1 + (pEvent->keyval - GDK_F1);
commit e85db50db96d9caaf8a75e8fae6b06978467572b
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 19:46:11 2015 +0900

    android: reset selections in TextCursorView on empty invalidation
    
    Change-Id: I49997520057763299add18fb7c1bc662ef0c76b3

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index 630ffff..1f3b39b 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -8,6 +8,7 @@ import org.mozilla.gecko.TextSelection;
 import org.mozilla.gecko.TextSelectionHandle;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -207,6 +208,7 @@ public class InvalidationHandler {
             mState = OverlayState.CURSOR;
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.START);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.END);
+            mTextCursorLayer.changeSelections(Collections.EMPTY_LIST);
         } else {
             mState = OverlayState.SELECTION;
             List<RectF> rects = convertPayloadToRectangles(payload);
commit 6c972d2f48786102791bcc62317eaa4b6a9e4451
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 19:36:54 2015 +0900

    android: if in selection state, reset selection on single tap
    
    Change-Id: I8ae76ee4898087998b88ac2e1d73b3843fc00559

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index 9e328d6..630ffff 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -52,6 +52,10 @@ public class InvalidationHandler {
         }
     }
 
+    public OverlayState getOverlayState() {
+        return mState;
+    }
+
     public void setOverlayState(OverlayState state) {
         this.mState = state;
     }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 39a200c..334cd44 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -255,6 +255,9 @@ public class LOKitThread extends Thread {
             mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
             mTileProvider.mouseButtonDown(documentCoordinate, 1);
             mTileProvider.mouseButtonUp(documentCoordinate, 1);
+            if (mInvalidationHandler.getOverlayState() == InvalidationHandler.OverlayState.SELECTION) {
+                mTileProvider.setTextSelectionReset();
+            }
         }
     }
 
commit b99fe5ebc4c06dfbaf6714ea13ce690fa01988f8
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 19:36:10 2015 +0900

    android: add selection reset to TileProvider
    
    Change-Id: I9a42913069dcaa2bab1e3db51cebe3a0cdcf5746

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index f3af706..c2d59e0 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -354,6 +354,11 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
     }
 
     @Override
+    public void setTextSelectionReset() {
+        mDocument.setTextSelection(Document.TEXT_SELECTION_RESET, 0, 0);
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         close();
         super.finalize();
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index b4f1b82..66ad101 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -115,6 +115,11 @@ public class MockTileProvider implements TileProvider {
     }
 
     @Override
+    public void setTextSelectionReset() {
+
+    }
+
+    @Override
     public void changePart(int partIndex) {
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
index d749f78..6c284ce 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
@@ -86,4 +86,6 @@ public interface TileProvider {
     void setTextSelectionStart(PointF documentCoordinate);
 
     void setTextSelectionEnd(PointF documentCoordinate);
+
+    void setTextSelectionReset();
 }
commit b29e9060d5b0b33d54b6131188a95986910be046
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 19:24:12 2015 +0900

    android: still process cursor invalidations when in selection mode
    
    Change-Id: I24d0b4f58e2227b289eb55cb881664e2cb5a51e9

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index d9d4f5e..9e328d6 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -141,19 +141,16 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateCursor(String payload) {
-        if (mState == OverlayState.CURSOR) {
-            RectF cursorRectangle = convertPayloadToRectangle(payload);
-            if (cursorRectangle != null) {
-                TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
-                textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, createRectangleUnderSelection(cursorRectangle));
-                textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
-                textSelection.hideHandle(TextSelectionHandle.HandleType.START);
-                textSelection.hideHandle(TextSelectionHandle.HandleType.END);
-
-                TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
-                textCursorLayer.positionCursor(cursorRectangle);
-                textCursorLayer.showCursor();
+        RectF cursorRectangle = convertPayloadToRectangle(payload);
+        if (cursorRectangle != null) {
+            if (mState == OverlayState.CURSOR) {
+                mTextSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, createRectangleUnderSelection(cursorRectangle));
+                mTextSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
+                mTextSelection.hideHandle(TextSelectionHandle.HandleType.START);
+                mTextSelection.hideHandle(TextSelectionHandle.HandleType.END);
             }
+            mTextCursorLayer.positionCursor(cursorRectangle);
+            mTextCursorLayer.showCursor();
         }
     }
 
commit b0478cb1a116fe53b721ee627c98ec16e07c203c
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 19:22:25 2015 +0900

    android: don't hide soft keyboard on long press
    
    Change-Id: I8b1f6057b08752171710e1e5d4d0ab4664a63369

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index fc576c6b..39a200c 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -248,7 +248,6 @@ public class LOKitThread extends Thread {
             return;
         }
         if (touchType.equals("LongPress")) {
-            LibreOfficeMainActivity.mAppContext.hideSoftKeyboard();
             mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.SELECTION);
             mTileProvider.mouseButtonDown(documentCoordinate, 2);
         } else { // "SingleTap"
commit ae53e0841d86b33c81ae17eea3aa809d99bce8ca
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 18:58:38 2015 +0900

    android: fix parsing of selection coordinates and make more robust
    
    Change-Id: Ie2fb81cc9c2096df4d9361887ed5bab8a0b841d3

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index fb46c77..d9d4f5e 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -38,19 +38,15 @@ public class InvalidationHandler {
                 invalidateTiles(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_VISIBLE_CURSOR:
-                Log.i(LOGTAG, "Cursor: " + payload);
                 invalidateCursor(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_TEXT_SELECTION:
-                Log.i(LOGTAG, "Selection: " + payload);
                 invalidateSelection(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_START:
-                Log.i(LOGTAG, "Selection start: " + payload);
                 invalidateSelectionStart(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_TEXT_SELECTION_END:
-                Log.i(LOGTAG, "Selection end: " + payload);
                 invalidateSelectionEnd(payload);
                 break;
         }
@@ -67,19 +63,22 @@ public class InvalidationHandler {
      * @return rectangle in pixel coordinates
      */
     private RectF convertPayloadToRectangle(String payload) {
-        if (payload.equals("EMPTY")) {
+        String payloadWithoutWhitespace = payload.replaceAll("\\s",""); // remove all whitespace from the string
+
+        if (payloadWithoutWhitespace.isEmpty() || payloadWithoutWhitespace.equals("EMPTY")) {
             return null;
         }
 
-        String[] coordinates = payload.split(",");
+        String[] coordinates = payloadWithoutWhitespace.split(",");
 
         if (coordinates.length != 4) {
             return null;
         }
-        int width = Integer.decode(coordinates[0].trim());
-        int height = Integer.decode(coordinates[1].trim());
-        int x = Integer.decode(coordinates[2].trim());
-        int y = Integer.decode(coordinates[3].trim());
+
+        int width = Integer.decode(coordinates[0]);
+        int height = Integer.decode(coordinates[1]);
+        int x = Integer.decode(coordinates[2]);
+        int y = Integer.decode(coordinates[3]);
 
         float dpi = (float) LOKitShell.getDpi();
 
@@ -104,8 +103,11 @@ public class InvalidationHandler {
         String[] rectangleArray = payload.split(";");
 
         for (String coordinates : rectangleArray) {
-            RectF rectangle = convertPayloadToRectangle(payload);
-            rectangles.add(rectangle);
+            RectF rectangle = convertPayloadToRectangle(coordinates);
+            if (rectangle != null) {
+                rectangles.add(rectangle);
+            }
+
         }
 
         return rectangles;
commit d5ad8429df278f029ed0117cea8c2bf93460ca66
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 18:56:41 2015 +0900

    android: send selection/cursor change from TextSelectionHandle
    
    Change-Id: Iac570c890717d8e2dcfde7f29da996c809df81dc

diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java
index 6b07a3f..0bd1486 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java
@@ -74,51 +74,33 @@ public class TextSelectionHandle extends ImageView implements View.OnTouchListen
             case MotionEvent.ACTION_UP: {
                 mTouchStartX = 0;
                 mTouchStartY = 0;
-
-                // Reposition handles to line up with ends of selection
-                JSONObject args = new JSONObject();
-                try {
-                    args.put("handleType", mHandleType.toString());
-                } catch (Exception e) {
-                    Log.e(LOGTAG, "Error building JSON arguments for TextSelection:Position");
-                }
-                //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("TextSelection:Position", args.toString()));
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
-                move(Math.round(event.getX()), Math.round(event.getY()));
+                move(event.getX(), event.getY());
                 break;
             }
         }
         return true;
     }
 
-    private void move(int newX, int newY) {
-        mLeft = mLeft + newX - mTouchStartX;
-        mTop = mTop + newY - mTouchStartY;
-
+    private void move(float newX, float newY) {
         LayerView layerView = LOKitShell.getLayerView();
         if (layerView == null) {
             Log.e(LOGTAG, "Can't move selection because layerView is null");
             return;
         }
+
+        float newLeft = mLeft + newX - mTouchStartX;
+        float newTop  = mTop + newY - mTouchStartY;
+
         // Send x coordinate on the right side of the start handle, left side of the end handle.
-        float left = (float) mLeft + adjustLeftForHandle();
-
-        PointF geckoPoint = new PointF(left, (float) mTop);
-        geckoPoint = layerView.getLayerClient().convertViewPointToLayerPoint(geckoPoint);
-
-        JSONObject args = new JSONObject();
-        try {
-            args.put("handleType", mHandleType.toString());
-            args.put("x", Math.round(geckoPoint.x));
-            args.put("y", Math.round(geckoPoint.y));
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Error building JSON arguments for TextSelection:Move");
-        }
-        //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("TextSelection:Move", args.toString()));
+        float left = (float) newLeft + adjustLeftForHandle();
 
-        setLayoutPosition();
+        PointF documentPoint = new PointF(left, newTop);
+        documentPoint = layerView.getLayerClient().convertViewPointToLayerPoint(documentPoint);
+
+        LOKitShell.sendChangeHandlePositionEvent(mHandleType, documentPoint);
     }
 
     void positionFromGecko(int left, int top, boolean rtl) {
commit b2753b46665bf9b51fd2870e5894dd6d924a7d8c
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 18:55:34 2015 +0900

    android: connect the selection change LOEvent with TileProvider
    
    Change-Id: Ic0b499fcafb56fd5cff6559b66a1698992c1c6ed

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 295d982..fc576c6b 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -7,6 +7,7 @@ import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.CairoImage;
 import org.mozilla.gecko.gfx.ComposedTileLayer;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
@@ -204,6 +205,22 @@ public class LOKitThread extends Thread {
             case LOEvent.TILE_REEVALUATION_REQUEST:
                 tileReevaluationRequest(event.mComposedTileLayer);
                 break;
+            case LOEvent.CHANGE_HANDLE_POSITION:
+                changeHandlePosition(event.mHandleType, event.mDocumentCoordinate);
+                break;
+        }
+    }
+
+    private void changeHandlePosition(TextSelectionHandle.HandleType handleType, PointF documentCoordinate) {
+        if (handleType == TextSelectionHandle.HandleType.MIDDLE) {
+            LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
+            mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
+            mTileProvider.mouseButtonDown(documentCoordinate, 1);
+            mTileProvider.mouseButtonUp(documentCoordinate, 1);
+        } else if (handleType == TextSelectionHandle.HandleType.START) {
+            mTileProvider.setTextSelectionStart(documentCoordinate);
+        } else if (handleType == TextSelectionHandle.HandleType.END) {
+            mTileProvider.setTextSelectionEnd(documentCoordinate);
         }
     }
 
commit ed42840dfb61899edde5e88373904c34a59a55c6
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 18:51:41 2015 +0900

    android: support selection change (start,end) to TileProvider
    
    Change-Id: If983699dacbd6c992ffff886215e8f686e0f5a2d

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index f1faf71..f3af706 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -340,6 +340,20 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
     }
 
     @Override
+    public void setTextSelectionStart(PointF documentCoordinate) {
+        int x = (int) pixelToTwip(documentCoordinate.x, mDPI);
+        int y = (int) pixelToTwip(documentCoordinate.y, mDPI);
+        mDocument.setTextSelection(Document.TEXT_SELECTION_START, x, y);
+    }
+
+    @Override
+    public void setTextSelectionEnd(PointF documentCoordinate) {
+        int x = (int) pixelToTwip(documentCoordinate.x, mDPI);
+        int y = (int) pixelToTwip(documentCoordinate.y, mDPI);
+        mDocument.setTextSelection(Document.TEXT_SELECTION_END, x, y);
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         close();
         super.finalize();
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index fe934a1..b4f1b82 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -105,6 +105,16 @@ public class MockTileProvider implements TileProvider {
     }
 
     @Override
+    public void setTextSelectionStart(PointF documentCoordinate) {
+
+    }
+
+    @Override
+    public void setTextSelectionEnd(PointF documentCoordinate) {
+
+    }
+
+    @Override
     public void changePart(int partIndex) {
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
index ea868bd..d749f78 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
@@ -82,4 +82,8 @@ public interface TileProvider {
      * @param numberOfClicks     - number of clicks (1 - single click, 2 - double click)
      */
     void mouseButtonUp(PointF documentCoordinate, int numberOfClicks);
+
+    void setTextSelectionStart(PointF documentCoordinate);
+
+    void setTextSelectionEnd(PointF documentCoordinate);
 }
commit bb0892de8f54e8f6f2b6427c4d839fef217b03f4
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 18:48:47 2015 +0900

    android: add LOEvent to report handle position changes
    
    With LOEvent CHANGE_HANDLE_POSITION we can report handle (start
    and end selelection change and cursor change) to the LOKitThread.
    
    Change-Id: Ia247acc147e54a1f05999a5e55786367c4377091

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
index e5afe16..a046988 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
@@ -5,6 +5,7 @@ import android.graphics.RectF;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.ComposedTileLayer;
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.SubTile;
@@ -19,6 +20,7 @@ public class LOEvent implements Comparable<LOEvent> {
     public static final int TILE_INVALIDATION = 7;
     public static final int TOUCH = 8;
     public static final int KEY_EVENT = 9;
+    public static final int CHANGE_HANDLE_POSITION = 10;
 
     public final int mType;
     public int mPriority = 0;
@@ -29,9 +31,10 @@ public class LOEvent implements Comparable<LOEvent> {
     public String mFilename;
     public ComposedTileLayer mComposedTileLayer;
     public String mTouchType;
-    public PointF mDocumentTouchCoordinate;
+    public PointF mDocumentCoordinate;
     public KeyEvent mKeyEvent;
     public RectF mInvalidationRect;
+    public TextSelectionHandle.HandleType mHandleType;
 
     public LOEvent(int type) {
         mType = type;
@@ -65,7 +68,7 @@ public class LOEvent implements Comparable<LOEvent> {
         mType = type;
         mTypeString = "Touch";
         mTouchType = touchType;
-        mDocumentTouchCoordinate = documentTouchCoordinate;
+        mDocumentCoordinate = documentTouchCoordinate;
     }
 
     public LOEvent(int type, KeyEvent keyEvent) {
@@ -80,6 +83,12 @@ public class LOEvent implements Comparable<LOEvent> {
         mInvalidationRect = rect;
     }
 
+    public LOEvent(int type, TextSelectionHandle.HandleType handleType, PointF documentCoordinate) {
+        mType = type;
+        mHandleType = handleType;
+        mDocumentCoordinate = documentCoordinate;
+    }
+
     public String getTypeString() {
         if (mTypeString == null) {
             return "Event type: " + mType;
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
index 809b4c1..68ab523 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
@@ -10,6 +10,7 @@ import android.util.DisplayMetrics;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.ComposedTileLayer;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.SubTile;
@@ -126,4 +127,11 @@ public class LOKitShell {
     public static void sendTileInvalidationRequest(RectF rect) {
         LOKitShell.sendEvent(new LOEvent(LOEvent.TILE_INVALIDATION, rect));
     }
+
+    /**
+     * Send change handle position event to LOKitThread.
+     */
+    public static void sendChangeHandlePositionEvent(TextSelectionHandle.HandleType handleType, PointF documentCoordinate) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.CHANGE_HANDLE_POSITION, handleType, documentCoordinate));
+    }
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 6fb6f3a..295d982 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -196,7 +196,7 @@ public class LOKitThread extends Thread {
                 createThumbnail(event.mTask);
                 break;
             case LOEvent.TOUCH:
-                touch(event.mTouchType, event.mDocumentTouchCoordinate);
+                touch(event.mTouchType, event.mDocumentCoordinate);
                 break;
             case LOEvent.KEY_EVENT:
                 keyEvent(event.mKeyEvent);
@@ -223,7 +223,7 @@ public class LOKitThread extends Thread {
     /**
      * Processes touch events.
      */
-    private void touch(String touchType, PointF mDocumentTouchCoordinate) {
+    private void touch(String touchType, PointF documentCoordinate) {
         if (!LOKitShell.isEditingEnabled()) {
             return;
         }
@@ -233,12 +233,12 @@ public class LOKitThread extends Thread {
         if (touchType.equals("LongPress")) {
             LibreOfficeMainActivity.mAppContext.hideSoftKeyboard();
             mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.SELECTION);
-            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2);
+            mTileProvider.mouseButtonDown(documentCoordinate, 2);
         } else { // "SingleTap"
             LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
             mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
-            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 1);
-            mTileProvider.mouseButtonUp(mDocumentTouchCoordinate, 1);
+            mTileProvider.mouseButtonDown(documentCoordinate, 1);
+            mTileProvider.mouseButtonUp(documentCoordinate, 1);
         }
     }
 
commit cb08fbbd2ac8816c48c8439895ee809a4359278c
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:59:27 2015 +0900

    android: no need for MotionEvent when sending TOUCH LOEvent
    
    Change-Id: I9173f50fb60680de1bb9744d3f351c44f8175d0c

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
index bce6284..e5afe16 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOEvent.java
@@ -29,7 +29,6 @@ public class LOEvent implements Comparable<LOEvent> {
     public String mFilename;
     public ComposedTileLayer mComposedTileLayer;
     public String mTouchType;
-    public MotionEvent mMotionEvent;
     public PointF mDocumentTouchCoordinate;
     public KeyEvent mKeyEvent;
     public RectF mInvalidationRect;
@@ -62,11 +61,10 @@ public class LOEvent implements Comparable<LOEvent> {
         mTypeString = "Thumbnail";
     }
 
-    public LOEvent(int type, String touchType, MotionEvent motionEvent, PointF documentTouchCoordinate) {
+    public LOEvent(int type, String touchType, PointF documentTouchCoordinate) {
         mType = type;
         mTypeString = "Touch";
         mTouchType = touchType;
-        mMotionEvent = motionEvent;
         mDocumentTouchCoordinate = documentTouchCoordinate;
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
index f574e69..809b4c1 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
@@ -86,8 +86,8 @@ public class LOKitShell {
     /**
      * Send touch event to LOKitThread.
      */
-    public static void sendTouchEvent(String touchType, MotionEvent motionEvent, PointF pointF) {
-        LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, touchType, motionEvent, pointF));
+    public static void sendTouchEvent(String touchType, PointF documentTouchCoordinate) {
+        LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, touchType, documentTouchCoordinate));
     }
 
     /**
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 6f63e93..6fb6f3a 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -196,7 +196,7 @@ public class LOKitThread extends Thread {
                 createThumbnail(event.mTask);
                 break;
             case LOEvent.TOUCH:
-                touch(event.mTouchType, event.mMotionEvent, event.mDocumentTouchCoordinate);
+                touch(event.mTouchType, event.mDocumentTouchCoordinate);
                 break;
             case LOEvent.KEY_EVENT:
                 keyEvent(event.mKeyEvent);
@@ -223,7 +223,7 @@ public class LOKitThread extends Thread {
     /**
      * Processes touch events.
      */
-    private void touch(String touchType, MotionEvent motionEvent, PointF mDocumentTouchCoordinate) {
+    private void touch(String touchType, PointF mDocumentTouchCoordinate) {
         if (!LOKitShell.isEditingEnabled()) {
             return;
         }
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
index 5de4260..adae23a 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
@@ -941,7 +941,7 @@ public class JavaPanZoomController
 
     @Override
     public void onLongPress(MotionEvent motionEvent) {
-        LOKitShell.sendTouchEvent("LongPress", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+        LOKitShell.sendTouchEvent("LongPress", getMotionInDocumentCoordinates(motionEvent));
     }
 
     @Override
@@ -949,7 +949,7 @@ public class JavaPanZoomController
         // When double-tapping is allowed, we have to wait to see if this is
         // going to be a double-tap.
         if (!mWaitForDoubleTap) {
-            LOKitShell.sendTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+            LOKitShell.sendTouchEvent("SingleTap", getMotionInDocumentCoordinates(motionEvent));
         }
         // return false because we still want to get the ACTION_UP event that triggers this
         return false;
@@ -959,14 +959,14 @@ public class JavaPanZoomController
     public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
         // In cases where we don't wait for double-tap, we handle this in onSingleTapUp.
         if (mWaitForDoubleTap) {
-            LOKitShell.sendTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+            LOKitShell.sendTouchEvent("SingleTap", getMotionInDocumentCoordinates(motionEvent));
         }
         return true;
     }
 
     @Override
     public boolean onDoubleTap(MotionEvent motionEvent) {
-        LOKitShell.sendTouchEvent("DoubleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+        LOKitShell.sendTouchEvent("DoubleTap", getMotionInDocumentCoordinates(motionEvent));
         return true;
     }
 
commit 5fef3a0c6d4c13166e68d0e84c27dc0febef8d8a
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:53:18 2015 +0900

    android: sent -> send
    
    Change-Id: I241a1cbfb98b9e9bb8f769980547c395427bc0b5

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
index b2b4b9d..f574e69 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitShell.java
@@ -86,7 +86,7 @@ public class LOKitShell {
     /**
      * Send touch event to LOKitThread.
      */
-    public static void sentTouchEvent(String touchType, MotionEvent motionEvent, PointF pointF) {
+    public static void sendTouchEvent(String touchType, MotionEvent motionEvent, PointF pointF) {
         LOKitShell.sendEvent(new LOEvent(LOEvent.TOUCH, touchType, motionEvent, pointF));
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
index af3f11c..5de4260 100644
--- a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
@@ -941,7 +941,7 @@ public class JavaPanZoomController
 
     @Override
     public void onLongPress(MotionEvent motionEvent) {
-        LOKitShell.sentTouchEvent("LongPress", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+        LOKitShell.sendTouchEvent("LongPress", motionEvent, getMotionInDocumentCoordinates(motionEvent));
     }
 
     @Override
@@ -949,7 +949,7 @@ public class JavaPanZoomController
         // When double-tapping is allowed, we have to wait to see if this is
         // going to be a double-tap.
         if (!mWaitForDoubleTap) {
-            LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+            LOKitShell.sendTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         }
         // return false because we still want to get the ACTION_UP event that triggers this
         return false;
@@ -959,14 +959,14 @@ public class JavaPanZoomController
     public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
         // In cases where we don't wait for double-tap, we handle this in onSingleTapUp.
         if (mWaitForDoubleTap) {
-            LOKitShell.sentTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+            LOKitShell.sendTouchEvent("SingleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         }
         return true;
     }
 
     @Override
     public boolean onDoubleTap(MotionEvent motionEvent) {
-        LOKitShell.sentTouchEvent("DoubleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
+        LOKitShell.sendTouchEvent("DoubleTap", motionEvent, getMotionInDocumentCoordinates(motionEvent));
         return true;
     }
 
commit fc4672c9e22bdd7f3f2683ca484ee87f732e0362
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:42:56 2015 +0900

    android: first set overlay state and send mouse button event later
    
    Change-Id: I30d909ce04baaab8bed1e8bd03b4926b95ed95ee

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index c2dbb23..6f63e93 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -232,13 +232,13 @@ public class LOKitThread extends Thread {
         }
         if (touchType.equals("LongPress")) {
             LibreOfficeMainActivity.mAppContext.hideSoftKeyboard();
-            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2);
             mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.SELECTION);
+            mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2);
         } else { // "SingleTap"
             LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
+            mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
             mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 1);
             mTileProvider.mouseButtonUp(mDocumentTouchCoordinate, 1);
-            mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
         }
     }
 
commit 39d4b4010f93b490ebcb46b43fee1dedfc0902d1
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:29:43 2015 +0900

    android: add OverlayState to track the state of overlay
    
    Overlay can either be in state NONE - no overlay elements should
    be shown (this is the deocument reader state), CURSOR - cursor
    and the cursor handle are shown, SELECTION - selection and
    selection handles are shown. The states can change either by
    an invalidation or touch input.
    
    Change-Id: Ia15eb58193675b3799c0014a91f4429a729e30d4

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index 8933c88..fb46c77 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -18,10 +18,12 @@ public class InvalidationHandler {
 
     private TextCursorLayer mTextCursorLayer;
     private TextSelection mTextSelection;
+    private OverlayState mState;
 
     public InvalidationHandler(LibreOfficeMainActivity mainActivity) {
         mTextCursorLayer = mainActivity.getTextCursorLayer();
         mTextSelection = mainActivity.getTextSelection();
+        mState = OverlayState.NONE;
     }
 
     /**
@@ -54,6 +56,10 @@ public class InvalidationHandler {
         }
     }
 
+    public void setOverlayState(OverlayState state) {
+        this.mState = state;
+    }
+
     /**
      * Parses the payload text with rectangle coordinates and converts to rectangle in pixel coordinates
      *
@@ -133,18 +139,19 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateCursor(String payload) {
-        RectF cursorRectangle = convertPayloadToRectangle(payload);
-        if (cursorRectangle != null) {
-            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
-            textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, createRectangleUnderSelection(cursorRectangle));
-            textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
-
-            textSelection.hideHandle(TextSelectionHandle.HandleType.START);
-            textSelection.hideHandle(TextSelectionHandle.HandleType.END);
-
-            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
-            textCursorLayer.positionCursor(cursorRectangle);
-            textCursorLayer.showCursor();
+        if (mState == OverlayState.CURSOR) {
+            RectF cursorRectangle = convertPayloadToRectangle(payload);
+            if (cursorRectangle != null) {
+                TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+                textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, createRectangleUnderSelection(cursorRectangle));
+                textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
+                textSelection.hideHandle(TextSelectionHandle.HandleType.START);
+                textSelection.hideHandle(TextSelectionHandle.HandleType.END);
+
+                TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
+                textCursorLayer.positionCursor(cursorRectangle);
+                textCursorLayer.showCursor();
+            }
         }
     }
 
@@ -154,12 +161,15 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateSelectionStart(String payload) {
+        if (mState == OverlayState.NONE) {
+            return;
+        }
         RectF selectionRectangle = convertPayloadToRectangle(payload);
         if (selectionRectangle != null) {
             mTextSelection.positionHandle(TextSelectionHandle.HandleType.START, createRectangleUnderSelection(selectionRectangle));
             mTextSelection.showHandle(TextSelectionHandle.HandleType.START);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
-            mTextCursorLayer.hideCursor();
+            mState = OverlayState.SELECTION;
         }
     }
 
@@ -169,12 +179,15 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateSelectionEnd(String payload) {
+        if (mState == OverlayState.NONE) {
+            return;
+        }
         RectF selectionRect = convertPayloadToRectangle(payload);
         if (selectionRect != null) {
             mTextSelection.positionHandle(TextSelectionHandle.HandleType.END, createRectangleUnderSelection(selectionRect));
             mTextSelection.showHandle(TextSelectionHandle.HandleType.END);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
-            mTextCursorLayer.hideCursor();
+            mState = OverlayState.SELECTION;
         }
     }
 
@@ -184,12 +197,23 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateSelection(String payload) {
+        if (mState == OverlayState.NONE) {
+            return;
+        }
         if (payload.isEmpty()) {
+            mState = OverlayState.CURSOR;
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.START);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.END);
         } else {
+            mState = OverlayState.SELECTION;
             List<RectF> rects = convertPayloadToRectangles(payload);
             mTextCursorLayer.changeSelections(rects);
         }
     }
+
+    public enum OverlayState {
+        NONE,
+        CURSOR,
+        SELECTION
+    }
 }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index fa7fb59..c2dbb23 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -233,10 +233,12 @@ public class LOKitThread extends Thread {
         if (touchType.equals("LongPress")) {
             LibreOfficeMainActivity.mAppContext.hideSoftKeyboard();
             mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 2);
+            mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.SELECTION);
         } else { // "SingleTap"
             LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
             mTileProvider.mouseButtonDown(mDocumentTouchCoordinate, 1);
             mTileProvider.mouseButtonUp(mDocumentTouchCoordinate, 1);
+            mInvalidationHandler.setOverlayState(InvalidationHandler.OverlayState.CURSOR);
         }
     }
 
commit a8547448f08c6b5083faf72ed3cdf7cac3ab4a1b
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:24:42 2015 +0900

    android: swap position of invalidateCursor and invalidateTiles
    
    Change-Id: Iebc21915b92c82b0def3f4f61fb2128c2942d122

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index cfef907..8933c88 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -116,6 +116,18 @@ public class InvalidationHandler {
     }
 
     /**
+     * Handles the tile invalidation message
+     *
+     * @param payload
+     */
+    private void invalidateTiles(String payload) {
+        RectF rectangle = convertPayloadToRectangle(payload);
+        if (rectangle != null) {
+            LOKitShell.sendTileInvalidationRequest(rectangle);
+        }
+    }
+
+    /**
      * Handles the cursor invalidation message
      *
      * @param payload
@@ -137,18 +149,6 @@ public class InvalidationHandler {
     }
 
     /**
-     * Handles the tile invalidation message
-     *
-     * @param payload
-     */
-    private void invalidateTiles(String payload) {
-        RectF rectangle = convertPayloadToRectangle(payload);
-        if (rectangle != null) {
-            LOKitShell.sendTileInvalidationRequest(rectangle);
-        }
-    }
-
-    /**
      * Handles the selection start invalidation message
      *
      * @param payload
commit c2ab77974ec69d73ca2b372e18de76bdeda1e83b
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 16:18:08 2015 +0900

    android: support parsing selection rectangles, rectructure code
    
    Add support to parse selection rectangles (series of rectangles
    separated by ";"). Additionally restructure and clean up the code
    in InvalidationHandler to be better readable - better comments,
    better variable and method names.
    
    Change-Id: I64d1dc4a806831bd47ca9f14478bec8192035461

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index a43127b..cfef907 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -7,6 +7,9 @@ import org.libreoffice.kit.Document;
 import org.mozilla.gecko.TextSelection;
 import org.mozilla.gecko.TextSelectionHandle;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Parses (interprets) and handles invalidation messages from LibreOffice.
  */
@@ -52,17 +55,17 @@ public class InvalidationHandler {
     }
 
     /**
-     * Parses the invalidation message text and converts to rectangle in pixel coordinates
+     * Parses the payload text with rectangle coordinates and converts to rectangle in pixel coordinates
      *
-     * @param messageText - invalidation message text
+     * @param payload - invalidation message payload text
      * @return rectangle in pixel coordinates
      */
-    private RectF convertCallbackMessageStringToRectF(String messageText) {
-        if (messageText.equals("EMPTY")) {
+    private RectF convertPayloadToRectangle(String payload) {
+        if (payload.equals("EMPTY")) {
             return null;
         }
 
-        String[] coordinates = messageText.split(",");
+        String[] coordinates = payload.split(",");
 
         if (coordinates.length != 4) {
             return null;
@@ -85,18 +88,51 @@ public class InvalidationHandler {
     }
 
     /**
+     * Parses the payload text with more rectangles (separated by ';') and converts to a list of rectangles.
+     *
+     * @param payload - invalidation message payload text
+     * @return list of rectangles
+     */
+    private List<RectF> convertPayloadToRectangles(String payload) {
+        List<RectF> rectangles = new ArrayList<RectF>();
+        String[] rectangleArray = payload.split(";");
+
+        for (String coordinates : rectangleArray) {
+            RectF rectangle = convertPayloadToRectangle(payload);
+            rectangles.add(rectangle);
+        }
+
+        return rectangles;
+    }
+
+    /**
+     * From input rectangle, create a new rectangle which positions under the input rectangle.
+     *
+     * @param rectangle - input rectangle
+     * @return new rectangle positioned under the input rectangle
+     */
+    private RectF createRectangleUnderSelection(RectF rectangle) {
+        return new RectF(rectangle.centerX(), rectangle.bottom, rectangle.centerX(), rectangle.bottom);
+    }
+
+    /**
      * Handles the cursor invalidation message
      *
      * @param payload
      */
     private void invalidateCursor(String payload) {
-        RectF rect = convertCallbackMessageStringToRectF(payload);
-        if (rect != null) {
-            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
-            mTextSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, underSelection);
-            mTextSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
-            mTextCursorLayer.positionCursor(rect);
-            mTextCursorLayer.showCursor();
+        RectF cursorRectangle = convertPayloadToRectangle(payload);
+        if (cursorRectangle != null) {
+            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+            textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, createRectangleUnderSelection(cursorRectangle));
+            textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
+
+            textSelection.hideHandle(TextSelectionHandle.HandleType.START);
+            textSelection.hideHandle(TextSelectionHandle.HandleType.END);
+
+            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
+            textCursorLayer.positionCursor(cursorRectangle);
+            textCursorLayer.showCursor();
         }
     }
 
@@ -106,9 +142,9 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateTiles(String payload) {
-        RectF rect = convertCallbackMessageStringToRectF(payload);
-        if (rect != null) {
-            LOKitShell.sendTileInvalidationRequest(rect);
+        RectF rectangle = convertPayloadToRectangle(payload);
+        if (rectangle != null) {
+            LOKitShell.sendTileInvalidationRequest(rectangle);
         }
     }
 
@@ -118,10 +154,9 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateSelectionStart(String payload) {
-        RectF rect = convertCallbackMessageStringToRectF(payload);
-        if (rect != null) {
-            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
-            mTextSelection.positionHandle(TextSelectionHandle.HandleType.START, underSelection);
+        RectF selectionRectangle = convertPayloadToRectangle(payload);
+        if (selectionRectangle != null) {
+            mTextSelection.positionHandle(TextSelectionHandle.HandleType.START, createRectangleUnderSelection(selectionRectangle));
             mTextSelection.showHandle(TextSelectionHandle.HandleType.START);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
             mTextCursorLayer.hideCursor();
@@ -134,10 +169,9 @@ public class InvalidationHandler {
      * @param payload
      */
     private void invalidateSelectionEnd(String payload) {
-        RectF rect = convertCallbackMessageStringToRectF(payload);
-        if (rect != null) {
-            RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
-            mTextSelection.positionHandle(TextSelectionHandle.HandleType.END, underSelection);
+        RectF selectionRect = convertPayloadToRectangle(payload);
+        if (selectionRect != null) {
+            mTextSelection.positionHandle(TextSelectionHandle.HandleType.END, createRectangleUnderSelection(selectionRect));
             mTextSelection.showHandle(TextSelectionHandle.HandleType.END);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
             mTextCursorLayer.hideCursor();
@@ -153,7 +187,9 @@ public class InvalidationHandler {
         if (payload.isEmpty()) {
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.START);
             mTextSelection.hideHandle(TextSelectionHandle.HandleType.END);
+        } else {
+            List<RectF> rects = convertPayloadToRectangles(payload);
+            mTextCursorLayer.changeSelections(rects);
         }
     }
-
 }
commit dc43b61e6e792c0e7a9126dee81da2f437d81cfe
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Feb 26 15:53:49 2015 +0900

    android: create InvalidationHandler outside of LOKitTileProvider
    
    Change-Id: I79bb20ca5a16a2ebdc6ed3f1c97a483173b0762b

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
index ae18647b..a43127b 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/InvalidationHandler.java
@@ -13,14 +13,19 @@ import org.mozilla.gecko.TextSelectionHandle;
 public class InvalidationHandler {
     private static String LOGTAG = InvalidationHandler.class.getSimpleName();
 
-    public InvalidationHandler() {
+    private TextCursorLayer mTextCursorLayer;
+    private TextSelection mTextSelection;
+
+    public InvalidationHandler(LibreOfficeMainActivity mainActivity) {
+        mTextCursorLayer = mainActivity.getTextCursorLayer();
+        mTextSelection = mainActivity.getTextSelection();
     }
 
     /**
      * Processes invalidation message
      *
      * @param messageID - ID of the message
-     * @param payload - additional invalidation message payload
+     * @param payload   - additional invalidation message payload
      */
     public void processMessage(int messageID, String payload) {
         switch (messageID) {
@@ -28,6 +33,7 @@ public class InvalidationHandler {
                 invalidateTiles(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+                Log.i(LOGTAG, "Cursor: " + payload);
                 invalidateCursor(payload);
                 break;
             case Document.CALLBACK_INVALIDATE_TEXT_SELECTION:
@@ -80,24 +86,23 @@ public class InvalidationHandler {
 
     /**
      * Handles the cursor invalidation message
+     *
      * @param payload
      */
     private void invalidateCursor(String payload) {
         RectF rect = convertCallbackMessageStringToRectF(payload);
         if (rect != null) {
             RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
-            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
-            textSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, underSelection);
-            textSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
-
-            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
-            textCursorLayer.positionCursor(rect);
-            textCursorLayer.showCursor();
+            mTextSelection.positionHandle(TextSelectionHandle.HandleType.MIDDLE, underSelection);
+            mTextSelection.showHandle(TextSelectionHandle.HandleType.MIDDLE);
+            mTextCursorLayer.positionCursor(rect);
+            mTextCursorLayer.showCursor();
         }
     }
 
     /**
      * Handles the tile invalidation message
+     *
      * @param payload
      */
     private void invalidateTiles(String payload) {
@@ -109,51 +114,45 @@ public class InvalidationHandler {
 
     /**
      * Handles the selection start invalidation message
+     *
      * @param payload
      */
     private void invalidateSelectionStart(String payload) {
         RectF rect = convertCallbackMessageStringToRectF(payload);
         if (rect != null) {
             RectF underSelection = new RectF(rect.centerX(), rect.bottom, rect.centerX(), rect.bottom);
-            TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
-            textSelection.positionHandle(TextSelectionHandle.HandleType.START, underSelection);
-            textSelection.showHandle(TextSelectionHandle.HandleType.START);
-
-            textSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
-
-            TextCursorLayer textCursorLayer = LibreOfficeMainActivity.mAppContext.getTextCursorLayer();
-            textCursorLayer.hideCursor();
+            mTextSelection.positionHandle(TextSelectionHandle.HandleType.START, underSelection);
+            mTextSelection.showHandle(TextSelectionHandle.HandleType.START);
+            mTextSelection.hideHandle(TextSelectionHandle.HandleType.MIDDLE);
+            mTextCursorLayer.hideCursor();
         }
     }
 
     /**
      * Handles the selection end invalidation message
+     *
      * @param payload
      */

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list