[Libreoffice-commits] core.git: Branch 'feature/tiled-editing' - 5 commits - android/experimental sw/inc sw/source

Tomaž Vajngerl tomaz.vajngerl at collabora.co.uk
Thu Jan 29 18:41:57 PST 2015


 android/experimental/LOAndroid3/res/drawable-hdpi/handle_end.png                      |binary
 android/experimental/LOAndroid3/res/drawable-hdpi/handle_middle.png                   |binary
 android/experimental/LOAndroid3/res/drawable-hdpi/handle_start.png                    |binary
 android/experimental/LOAndroid3/res/drawable-xhdpi/handle_end.png                     |binary
 android/experimental/LOAndroid3/res/drawable-xhdpi/handle_middle.png                  |binary
 android/experimental/LOAndroid3/res/drawable-xhdpi/handle_start.png                   |binary
 android/experimental/LOAndroid3/res/drawable/handle_end.png                           |binary
 android/experimental/LOAndroid3/res/drawable/handle_middle.png                        |binary
 android/experimental/LOAndroid3/res/drawable/handle_start.png                         |binary
 android/experimental/LOAndroid3/res/layout/activity_main.xml                          |    3 
 android/experimental/LOAndroid3/res/layout/text_selection_handles.xml                 |   29 +
 android/experimental/LOAndroid3/res/values/attrs.xml                                  |   17 
 android/experimental/LOAndroid3/res/values/dimens.xml                                 |    3 
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java             |   27 -
 android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java       |   16 
 android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java |   15 
 android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java        |    5 
 android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java            |    5 
 android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java             |   32 -
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelection.java         |  191 ++++++++++
 android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java   |  170 ++++++++
 sw/inc/crsrsh.hxx                                                                     |    6 
 sw/source/core/crsr/crsrsh.cxx                                                        |   21 -
 23 files changed, 482 insertions(+), 58 deletions(-)

New commits:
commit 0266b4ab4464998bea7bc29b6289f193040523d3
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Jan 29 19:03:07 2015 +0900

    tdf#87098 don't adjust zoom/position for spreadsheets
    
    Change-Id: Ieb908980a931b123e2c48fe3ecdc7830b48810ed

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index 5cb8f31..354404c 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -63,16 +63,27 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
         mViewportMetrics = mLayerClient.getViewportMetrics();
         mLayerClient.setViewportMetrics(mViewportMetrics);
 
-        if (mTileProvider.isTextDocument()) {
+        zoomAndRepositionTheDocument();
+
+        mLayerClient.forceRedraw();
+    }
+
+    private void zoomAndRepositionTheDocument() {
+        if (mTileProvider.isSpreadsheet()) {
+            // Don't do anything for spreadsheets - show at 100%
+        } else if (mTileProvider.isTextDocument()) {
+            // Always zoom text document to the beginning of the document and centered by width
             float centerY = mViewportMetrics.getCssViewport().centerY();
-            mLayerClient.zoomTo(new RectF (0, centerY, mTileProvider.getPageWidth(), centerY));
-        } else if (mViewportMetrics.getViewport().width() < mViewportMetrics.getViewport().height()) {
-            mLayerClient.zoomTo(mTileProvider.getPageWidth(), 0);
+            mLayerClient.zoomTo(new RectF(0, centerY, mTileProvider.getPageWidth(), centerY));
         } else {
-            mLayerClient.zoomTo(0, mTileProvider.getPageHeight());
+            // Other documents - always show the whole document on the screen,
+            // regardless of document shape and orientation.
+            if (mViewportMetrics.getViewport().width() < mViewportMetrics.getViewport().height()) {
+                mLayerClient.zoomTo(mTileProvider.getPageWidth(), 0);
+            } else {
+                mLayerClient.zoomTo(0, mTileProvider.getPageHeight());
+            }
         }
-
-        mLayerClient.forceRedraw();
     }
 
     /** Invalidate everything + handle the geometry change */
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 5539450..c5f31b3 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -270,6 +270,11 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
         return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_TEXT;
     }
 
