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