package com.numix.calculator.view; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.res.Resources; import android.text.Editable.Factory; import android.text.TextUtils; import android.text.method.KeyListener; import android.util.AttributeSet; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Toast; import com.numix.calculator.Logic; import com.numix.calculator.MutableString; import com.numix.calculator.R; public class AdvancedDisplay extends LinearLayout { private static final int CUT = 0; private static final int COPY = 1; private static final int PASTE = 2; private String[] mMenuItemsStrings; EditText mActiveEditText; KeyListener mKeyListener; Factory mFactory; Logic mLogic; public AdvancedDisplay(Context context) { this(context, null); } public AdvancedDisplay(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(HORIZONTAL); } @Override public void removeAllViews() { super.removeAllViews(); mActiveEditText = null; } @Override public void removeView(View view) { int index = getChildIndex(view); if(index == -1) return; super.removeViewAt(index); // Combine the 2 EditTexts on either side CalculatorEditText leftSide = (CalculatorEditText) getChildAt(index - 1); CalculatorEditText rightSide = (CalculatorEditText) getChildAt(index); int cursor = leftSide.getText().length(); leftSide.setText(leftSide.getText().toString() + rightSide.getText().toString()); super.removeViewAt(index); // Remove the second EditText leftSide.requestFocus(); leftSide.setSelection(cursor); } public int getChildIndex(View view) { for(int i = 0; i < getChildCount(); i++) { if(getChildAt(i) == view) return i; } return -1; } public String getText() { String text = ""; for(int i = 0; i < getChildCount(); i++) { text += getChildAt(i).toString(); } return text; } public void clear() { removeAllViews(); } View nextView(View currentView) { boolean foundCurrentView = false; for(int i = 0; i < getChildCount(); i++) { if(foundCurrentView) return getChildAt(i); else if(currentView == getChildAt(i)) foundCurrentView = true; } return getChildAt(0); } View previousView(View currentView) { boolean foundCurrentView = false; for(int i = getChildCount() - 1; i >= 0; i--) { if(foundCurrentView) return getChildAt(i); else if(currentView == getChildAt(i)) foundCurrentView = true; } return getChildAt(getChildCount() - 1); } public void insert(String delta) { if(mActiveEditText == null) { // don't allow leading operators if(Logic.isOperator(delta) && !delta.equals(String.valueOf(Logic.MINUS))) { return; } setText(delta); } else { if(CalculatorEditText.class.isInstance(getActiveEditText())) { // Logic to insert, split text if there's another view, etc final MutableString text = new MutableString(delta); while(!text.isEmpty()) { int cursor = getActiveEditText().getSelectionStart(); final int index = getChildIndex(getActiveEditText()); if(MatrixView.load(text, this, index + 1)) splitText(cursor, index, text); else if(MatrixTransposeView.load(text, this, index + 1)) { splitText(cursor, index, text); getChildAt(index + 2).requestFocus(); ((CalculatorEditText) getChildAt(index + 2)).setSelection(0); } else if(MatrixInverseView.load(text, this, index + 1)) { splitText(cursor, index, text); getChildAt(index + 2).requestFocus(); ((CalculatorEditText) getChildAt(index + 2)).setSelection(0); } else { // don't allow leading operators if(cursor == 0 && getActiveEditText() == getChildAt(0) && Logic.isOperator(delta) && !delta.equals(String.valueOf(Logic.MINUS))) { return; } getActiveEditText().getText().insert(cursor, text.subSequence(0, 1)); text.setText(text.substring(1, text.length())); } } } else { int cursor = getActiveEditText().getSelectionStart(); getActiveEditText().getText().insert(cursor, delta); } } } private void splitText(int cursor, int index, MutableString text) { final String leftText = getActiveEditText().getText().toString().substring(0, cursor); final String rightText = getActiveEditText().getText().toString().substring(cursor); getActiveEditText().setText(leftText); CalculatorEditText.load(rightText, this, index + 2); if(text.isEmpty()) { getChildAt(index + 1).requestFocus(); } else { getChildAt(index + 2).requestFocus(); ((CalculatorEditText) getChildAt(index + 2)).setSelection(0); } } public void setText(String text) { clear(); CalculatorEditText.load(this); getLastView().requestFocus(); final MutableString ms = new MutableString(text); while(!ms.isEmpty()) { if(MatrixView.load(ms, this)) continue; if(MatrixTransposeView.load(ms, this)) continue; if(MatrixInverseView.load(ms, this)) continue; // Append the next character to the trailing EditText ((CalculatorEditText) getLastView()).setText(((CalculatorEditText) getLastView()).getText() + ms.substring(0, 1)); ((CalculatorEditText) getLastView()).setSelection(((CalculatorEditText) getLastView()).length()); ms.setText(ms.substring(1, ms.length())); } getLastView().requestFocus(); } public void setKeyListener(KeyListener input) { mKeyListener = input; } public void setEditableFactory(Factory factory) { mFactory = factory; } public void setLogic(Logic logic) { mLogic = logic; } public EditText getActiveEditText() { return mActiveEditText; } public View getLastView() { if(getChildCount() == 0) return null; return getChildAt(getChildCount() - 1); } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); for(int i = 0; i < getChildCount(); i++) { getChildAt(i).setEnabled(enabled); } } @Override public boolean performLongClick() { showContextMenu(); return true; } private class MenuHandler implements MenuItem.OnMenuItemClickListener { public boolean onMenuItemClick(MenuItem item) { return onTextContextMenuItem(item.getTitle()); } } public boolean onTextContextMenuItem(CharSequence title) { boolean handled = false; if(TextUtils.equals(title, mMenuItemsStrings[CUT])) { cutContent(); handled = true; } else if(TextUtils.equals(title, mMenuItemsStrings[COPY])) { copyContent(); handled = true; } else if(TextUtils.equals(title, mMenuItemsStrings[PASTE])) { pasteContent(); handled = true; } return handled; } @Override public void onCreateContextMenu(ContextMenu menu) { MenuHandler handler = new MenuHandler(); if(mMenuItemsStrings == null) { Resources resources = getResources(); mMenuItemsStrings = new String[3]; mMenuItemsStrings[CUT] = resources.getString(android.R.string.cut); mMenuItemsStrings[COPY] = resources.getString(android.R.string.copy); mMenuItemsStrings[PASTE] = resources.getString(android.R.string.paste); } for(int i = 0; i < mMenuItemsStrings.length; i++) { menu.add(Menu.NONE, i, i, mMenuItemsStrings[i]).setOnMenuItemClickListener(handler); } if(getText().length() == 0) { menu.getItem(CUT).setVisible(false); menu.getItem(COPY).setVisible(false); } ClipData primaryClip = getPrimaryClip(); if(primaryClip == null || primaryClip.getItemCount() == 0 || !canPaste(primaryClip.getItemAt(0).coerceToText(getContext()))) { menu.getItem(PASTE).setVisible(false); } } private void setPrimaryClip(ClipData clip) { ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(clip); } private void copyContent() { final String text = getText(); ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(ClipData.newPlainText(null, text)); String toastText = String.format(getResources().getString(R.string.text_copied_toast), text); Toast.makeText(getContext(), toastText, Toast.LENGTH_SHORT).show(); } private void cutContent() { final String text = getText(); setPrimaryClip(ClipData.newPlainText(null, text)); clear(); } private ClipData getPrimaryClip() { ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); return clipboard.getPrimaryClip(); } private void pasteContent() { ClipData clip = getPrimaryClip(); if(clip != null) { for(int i = 0; i < clip.getItemCount(); i++) { CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); if(canPaste(paste)) { insert(paste.toString()); } } } } private boolean canPaste(CharSequence paste) { return paste.length() > 0; } }