+    @Override
+    public boolean isSpreadsheet() {
+        return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_SPREADSHEET;
+    }
+
     /**
      * Register the tile invalidation callback.
      */
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
index 006ae90..34347bb 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/MockTileProvider.java
@@ -85,6 +85,11 @@ public class MockTileProvider implements TileProvider {
     }
 
     @Override
+    public boolean isSpreadsheet() {
+        return false;
+    }
+
+    @Override
     public void registerInvalidationCallback(TileInvalidationCallback tileInvalidationCallback) {
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
index 759ecad..c983c62 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/TileProvider.java
@@ -54,6 +54,11 @@ public interface TileProvider {
     boolean isTextDocument();
 
     /**
+     * Returns true if the current open document is a spreadsheet.
+     */
+    boolean isSpreadsheet();
+
+    /**
      * Register a callback that is invoked when a tile invalidation is
      * required.
      *
commit 5b97fb635f160e5a2f8ee88adbb488174acec9c3
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Jan 29 18:07:06 2015 +0900

    Don't hide cursor for Android anymore - makes cursor events work
    
    Change-Id: I97caadf079c9a5e38c00c80fdc8185aee997f632

diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx
index 7e61c7d4..220bb8c 100644
--- a/sw/source/core/crsr/crsrsh.cxx
+++ b/sw/source/core/crsr/crsrsh.cxx
@@ -63,7 +63,7 @@
 #include <comcore.hrc>
 #include <IDocumentLayoutAccess.hxx>
 
-#if defined(ANDROID) || defined(IOS)
+#if defined(IOS)
 #include <touch/touch.h>
 #endif
 
@@ -2119,7 +2119,7 @@ void SwCrsrShell::ShowCrsr()
     {
         m_bSVCrsrVis = true;
         m_pCurCrsr->SetShowTxtInputFldOverlay( true );
-#if defined(ANDROID) || defined(IOS)
+#if defined(IOS)
         touch_ui_show_keyboard();
 #endif
         UpdateCrsr();
@@ -2135,7 +2135,7 @@ void SwCrsrShell::HideCrsr()
         SET_CURR_SHELL( this );
         m_pCurCrsr->SetShowTxtInputFldOverlay( false );
         m_pVisCrsr->Hide();
-#if defined(ANDROID) || defined(IOS)
+#if defined(IOS)
         touch_ui_hide_keyboard();
 #endif
     }
@@ -2628,7 +2628,7 @@ SwCrsrShell::SwCrsrShell( SwCrsrShell& rShell, vcl::Window *pInitWin )
     m_pVisCrsr = new SwVisCrsr( this );
     m_bMacroExecAllowed = rShell.IsMacroExecAllowed();
 
-#if defined(ANDROID) || defined(IOS)
+#if defined(IOS)
     HideCrsr();
 #endif
 }
@@ -2679,7 +2679,7 @@ SwCrsrShell::SwCrsrShell( SwDoc& rDoc, vcl::Window *pInitWin,
     m_pVisCrsr = new SwVisCrsr( this );
     m_bMacroExecAllowed = true;
 
-#if defined(ANDROID) || defined(IOS)
+#if defined(IOS)
     HideCrsr();
 #endif
 }
commit e1372059d04ff07300d418747766c7f2326d243f
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Jan 29 17:46:23 2015 +0900

    Combine DBG_UTIL and header version of Stt{End}CrsrMove
    
    Having 2 versions (debug and release) of one method (on top of
    that in different places - one implemented in header) which are
    mostly the same is a recipe for disaster.
    
    Change-Id: Ia38f900ac076d1c1765fea2c7326147b115e343d

diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx
index d57520c..13489ce 100644
--- a/sw/inc/crsrsh.hxx
+++ b/sw/inc/crsrsh.hxx
@@ -447,14 +447,8 @@ public:
      */
     void Combine();
 
-#ifdef DBG_UTIL
     void SttCrsrMove();
     void EndCrsrMove( const bool bIdleEnd = false );
-#else
-    void SttCrsrMove() { ++m_nCrsrMove; StartAction(); }
-    void EndCrsrMove( const bool bIdleEnd = false )
-            { EndAction( bIdleEnd, true ); --m_nCrsrMove; }
-#endif
 
     /*
      * When the focus is lost the selected ranges are not displayed anymore.
diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx
index 980c278..7e61c7d4 100644
--- a/sw/source/core/crsr/crsrsh.cxx
+++ b/sw/source/core/crsr/crsrsh.cxx
@@ -314,22 +314,27 @@ void SwCrsrShell::EndAction( const bool bIdleEnd, const bool DoSetPosX )
     }
 }
 
-#ifdef DBG_UTIL
 void SwCrsrShell::SttCrsrMove()
 {
+#ifdef DBG_UTIL
     OSL_ENSURE( m_nCrsrMove < USHRT_MAX, "To many nested CrsrMoves." );
+#endif
     ++m_nCrsrMove;
     StartAction();
 }
 
 void SwCrsrShell::EndCrsrMove( const bool bIdleEnd )
 {
+#ifdef DBG_UTIL
     OSL_ENSURE( m_nCrsrMove, "EndCrsrMove() without SttCrsrMove()." );
+#endif
     EndAction( bIdleEnd, true );
-    if( !--m_nCrsrMove )
+    --m_nCrsrMove;
+#ifdef DBG_UTIL
+    if( !m_nCrsrMove )
         m_bInCMvVisportChgd = false;
-}
 #endif
+}
 
 bool SwCrsrShell::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
                              bool bVisualAllowed )
commit 9bdf428d99eb6eb78882eff9f176bfde0f7cb01e
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Jan 29 14:40:29 2015 +0900

    android: Actually we don't need ViewFactory
    
    Change-Id: I2d1ccafefe9c52d0536601ba7ff219e6547ceb20

diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
index 362fb78..f1eb759 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -96,8 +96,6 @@ public class LibreOfficeMainActivity extends LOAbout {
 
         mMainHandler = new Handler();
 
-        LayoutInflater.from(this).setFactory(ViewFactory.getInstance());
-
         if (getIntent().getData() != null) {
             mInputFile = getIntent().getData().getPath();
         } else {
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java
deleted file mode 100644
index 68f88d9..0000000
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.libreoffice;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import org.mozilla.gecko.TextSelectionHandle;
-import org.mozilla.gecko.gfx.LayerView;
-
-public class ViewFactory implements LayoutInflater.Factory {
-    private static final String LOGTAG = ViewFactory.class.getSimpleName();
-    private static final String LAYER_VIEW_ID = "org.mozilla.gecko.gfx.LayerView";
-    private static final String TEXT_SELECTION_HANDLE_ID = "org.mozilla.gecko.TextSelectionHandle";
-    private static final ViewFactory INSTANCE = new ViewFactory();
-
-    private ViewFactory() {
-    }
-
-    public static LayoutInflater.Factory getInstance() {
-        return INSTANCE;
-    }
-
-    @Override
-    public View onCreateView(String name, Context context, AttributeSet attrs) {
-        if (name.equals(LAYER_VIEW_ID)) {
-            Log.i(LOGTAG, "Creating custom Gecko view: " + name);
-            return new LayerView(context, attrs);
-        } else if (name.equals(TEXT_SELECTION_HANDLE_ID)) {
-            Log.i(LOGTAG, "Creating custom Gecko view: " + name);
-            return new TextSelectionHandle(context, attrs);
-        }
-        return null;
-    }
-}
\ No newline at end of file
commit bdb25ebb9e0b38838cd5e174774443f75b316433
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Thu Jan 29 11:26:24 2015 +0900

    android: integrate text selection handles from Fennec
    
    Integrate text selection handles from Fennec and insert middle
    handle when a cursor invalidation event is recieved from LO.
    
    Change-Id: I6ba31d46bf89555bdbca9ce4be666039e8bc9041

diff --git a/android/experimental/LOAndroid3/res/drawable-hdpi/handle_end.png b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_end.png
new file mode 100644
index 0000000..d5e2044
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_end.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable-hdpi/handle_middle.png b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_middle.png
new file mode 100644
index 0000000..5dcee14
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_middle.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable-hdpi/handle_start.png b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_start.png
new file mode 100644
index 0000000..b6a8ce7
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-hdpi/handle_start.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_end.png b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_end.png
new file mode 100644
index 0000000..c83b7b6
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_end.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_middle.png b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_middle.png
new file mode 100644
index 0000000..2a1774f
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_middle.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_start.png b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_start.png
new file mode 100644
index 0000000..9af7654
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable-xhdpi/handle_start.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable/handle_end.png b/android/experimental/LOAndroid3/res/drawable/handle_end.png
new file mode 100644
index 0000000..32b77df
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable/handle_end.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable/handle_middle.png b/android/experimental/LOAndroid3/res/drawable/handle_middle.png
new file mode 100644
index 0000000..751eb89
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable/handle_middle.png differ
diff --git a/android/experimental/LOAndroid3/res/drawable/handle_start.png b/android/experimental/LOAndroid3/res/drawable/handle_start.png
new file mode 100644
index 0000000..cf12a0d
Binary files /dev/null and b/android/experimental/LOAndroid3/res/drawable/handle_start.png differ
diff --git a/android/experimental/LOAndroid3/res/layout/activity_main.xml b/android/experimental/LOAndroid3/res/layout/activity_main.xml
index 1b1bb07..e9a946f 100644
--- a/android/experimental/LOAndroid3/res/layout/activity_main.xml
+++ b/android/experimental/LOAndroid3/res/layout/activity_main.xml
@@ -22,6 +22,9 @@
                 android:id="@+id/layer_view"
                 android:layout_width="fill_parent"
                 android:layout_height="fill_parent"/>
+
+            <include layout="@layout/text_selection_handles"/>
+
         </RelativeLayout>
 
         <RelativeLayout
diff --git a/android/experimental/LOAndroid3/res/layout/text_selection_handles.xml b/android/experimental/LOAndroid3/res/layout/text_selection_handles.xml
new file mode 100644
index 0000000..1f0d816
--- /dev/null
+++ b/android/experimental/LOAndroid3/res/layout/text_selection_handles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+    <org.mozilla.gecko.TextSelectionHandle android:id="@+id/start_handle"
+                                           android:layout_width="@dimen/text_selection_handle_width"
+                                           android:layout_height="@dimen/text_selection_handle_height"
+                                           android:src="@drawable/handle_start"
+                                           android:visibility="gone"
+                                           gecko:handleType="start"/>
+
+    <org.mozilla.gecko.TextSelectionHandle android:id="@+id/middle_handle"
+                                           android:layout_width="@dimen/text_selection_handle_width"
+                                           android:layout_height="@dimen/text_selection_handle_height"
+                                           android:src="@drawable/handle_middle"
+                                           android:visibility="gone"
+                                           gecko:handleType="middle"/>
+
+    <org.mozilla.gecko.TextSelectionHandle android:id="@+id/end_handle"
+                                           android:layout_width="@dimen/text_selection_handle_width"
+                                           android:layout_height="@dimen/text_selection_handle_height"
+                                           android:src="@drawable/handle_end"
+                                           android:visibility="gone"
+                                           gecko:handleType="end"/>
+</merge>
diff --git a/android/experimental/LOAndroid3/res/values/attrs.xml b/android/experimental/LOAndroid3/res/values/attrs.xml
new file mode 100644
index 0000000..ed73978
--- /dev/null
+++ b/android/experimental/LOAndroid3/res/values/attrs.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<resources>
+
+    <declare-styleable name="TextSelectionHandle">
+        <attr name="handleType">
+            <flag name="start" value="0x01"/>
+            <flag name="middle" value="0x02"/>
+            <flag name="end" value="0x03"/>
+        </attr>
+    </declare-styleable>
+
+</resources>
+
diff --git a/android/experimental/LOAndroid3/res/values/dimens.xml b/android/experimental/LOAndroid3/res/values/dimens.xml
index 47c8224..1e5d771 100644
--- a/android/experimental/LOAndroid3/res/values/dimens.xml
+++ b/android/experimental/LOAndroid3/res/values/dimens.xml
@@ -2,4 +2,7 @@
     <!-- Default screen margins, per the Android Design guidelines. -->
     <dimen name="activity_horizontal_margin">16dp</dimen>
     <dimen name="activity_vertical_margin">16dp</dimen>
+    <dimen name="text_selection_handle_width">30dp</dimen>
+    <dimen name="text_selection_handle_height">44dp</dimen>
+    <dimen name="text_selection_handle_shadow">2dp</dimen>
 </resources>
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
index c8bcc23..5cb8f31 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitThread.java
@@ -168,8 +168,6 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
 
     private void touch(String touchType, MotionEvent motionEvent, PointF mDocumentTouchCoordinate) {
         LibreOfficeMainActivity.mAppContext.showSoftKeyboard();
-        float x = motionEvent.getX();
-        float y = motionEvent.getY();
         mTileProvider.mouseButtonDown(mDocumentTouchCoordinate);
     }
 
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
index 3e3412a7..5539450 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LOKitTileProvider.java
@@ -10,6 +10,7 @@ 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.gfx.BufferedCairoImage;
 import org.mozilla.gecko.gfx.CairoImage;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
@@ -303,6 +304,11 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
     private void mouseButton(int type, PointF inDocument) {
         int x = (int) pixelToTwip(inDocument.x, mDPI);
         int y = (int) pixelToTwip(inDocument.y, mDPI);
+
+        TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+        textSelection.positionHandle("MIDDLE", new RectF(inDocument.x, inDocument.y, inDocument.x, inDocument.y));
+        textSelection.showHandle("MIDDLE");
+
         mDocument.postMouseEvent(type, x, y);
     }
 
@@ -376,9 +382,12 @@ public class LOKitTileProvider implements TileProvider, Document.MessageCallback
                 break;
             }
             case Document.CALLBACK_INVALIDATE_VISIBLE_CURSOR: {
+                Log.i(LOGTAG, "Invalidate visible cursor: " + payload);
                 RectF rect = convertCallbackMessageStringToRectF(payload);
                 if (rect != null) {
-                    //tileInvalidationCallback.invalidate(rect);
+                    TextSelection textSelection = LibreOfficeMainActivity.mAppContext.getTextSelection();
+                    textSelection.positionHandle("MIDDLE", rect);
+                    textSelection.showHandle("MIDDLE");
                 }
                 break;
             }
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
index bca8230..362fb78 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -20,6 +20,8 @@ import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import org.mozilla.gecko.TextSelection;
+import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.ZoomConstraints;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.LayerView;
@@ -45,6 +47,7 @@ public class LibreOfficeMainActivity extends LOAbout {
     private List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>();
     private DocumentPartViewListAdapter mDocumentPartViewListAdapter;
     private String mInputFile;
+    private TextSelection mTextSelection;
 
     public LibreOfficeMainActivity() {
         super(/*newActivity=*/false);
@@ -124,6 +127,12 @@ public class LibreOfficeMainActivity extends LOAbout {
             sLOKitThread.clearQueue();
         }
 
+        TextSelectionHandle startHandle = (TextSelectionHandle) findViewById(R.id.start_handle);
+        TextSelectionHandle middleHandle = (TextSelectionHandle) findViewById(R.id.middle_handle);
+        TextSelectionHandle endHandle = (TextSelectionHandle) findViewById(R.id.end_handle);
+
+        mTextSelection = new TextSelection(startHandle, middleHandle, endHandle);
+
         mLayerClient = new GeckoLayerClient(this);
         mLayerClient.setZoomConstraints(new ZoomConstraints(true));
         LayerView layerView = (LayerView) findViewById(R.id.layer_view);
@@ -231,6 +240,10 @@ public class LibreOfficeMainActivity extends LOAbout {
         alertDialog.show();
     }
 
+    public TextSelection getTextSelection() {
+        return mTextSelection;
+    }
+
     private class DocumentPartClickListener implements android.widget.AdapterView.OnItemClickListener {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
diff --git a/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java b/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java
index c26ad22..68f88d9 100644
--- a/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java
+++ b/android/experimental/LOAndroid3/src/java/org/libreoffice/ViewFactory.java
@@ -6,11 +6,13 @@ import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 
+import org.mozilla.gecko.TextSelectionHandle;
 import org.mozilla.gecko.gfx.LayerView;
 
 public class ViewFactory implements LayoutInflater.Factory {
     private static final String LOGTAG = ViewFactory.class.getSimpleName();
     private static final String LAYER_VIEW_ID = "org.mozilla.gecko.gfx.LayerView";
+    private static final String TEXT_SELECTION_HANDLE_ID = "org.mozilla.gecko.TextSelectionHandle";
     private static final ViewFactory INSTANCE = new ViewFactory();
 
     private ViewFactory() {
@@ -25,8 +27,10 @@ public class ViewFactory implements LayoutInflater.Factory {
         if (name.equals(LAYER_VIEW_ID)) {
             Log.i(LOGTAG, "Creating custom Gecko view: " + name);
             return new LayerView(context, attrs);
+        } else if (name.equals(TEXT_SELECTION_HANDLE_ID)) {
+            Log.i(LOGTAG, "Creating custom Gecko view: " + name);
+            return new TextSelectionHandle(context, attrs);
         }
-
         return null;
     }
 }
\ No newline at end of file
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelection.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelection.java
new file mode 100644
index 0000000..f09e93a
--- /dev/null
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelection.java
@@ -0,0 +1,191 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.View;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.libreoffice.LOKitShell;
+import org.libreoffice.LibreOfficeMainActivity;
+import org.mozilla.gecko.gfx.Layer;
+import org.mozilla.gecko.gfx.LayerView;
+import org.mozilla.gecko.util.FloatUtils;
+
+public class TextSelection extends Layer {
+    private static final String LOGTAG = "GeckoTextSelection";
+
+    private final TextSelectionHandle mStartHandle;
+    private final TextSelectionHandle mMiddleHandle;
+    private final TextSelectionHandle mEndHandle;
+
+    private float mViewLeft;
+    private float mViewTop;
+    private float mViewZoom;
+
+    public TextSelection(TextSelectionHandle startHandle,
+                         TextSelectionHandle middleHandle,
+                         TextSelectionHandle endHandle) {
+        mStartHandle = startHandle;
+        mMiddleHandle = middleHandle;
+        mEndHandle = endHandle;
+
+        // Only register listeners if we have valid start/middle/end handles
+        if (mStartHandle == null || mMiddleHandle == null || mEndHandle == null) {
+            Log.e(LOGTAG, "Failed to initialize text selection because at least one handle is null");
+        }
+    }
+
+    void destroy() {
+    }
+
+    public void handleMessage(String event, JSONObject message) {
+        try {
+            if (event.equals("TextSelection:ShowHandles")) {
+                final JSONArray handles = message.getJSONArray("handles");
+                LibreOfficeMainActivity.mAppContext.mMainHandler.post(new Runnable() {
+                    public void run() {
+                        try {
+                            for (int i=0; i < handles.length(); i++) {
+                                String handle = handles.getString(i);
+
+                                if (handle.equals("START"))
+                                    mStartHandle.setVisibility(View.VISIBLE);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.setVisibility(View.VISIBLE);
+                                else
+                                    mEndHandle.setVisibility(View.VISIBLE);
+                            }
+
+                            mViewLeft = 0.0f;
+                            mViewTop = 0.0f;
+                            mViewZoom = 0.0f;
+                            LayerView layerView = LOKitShell.getLayerView();
+                            if (layerView != null) {
+                                layerView.addLayer(TextSelection.this);
+                            }
+                        } catch(Exception e) {}
+                    }
+                });
+            } else if (event.equals("TextSelection:HideHandles")) {
+                final JSONArray handles = message.getJSONArray("handles");
+                LibreOfficeMainActivity.mAppContext.mMainHandler.post(new Runnable() {
+                    public void run() {
+                        try {
+                            LayerView layerView = LOKitShell.getLayerView();
+                            if (layerView != null) {
+                                layerView.removeLayer(TextSelection.this);
+                            }
+
+                            for (int i=0; i < handles.length(); i++) {
+                                String handle = handles.getString(i);
+                                if (handle.equals("START"))
+                                    mStartHandle.setVisibility(View.GONE);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.setVisibility(View.GONE);
+                                else
+                                    mEndHandle.setVisibility(View.GONE);
+                            }
+
+                        } catch(Exception e) {}
+                    }
+                });
+            } else if (event.equals("TextSelection:PositionHandles")) {
+                final JSONArray positions = message.getJSONArray("positions");
+                LibreOfficeMainActivity.mAppContext.mMainHandler.post(new Runnable() {
+                    public void run() {
+                        try {
+                            for (int i=0; i < positions.length(); i++) {
+                                JSONObject position = positions.getJSONObject(i);
+                                String handle = position.getString("handle");
+                                int left = position.getInt("left");
+                                int top = position.getInt("top");
+
+                                if (handle.equals("START"))
+                                    mStartHandle.positionFromGecko(left, top);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.positionFromGecko(left, top);
+                                else
+                                    mEndHandle.positionFromGecko(left, top);
+                            }
+                        } catch (Exception e) { }
+                    }
+                });
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
+        }
+    }
+
+    @Override
+    public void draw(final RenderContext context) {
+        // cache the relevant values from the context and bail out if they are the same. we do this
+        // because this draw function gets called a lot (once per compositor frame) and we want to
+        // avoid doing a lot of extra work in cases where it's not needed.
+        if (FloatUtils.fuzzyEquals(mViewLeft, context.viewport.left)
+                && FloatUtils.fuzzyEquals(mViewTop, context.viewport.top)
+                && FloatUtils.fuzzyEquals(mViewZoom, context.zoomFactor)) {
+            return;
+        }
+        mViewLeft = context.viewport.left;
+        mViewTop = context.viewport.top;
+        mViewZoom = context.zoomFactor;
+
+        LOKitShell.getMainHandler().post(new Runnable() {
+            public void run() {
+                mStartHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
+                mMiddleHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
+                mEndHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
+            }
+        });
+    }
+
+    public void showHandle(final String handleType) {
+        LOKitShell.getMainHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    TextSelectionHandle handle;
+                    if (handleType.equals("START"))
+                        handle = mStartHandle;
+                    else if (handleType.equals("MIDDLE"))
+                        handle = mMiddleHandle;
+                    else
+                        handle = mEndHandle;
+
+                    handle.setVisibility(View.VISIBLE);
+
+                    mViewLeft = 0.0f;
+                    mViewTop = 0.0f;
+                    mViewZoom = 0.0f;
+                    LayerView layerView = LOKitShell.getLayerView();
+                    if (layerView != null) {
+                        layerView.addLayer(TextSelection.this);
+                    }
+                } catch (Exception e) {
+                }
+            }
+        });
+    }
+
+    public void positionHandle(final String handleType, final RectF position) {
+        LOKitShell.getMainHandler().post(new Runnable() {
+            public void run() {
+                try {
+                    TextSelectionHandle handle;
+                    if (handleType.equals("START"))
+                        handle = mStartHandle;
+                    else if (handleType.equals("MIDDLE"))
+                        handle = mMiddleHandle;
+                    else
+                        handle = mEndHandle;
+
+                    handle.positionFromGecko((int) position.left, (int) position.top);
+                } catch (Exception e) { }
+            }
+        });
+    }
+}
diff --git a/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java
new file mode 100644
index 0000000..92ca9d4
--- /dev/null
+++ b/android/experimental/LOAndroid3/src/java/org/mozilla/gecko/TextSelectionHandle.java
@@ -0,0 +1,170 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import org.libreoffice.LOKitShell;
+import org.libreoffice.R;
+import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
+import org.mozilla.gecko.gfx.LayerView;
+
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+public class TextSelectionHandle extends ImageView implements View.OnTouchListener {
+    private static final String LOGTAG = "GeckoTextSelectionHandle";
+
+    private enum HandleType { START, MIDDLE, END };
+
+    private final HandleType mHandleType;
+    private final int mWidth;
+    private final int mHeight;
+    private final int mShadow;
+
+    private int mLeft;
+    private int mTop;
+    private PointF mGeckoPoint;
+    private int mTouchStartX;
+    private int mTouchStartY;
+
+    private RelativeLayout.LayoutParams mLayoutParams;
+
+    public TextSelectionHandle(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setOnTouchListener(this);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextSelectionHandle);
+        int handleType = a.getInt(R.styleable.TextSelectionHandle_handleType, 0x01);
+
+        if (handleType == 0x01)
+            mHandleType = HandleType.START;
+        else if (handleType == 0x02)
+            mHandleType = HandleType.MIDDLE;
+        else
+            mHandleType = HandleType.END;
+
+        mGeckoPoint = new PointF(0.0f, 0.0f);
+
+        mWidth = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_width);
+        mHeight = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_height);
+        mShadow = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_shadow);
+    }
+
+    public boolean onTouch(View v, MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                mTouchStartX = Math.round(event.getX());
+                mTouchStartY = Math.round(event.getY());
+                break;
+            }
+            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()));
+                break;
+            }
+        }
+        return true;
+    }
+
+    private void move(int newX, int newY) {
+        mLeft = mLeft + newX - mTouchStartX;
+        mTop = mTop + newY - mTouchStartY;
+
+        LayerView layerView = LOKitShell.getLayerView();
+        if (layerView == null) {
+            Log.e(LOGTAG, "Can't move selection because layerView is null");
+            return;
+        }
+        // Send x coordinate on the right side of the start handle, left side of the end handle.
+        float left = (float) mLeft;
+        if (mHandleType.equals(HandleType.START))
+            left +=  mWidth - mShadow;
+        else if (mHandleType.equals(HandleType.MIDDLE))
+            left +=  (float) ((mWidth - mShadow) / 2);
+        else
+            left += mShadow;
+
+        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()));
+
+        setLayoutPosition();
+    }
+
+    void positionFromGecko(int left, int top) {
+        Log.i(LOGTAG, "positionFromGecko: " + left + " " + top);
+        LayerView layerView = LOKitShell.getLayerView();
+        if (layerView == null) {
+            Log.e(LOGTAG, "Can't position handle because layerView is null");
+            return;
+        }
+
+        mGeckoPoint = new PointF((float) left, (float) top);
+        ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
+        repositionWithViewport(metrics.viewportRectLeft, metrics.viewportRectTop, metrics.zoomFactor);
+    }
+
+    void repositionWithViewport(float x, float y, float zoom) {
+        PointF viewPoint = new PointF((mGeckoPoint.x * zoom) - x,
+                (mGeckoPoint.y * zoom) - y);
+
+        mLeft = Math.round(viewPoint.x);
+        if (mHandleType.equals(HandleType.START))
+            mLeft -=  mWidth - mShadow;
+        else if (mHandleType.equals(HandleType.MIDDLE))
+            mLeft -=  (float) ((mWidth - mShadow) / 2);
+        else
+            mLeft -= mShadow;
+
+        mTop = Math.round(viewPoint.y);
+
+        setLayoutPosition();
+    }
+
+    private void setLayoutPosition() {
+        if (mLayoutParams == null) {
+            mLayoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
+            // Set negative right/bottom margins so that the handles can be dragged outside of
+            // the content area (if they are dragged to the left/top, the dyanmic margins set
+            // below will take care of that).
+            mLayoutParams.rightMargin = 0 - mWidth;
+            mLayoutParams.bottomMargin = 0 - mHeight;
+        }
+
+        mLayoutParams.leftMargin = mLeft;
+        mLayoutParams.topMargin = mTop;
+        setLayoutParams(mLayoutParams);
+    }
+}


More information about the Libreoffice-commits mailing list