diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java')
-rw-r--r-- | android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java | 278 |
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; + } + } +} |