LibreOffice Module android (master) 1
CalcHeadersView.java
Go to the documentation of this file.
1package org.libreoffice.overlay;
2
3import android.content.Context;
4import android.graphics.Canvas;
5import android.graphics.PointF;
6import android.graphics.RectF;
7import androidx.core.view.GestureDetectorCompat;
8import android.util.AttributeSet;
9import android.view.GestureDetector.SimpleOnGestureListener;
10import android.view.MotionEvent;
11import android.view.View;
12import android.widget.PopupWindow;
13
14import org.json.JSONException;
15import org.json.JSONObject;
20import org.libreoffice.kit.Document;
23
24import java.util.ArrayList;
25import java.util.Collections;
26
28
29public class CalcHeadersView extends View {
30 private static final String LOGTAG = CalcHeadersView.class.getSimpleName();
31
32 private boolean mInitialized;
34 private boolean mIsRow; // true if this is for row headers, false for column
35 private ArrayList<String> mLabels;
36 private ArrayList<Float> mDimens;
37 private RectF mCellCursorRect;
39 private GestureDetectorCompat mDetector;
40 private PopupWindow mPopupWindow;
41 private int mPrevScrollIndex = -1;
42
43 public CalcHeadersView(Context context) {
44 super(context);
45 }
46
47 public CalcHeadersView(Context context, AttributeSet attrs) {
48 super(context, attrs);
49 }
50
51 public CalcHeadersView(Context context, AttributeSet attrs, int defStyleAttr) {
52 super(context, attrs, defStyleAttr);
53 }
54
55 public void initialize(LayerView layerView, boolean isRow) {
56 if (!mInitialized) {
57 mLayerView = layerView;
58 mIsRow = isRow;
59
60 LOKitShell.getMainHandler().post(new Runnable() {
61 @Override
62 public void run() {
63 mDetector = new GestureDetectorCompat(getContext(), new HeaderGestureListener());
64 }
65 });
66
67 setOnTouchListener(new View.OnTouchListener() {
68 @Override
69 public boolean onTouch(View v, MotionEvent event) {
70 if (event.getActionMasked() == MotionEvent.ACTION_UP) {
71 mPrevScrollIndex = -1; // clear mPrevScrollIndex to default
72 }
73 return mDetector.onTouchEvent(event);
74 }
75 });
76
77 mInitialized = true;
78 }
79 }
80
81 @Override
82 protected void onDraw(Canvas canvas) {
83 super.onDraw(canvas);
84 if (mInitialized && mDimens != null && mLabels != null) {
85 updateHeaders(canvas);
86 }
87 }
88
89 private void updateHeaders(Canvas canvas) {
90 ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics();
91 float zoom = metrics.getZoomFactor();
92 PointF origin = metrics.getOrigin();
93
94 // Draw headers
95 boolean inRangeOfVisibleHeaders = false; // a helper variable for skipping unnecessary onDraw()'s
96 float top,bottom,left,right;
97 for (int i = 1; i < mLabels.size(); i++) {
98 if (mDimens.get(i).equals(mDimens.get(i-1))) continue;
99 if (mIsRow) {
100 top = -origin.y + zoom*mDimens.get(i-1);
101 bottom = -origin.y + zoom*mDimens.get(i);
102 if (top <= getHeight() && bottom >= 0) {
103 inRangeOfVisibleHeaders = true;
104 boolean isSelected = mCellCursorRect != null && bottom > mCellCursorRect.top - origin.y && top < mCellCursorRect.bottom - origin.y;
105 new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i), isSelected).onDraw(canvas);
106 } else {
107 if (inRangeOfVisibleHeaders) {
108 break;
109 }
110 }
111 } else {
112 left = -origin.x + zoom*mDimens.get(i-1);
113 right = -origin.x + zoom*mDimens.get(i);
114 if (left <= getWidth() && right >= 0) {
115 boolean isSelected = mCellCursorRect != null && right > mCellCursorRect.left - origin.x && left < mCellCursorRect.right - origin.x;
116 new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i), isSelected).onDraw(canvas);
117 } else {
118 if (inRangeOfVisibleHeaders) {
119 break;
120 }
121 }
122 }
123 }
124 }
125
130 private void highlightRowOrColumn(PointF point, boolean shift) {
131 int index = getIndexFromPointOfTouch(point);
132 try {
133 JSONObject rootJson = new JSONObject();
134 if (shift) {
135 addProperty(rootJson, "Modifier", "unsigned short",
136 String.valueOf(Document.KEYBOARD_MODIFIER_SHIFT));
137 } else {
138 addProperty(rootJson, "Modifier", "unsigned short", "0");
139 }
140 if (mIsRow) {
141 addProperty(rootJson, "Row", "unsigned short", String.valueOf(index));
142 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectRow", rootJson.toString()));
143 } else {
144 addProperty(rootJson, "Col", "unsigned short", String.valueOf(index));
145 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectColumn", rootJson.toString()));
146 }
147 } catch (JSONException e) {
148 e.printStackTrace();
149 }
150 // At this point, InvalidationHandler.java will have received two callbacks.
151 // One is for text selection (first) and the other for cell selection (second).
152 // The second will override the first on headers which is not wanted.
153 // setPendingRowOrColumnSelectionToShowUp(true) will skip the second call.
154 setPendingRowOrColumnSelectionToShowUp(true);
155 }
156
157 public int getIndexFromPointOfTouch(PointF point) {
158 int searchedIndex, index;
159 ImmutableViewportMetrics metrics = mLayerView.getViewportMetrics();
160 float zoom = metrics.getZoomFactor();
161 PointF origin = metrics.getOrigin();
162 if (mIsRow) {
163 searchedIndex = Collections.binarySearch(mDimens, (point.y+origin.y)/zoom);
164 } else {
165 searchedIndex = Collections.binarySearch(mDimens, (point.x+origin.x)/zoom);
166 }
167 // converting searched index to real index on headers
168 if (searchedIndex < 0) {
169 index = - searchedIndex - 2;
170 } else {
171 index = searchedIndex;
172 }
173 return index;
174 }
175
177 mPendingRowOrColumnSelectionToShowUp = b;
178 }
179
181 return mPendingRowOrColumnSelectionToShowUp;
182 }
183
184 public void setHeaders(ArrayList<String> labels, ArrayList<Float> dimens) {
185 mLabels = labels;
186 mDimens = dimens;
187 }
188
189 public void setHeaderSelection(RectF cellCursorRect) {
190 mCellCursorRect = cellCursorRect;
191 }
192
193 public void showHeaderPopup(PointF point) {
194 if (mPopupWindow == null ||
196 if (mIsRow) {
197 mPopupWindow.showAsDropDown(this, getWidth()*3/2, -getHeight()+(int)point.y);
198 } else {
199 mPopupWindow.showAsDropDown(this, (int)point.x, getHeight()/2);
200 }
201 }
202
203 public void dismissPopupWindow() {
204 if (mPopupWindow == null) return;
205 mPopupWindow.dismiss();
206 }
207
208 public void setHeaderPopupWindow(PopupWindow popupWindow) {
209 if (mPopupWindow != null) return;
210 mPopupWindow = popupWindow;
211 }
212
213 public void sendOptimalLengthRequest(String text) {
214 JSONObject rootJson = new JSONObject();
215 if (mIsRow) {
216 try {
217 addProperty(rootJson, "aExtraHeight", "unsigned short", text);
218 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SetOptimalRowHeight", rootJson.toString()));
219 } catch (JSONException ex) {
220 ex.printStackTrace();
221 }
222 } else {
223 try {
224 addProperty(rootJson, "aExtraWidth", "unsigned short", text);
225 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SetOptimalColumnWidth", rootJson.toString()));
226 } catch (JSONException ex) {
227 ex.printStackTrace();
228 }
229 }
230 }
231
232 private class HeaderGestureListener extends SimpleOnGestureListener {
233
234 @Override
235 public boolean onDown(MotionEvent e) {
236 return true;
237 }
238
239 @Override
240 public boolean onSingleTapConfirmed(MotionEvent e) {
241 PointF pointOfTouch = new PointF(e.getX(), e.getY());
242 highlightRowOrColumn(pointOfTouch, false);
243 showHeaderPopup(pointOfTouch);
244 return true;
245 }
246
247 @Override
248 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
249 PointF point2 = new PointF(e2.getX(), e2.getY());
250 if (mPrevScrollIndex != getIndexFromPointOfTouch(point2)) {
251 mPrevScrollIndex = getIndexFromPointOfTouch(point2);
252 highlightRowOrColumn(point2, true);
253 dismissPopupWindow();
254 showHeaderPopup(point2);
255 }
256 return true;
257 }
258
259 @Override
260 public boolean onDoubleTap(MotionEvent e) {
261 PointF pointOfTouch = new PointF(e.getX(), e.getY());
262 highlightRowOrColumn(pointOfTouch, false);
263 if (mIsRow) {
264 JSONObject rootJson = new JSONObject();
265 try {
266 addProperty(rootJson, "aExtraHeight", "unsigned short", String.valueOf(0));
267 } catch (JSONException ex) {
268 ex.printStackTrace();
269 }
270 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND,".uno:SetOptimalRowHeight", rootJson.toString()));
271 } else {
272 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND,".uno:SetOptimalColumnWidthDirect"));
273 }
274 showHeaderPopup(pointOfTouch);
275 return true;
276 }
277 }
278}
Events and data that is queued and processed by LOKitThread.
Definition: LOEvent.java:21
static final int UNO_COMMAND
Definition: LOEvent.java:35
Common static LOKit functions, functions to send events.
Definition: LOKitShell.java:26
static void sendEvent(LOEvent event)
Make sure LOKitThread is running and send event to it.
Definition: LOKitShell.java:72
static Handler getMainHandler()
Definition: LOKitShell.java:36
Main activity of the LibreOffice App.
static void addProperty(JSONObject json, String parentValue, String type, String value)
void onDraw(Canvas canvas)
Called inside draw if the element is visible.
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
void setHeaders(ArrayList< String > labels, ArrayList< Float > dimens)
void initialize(LayerView layerView, boolean isRow)
CalcHeadersView(Context context, AttributeSet attrs, int defStyleAttr)
void setHeaderPopupWindow(PopupWindow popupWindow)
CalcHeadersView(Context context, AttributeSet attrs)
void setHeaderSelection(RectF cellCursorRect)
void highlightRowOrColumn(PointF point, boolean shift)
Handle a single tap event on a header cell.
ImmutableViewportMetrics are used to store the viewport metrics in way that we can access a version o...
A view rendered by the layer compositor.
Definition: LayerView.java:41
OString right
OString top
OString bottom
def text(shape, orig_st)
def run(arg=None, arg2=-1)
def point()
int i
index
int shift
sal_uInt64 left