summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java')
-rw-r--r--android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java278
1 files changed, 278 insertions, 0 deletions
diff --git a/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
new file mode 100644
index 000000000..98af7a955
--- /dev/null
+++ b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
@@ -0,0 +1,278 @@
+package org.libreoffice.overlay;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import androidx.core.view.GestureDetectorCompat;
+import android.util.AttributeSet;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.PopupWindow;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.libreoffice.LOEvent;
+import org.libreoffice.LOKitShell;
+import org.libreoffice.LibreOfficeMainActivity;
+import org.libreoffice.canvas.CalcHeaderCell;
+import org.libreoffice.kit.Document;
+import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
+import org.mozilla.gecko.gfx.LayerView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import static org.libreoffice.SearchController.addProperty;
+
+public class CalcHeadersView extends View {
+ private static final String LOGTAG = CalcHeadersView.class.getSimpleName();
+
+ private boolean mInitialized;
+ private LayerView mLayerView;
+ private boolean mIsRow; // true if this is for row headers, false for column
+ private ArrayList<String> mLabels;
+ private ArrayList<Float> mDimens;
+ private RectF mCellCursorRect;
+ private boolean mPendingRowOrColumnSelectionToShowUp;
+ private GestureDetectorCompat mDetector;
+ private PopupWindow mPopupWindow;
+ private int mPrevScrollIndex = -1;
+
+ public CalcHeadersView(Context context) {
+ super(context);
+ }
+
+ public CalcHeadersView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CalcHeadersView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void initialize(LayerView layerView, boolean isRow) {
+ if (!mInitialized) {
+ mLayerView = layerView;
+ mIsRow = isRow;
+
+ LOKitShell.getMainHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ mDetector = new GestureDetectorCompat(getContext(), new HeaderGestureListener());
+ }
+ });
+
+ setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mPrevScrollIndex = -1; // clear mPrevScrollIndex to default
+ }
+ return mDetector.onTouchEvent(event);
+ }
+ });
+
+ mInitialized = true;
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mInitialized && mDimens != null && mLabels != null) {
+ updateHeaders(canvas);
+ }
+ }
+
+ private void updateHeaders(Canvas canvas) {
+ ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics();
+ float zoom = metrics.getZoomFactor();
+ PointF origin = metrics.getOrigin();
+
+ // Draw headers
+ boolean inRangeOfVisibleHeaders = false; // a helper variable for skipping unnecessary onDraw()'s
+ float top,bottom,left,right;
+ for (int i = 1; i < mLabels.size(); i++) {
+ if (mDimens.get(i).equals(mDimens.get(i-1))) continue;
+ if (mIsRow) {
+ top = -origin.y + zoom*mDimens.get(i-1);
+ bottom = -origin.y + zoom*mDimens.get(i);
+ if (top <= getHeight() && bottom >= 0) {
+ inRangeOfVisibleHeaders = true;
+ boolean isSelected = mCellCursorRect != null && bottom > mCellCursorRect.top - origin.y && top < mCellCursorRect.bottom - origin.y;
+ new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i), isSelected).onDraw(canvas);
+ } else {
+ if (inRangeOfVisibleHeaders) {
+ break;
+ }
+ }
+ } else {
+ left = -origin.x + zoom*mDimens.get(i-1);
+ right = -origin.x + zoom*mDimens.get(i);
+ if (left <= getWidth() && right >= 0) {
+ boolean isSelected = mCellCursorRect != null && right > mCellCursorRect.left - origin.x && left < mCellCursorRect.right - origin.x;
+ new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i), isSelected).onDraw(canvas);
+ } else {
+ if (inRangeOfVisibleHeaders) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Handle a single tap event on a header cell.
+ * Selects whole row/column.
+ */
+ private void highlightRowOrColumn(PointF point, boolean shift) {
+ int index = getIndexFromPointOfTouch(point);
+ try {
+ JSONObject rootJson = new JSONObject();
+ if (shift) {
+ addProperty(rootJson, "Modifier", "unsigned short",
+ String.valueOf(Document.KEYBOARD_MODIFIER_SHIFT));
+ } else {
+ addProperty(rootJson, "Modifier", "unsigned short", "0");
+ }
+ if (mIsRow) {
+ addProperty(rootJson, "Row", "unsigned short", String.valueOf(index));
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectRow", rootJson.toString()));
+ } else {
+ addProperty(rootJson, "Col", "unsigned short", String.valueOf(index));
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectColumn", rootJson.toString()));
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ // At this point, InvalidationHandler.java will have received two callbacks.
+ // One is for text selection (first) and the other for cell selection (second).
+ // The second will override the first on headers which is not wanted.
+ // setPendingRowOrColumnSelectionToShowUp(true) will skip the second call.
+ setPendingRowOrColumnSelectionToShowUp(true);
+ }
+
+ public int getIndexFromPointOfTouch(PointF point) {
+ int searchedIndex, index;
+ ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics();
+ float zoom = metrics.getZoomFactor();
+ PointF origin = metrics.getOrigin();
+ if (mIsRow) {
+ searchedIndex = Collections.binarySearch(mDimens, (point.y+origin.y)/zoom);
+ } else {
+ searchedIndex = Collections.binarySearch(mDimens, (point.x+origin.x)/zoom);
+ }
+ // converting searched index to real index on headers
+ if (searchedIndex < 0) {
+ index = - searchedIndex - 2;
+ } else {
+ index = searchedIndex;
+ }
+ return index;
+ }
+
+ public void setPendingRowOrColumnSelectionToShowUp(boolean b) {
+ mPendingRowOrColumnSelectionToShowUp = b;
+ }
+
+ public boolean pendingRowOrColumnSelectionToShowUp() {
+ return mPendingRowOrColumnSelectionToShowUp;
+ }
+
+ public void setHeaders(ArrayList<String> labels, ArrayList<Float> dimens) {
+ mLabels = labels;
+ mDimens = dimens;
+ }
+
+ public void setHeaderSelection(RectF cellCursorRect) {
+ mCellCursorRect = cellCursorRect;
+ }
+
+ public void showHeaderPopup(PointF point) {
+ if (mPopupWindow == null ||
+ !LibreOfficeMainActivity.isExperimentalMode()) return;
+ if (mIsRow) {
+ mPopupWindow.showAsDropDown(this, getWidth()*3/2, -getHeight()+(int)point.y);
+ } else {
+ mPopupWindow.showAsDropDown(this, (int)point.x, getHeight()/2);
+ }
+ }
+
+ public void dismissPopupWindow() {
+ if (mPopupWindow == null) return;
+ mPopupWindow.dismiss();
+ }
+
+ public void setHeaderPopupWindow(PopupWindow popupWindow) {
+ if (mPopupWindow != null) return;
+ mPopupWindow = popupWindow;
+ }
+
+ public void sendOptimalLengthRequest(String text) {
+ JSONObject rootJson = new JSONObject();
+ if (mIsRow) {
+ try {
+ addProperty(rootJson, "aExtraHeight", "unsigned short", text);
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SetOptimalRowHeight", rootJson.toString()));
+ } catch (JSONException ex) {
+ ex.printStackTrace();
+ }
+ } else {
+ try {
+ addProperty(rootJson, "aExtraWidth", "unsigned short", text);
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SetOptimalColumnWidth", rootJson.toString()));
+ } catch (JSONException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private class HeaderGestureListener extends SimpleOnGestureListener {
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ PointF pointOfTouch = new PointF(e.getX(), e.getY());
+ highlightRowOrColumn(pointOfTouch, false);
+ showHeaderPopup(pointOfTouch);
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ PointF point2 = new PointF(e2.getX(), e2.getY());
+ if (mPrevScrollIndex != getIndexFromPointOfTouch(point2)) {
+ mPrevScrollIndex = getIndexFromPointOfTouch(point2);
+ highlightRowOrColumn(point2, true);
+ dismissPopupWindow();
+ showHeaderPopup(point2);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ PointF pointOfTouch = new PointF(e.getX(), e.getY());
+ highlightRowOrColumn(pointOfTouch, false);
+ if (mIsRow) {
+ JSONObject rootJson = new JSONObject();
+ try {
+ addProperty(rootJson, "aExtraHeight", "unsigned short", String.valueOf(0));
+ } catch (JSONException ex) {
+ ex.printStackTrace();
+ }
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND,".uno:SetOptimalRowHeight", rootJson.toString()));
+ } else {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND,".uno:SetOptimalColumnWidthDirect"));
+ }
+ showHeaderPopup(pointOfTouch);
+ return true;
+ }
+ }
+}