1 package org.libreoffice.overlay;
2 
3 import android.content.Context;
4 import android.graphics.PointF;
5 import android.graphics.RectF;
6 import android.graphics.drawable.ColorDrawable;
7 import android.support.design.widget.Snackbar;
8 import android.util.Log;
9 import android.view.KeyEvent;
10 import android.view.Gravity;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.view.ViewGroup.LayoutParams;
14 import android.view.inputmethod.EditorInfo;
15 import android.widget.EditText;
16 import android.widget.Button;
17 import android.widget.PopupWindow;
18 import android.widget.TextView;
19 
20 import org.json.JSONArray;
21 import org.json.JSONException;
22 import org.json.JSONObject;
23 import org.libreoffice.LOEvent;
24 import org.libreoffice.LOKitShell;
25 import org.libreoffice.LibreOfficeMainActivity;
26 import org.libreoffice.R;
27 import org.mozilla.gecko.gfx.LayerView;
28 
29 import java.math.BigDecimal;
30 import java.util.ArrayList;
31 
32 import static org.libreoffice.SearchController.addProperty;
33 
34 public class CalcHeadersController {
35     private static final String LOGTAG = CalcHeadersController.class.getSimpleName();
36 
37     private final CalcHeadersView mCalcRowHeadersView;
38     private final CalcHeadersView mCalcColumnHeadersView;
39 
40     private LibreOfficeMainActivity mContext;
41 
CalcHeadersController(LibreOfficeMainActivity context, final LayerView layerView)42     public CalcHeadersController(LibreOfficeMainActivity context, final LayerView layerView) {
43         mContext = context;
44         mContext.getDocumentOverlay().setCalcHeadersController(this);
45         mCalcRowHeadersView = context.findViewById(R.id.calc_header_row);
46         mCalcColumnHeadersView = context.findViewById(R.id.calc_header_column);
47         if (mCalcColumnHeadersView == null || mCalcRowHeadersView == null) {
48             Log.e(LOGTAG, "Failed to initialize Calc headers - View is null");
49         } else {
50             mCalcRowHeadersView.initialize(layerView, true);
51             mCalcColumnHeadersView.initialize(layerView, false);
52         }
53         LOKitShell.sendEvent(new LOEvent(LOEvent.UPDATE_CALC_HEADERS));
54         context.findViewById(R.id.calc_header_top_left).setOnClickListener(new View.OnClickListener() {
55             @Override
56             public void onClick(View v) {
57                 LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SelectAll"));
58                 if (mCalcColumnHeadersView == null) return;
59                 mCalcColumnHeadersView.showHeaderPopup(new PointF());
60             }
61         });
62         ((EditText)context.findViewById(R.id.calc_address)).setOnEditorActionListener(new TextView.OnEditorActionListener() {
63             @Override
64             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
65                 if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_GO) {
66                     String text = v.getText().toString();
67                     JSONObject rootJson = new JSONObject();
68                     try {
69                         addProperty(rootJson, "ToPoint", "string", text);
70                     } catch (JSONException e) {
71                         e.printStackTrace();
72                     }
73                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:GoToCell", rootJson.toString()));
74                     mContext.hideSoftKeyboard();
75                     layerView.requestFocus();
76                 }
77                 return true;
78             }
79         });
80         ((EditText)context.findViewById(R.id.calc_formula)).setOnEditorActionListener(new TextView.OnEditorActionListener() {
81             @Override
82             public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
83                 if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_GO) {
84                     String text = v.getText().toString();
85                     JSONObject rootJson = new JSONObject();
86                     try {
87                         addProperty(rootJson, "StringName", "string", text);
88                         addProperty(rootJson, "DontCommit", "boolean", String.valueOf(false));
89                     } catch (JSONException e) {
90                         e.printStackTrace();
91                     }
92                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:EnterString", rootJson.toString()));
93                     mContext.hideSoftKeyboard();
94                     layerView.requestFocus();
95                     mContext.setDocumentChanged(true);
96                 }
97                 return true;
98             }
99         });
100         // manually select A1 for address bar and formula bar to update when calc first opens
101         JSONObject rootJson = new JSONObject();
102         try {
103             addProperty(rootJson, "ToPoint", "string", "A1");
104         } catch (JSONException e) {
105             e.printStackTrace();
106         }
107         LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:GoToCell", rootJson.toString()));
108     }
109 
setupHeaderPopupView()110     public void setupHeaderPopupView() {
111         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
112         String[] rowOrColumn = {"Row","Column"};
113         CalcHeadersView[] headersViews= {mCalcRowHeadersView, mCalcColumnHeadersView};
114         for (int i = 0; i < rowOrColumn.length; i++) {
115             // create popup window
116             final String tempName = rowOrColumn[i];
117             final CalcHeadersView tempView = headersViews[i];
118             final View headerPopupView = inflater.inflate(R.layout.calc_header_popup, null);
119             final PopupWindow popupWindow = new PopupWindow(headerPopupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
120             popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
121                 @Override
122                 public void onDismiss() {
123                     headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_dialog).setVisibility(View.GONE);
124                     popupWindow.setFocusable(false);
125                 }
126             });
127             popupWindow.setOutsideTouchable(true);
128             popupWindow.setBackgroundDrawable(new ColorDrawable());
129             popupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
130             tempView.setHeaderPopupWindow(popupWindow);
131             // set up child views in the popup window
132             headerPopupView.findViewById(R.id.calc_header_popup_insert).setOnClickListener(new View.OnClickListener() {
133                 @Override
134                 public void onClick(View v) {
135                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Insert"+tempName+"s"));
136                     tempView.dismissPopupWindow();
137                     mContext.setDocumentChanged(true);
138                 }
139             });
140             headerPopupView.findViewById(R.id.calc_header_popup_delete).setOnClickListener(new View.OnClickListener() {
141                 @Override
142                 public void onClick(View v) {
143                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Delete"+tempName+"s"));
144                     tempView.dismissPopupWindow();
145                     mContext.setDocumentChanged(true);
146                 }
147             });
148             headerPopupView.findViewById(R.id.calc_header_popup_hide).setOnClickListener(new View.OnClickListener() {
149                 @Override
150                 public void onClick(View v) {
151                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Hide"+tempName));
152                     tempView.dismissPopupWindow();
153                     mContext.setDocumentChanged(true);
154                 }
155             });
156             headerPopupView.findViewById(R.id.calc_header_popup_show).setOnClickListener(new View.OnClickListener() {
157                 @Override
158                 public void onClick(View v) {
159                     LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Show"+tempName));
160                     tempView.dismissPopupWindow();
161                     mContext.setDocumentChanged(true);
162                 }
163             });
164             headerPopupView.findViewById(R.id.calc_header_popup_optimal_length).setOnClickListener(new View.OnClickListener() {
165                 @Override
166                 public void onClick(View v) {
167                     View view = headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_dialog);
168                     if (view.getVisibility() == View.VISIBLE) {
169                         view.setVisibility(View.GONE);
170                         popupWindow.setFocusable(false);
171                         popupWindow.update();
172                     } else {
173                         popupWindow.dismiss();
174                         view.setVisibility(View.VISIBLE);
175                         popupWindow.setFocusable(true);
176                         popupWindow.showAtLocation(tempView, Gravity.CENTER, 0, 0);
177                         LOKitShell.getMainHandler().post(new Runnable() {
178                             @Override
179                             public void run() {
180                                 Snackbar.make(tempView, R.string.calc_alert_double_click_optimal_length, Snackbar.LENGTH_LONG).show();
181                             }
182                         });
183                     }
184                 }
185             });
186             headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_button).setOnClickListener(new View.OnClickListener() {
187                 @Override
188                 public void onClick(View v) {
189                     String text = ((EditText)headerPopupView.findViewById(R.id.calc_header_popup_optimal_length_text)).getText().toString();
190                     tempView.sendOptimalLengthRequest(text);
191                     tempView.dismissPopupWindow();
192                     mContext.setDocumentChanged(true);
193                 }
194             });
195             headerPopupView.findViewById(R.id.calc_header_popup_adjust_length).setOnClickListener(new View.OnClickListener() {
196                 @Override
197                 public void onClick(View v) {
198                     mContext.getDocumentOverlay().showAdjustLengthLine(tempView == mCalcRowHeadersView, tempView);
199                     tempView.dismissPopupWindow();
200                     mContext.setDocumentChanged(true);
201                 }
202             });
203             ((Button)headerPopupView.findViewById(R.id.calc_header_popup_adjust_length))
204                     .setText(tempView == mCalcRowHeadersView ? R.string.calc_adjust_height : R.string.calc_adjust_width);
205             ((Button)headerPopupView.findViewById(R.id.calc_header_popup_optimal_length))
206                     .setText(tempView == mCalcRowHeadersView ? R.string.calc_optimal_height : R.string.calc_optimal_width);
207 
208         }
209     }
210 
setHeaders(String headers)211     public void setHeaders(String headers) {
212         HeaderInfo parsedHeaders = parseHeaderInfo(headers);
213         if (parsedHeaders != null) {
214             mCalcRowHeadersView.setHeaders(parsedHeaders.rowLabels, parsedHeaders.rowDimens);
215             mCalcColumnHeadersView.setHeaders(parsedHeaders.columnLabels, parsedHeaders.columnDimens);
216             showHeaders();
217         } else {
218             Log.e(LOGTAG, "Parse header info JSON failed.");
219         }
220     }
221 
showHeaders()222     public void showHeaders() {
223         LOKitShell.getMainHandler().post(new Runnable() {
224             @Override
225             public void run() {
226                 mCalcColumnHeadersView.invalidate();
227                 mCalcRowHeadersView.invalidate();
228             }
229         });
230     }
231 
parseHeaderInfo(String headers)232     private HeaderInfo parseHeaderInfo(String headers) {
233         HeaderInfo headerInfo = new HeaderInfo();
234         try {
235             JSONObject collectiveResult = new JSONObject(headers);
236             JSONArray rowResult = collectiveResult.getJSONArray("rows");
237             for (int i = 0; i < rowResult.length(); i++) {
238                 headerInfo.rowLabels.add(rowResult.getJSONObject(i).getString("text"));
239                 headerInfo.rowDimens.add(BigDecimal.valueOf(rowResult.getJSONObject(i).getLong("size")).floatValue());
240             }
241             JSONArray columnResult = collectiveResult.getJSONArray("columns");
242             for (int i = 0; i < columnResult.length(); i++) {
243                 headerInfo.columnLabels.add(columnResult.getJSONObject(i).getString("text"));
244                 headerInfo.columnDimens.add(BigDecimal.valueOf(columnResult.getJSONObject(i).getLong("size")).floatValue());
245             }
246             return headerInfo;
247         } catch (JSONException e) {
248             e.printStackTrace();
249         }
250         return null;
251     }
252 
showHeaderSelection(RectF cellCursorRect)253     public void showHeaderSelection(RectF cellCursorRect) {
254         mCalcRowHeadersView.setHeaderSelection(cellCursorRect);
255         mCalcColumnHeadersView.setHeaderSelection(cellCursorRect);
256         showHeaders();
257     }
258 
setPendingRowOrColumnSelectionToShowUp(boolean b)259     public void setPendingRowOrColumnSelectionToShowUp(boolean b) {
260         mCalcRowHeadersView.setPendingRowOrColumnSelectionToShowUp(b);
261         mCalcColumnHeadersView.setPendingRowOrColumnSelectionToShowUp(b);
262     }
263 
pendingRowOrColumnSelectionToShowUp()264     public boolean pendingRowOrColumnSelectionToShowUp() {
265         return mCalcColumnHeadersView.pendingRowOrColumnSelectionToShowUp()
266                 || mCalcRowHeadersView.pendingRowOrColumnSelectionToShowUp();
267     }
268 
269     private class HeaderInfo {
270         ArrayList<String> rowLabels;
271         ArrayList<Float> rowDimens;
272         ArrayList<String> columnLabels;
273         ArrayList<Float> columnDimens;
HeaderInfo()274         private HeaderInfo() {
275             rowLabels = new ArrayList<String>();
276             rowDimens = new ArrayList<Float>();
277             columnDimens = new ArrayList<Float>();
278             columnLabels = new ArrayList<String>();
279         }
280     }
281 }
282