Calculator editor

This commit is contained in:
serso 2012-09-21 12:14:55 +04:00
parent 9f4b0ba4f9
commit 30fbc24f81
24 changed files with 1608 additions and 1337 deletions

View File

@ -0,0 +1,19 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:47
*/
public interface CalculatorEditor extends CalculatorEventListener/*, CursorControl*/ {
void setView(@Nullable CalculatorEditorView view);
@NotNull
CalculatorEditorViewState getViewState();
void setViewState(@NotNull CalculatorEditorViewState viewState);
}

View File

@ -0,0 +1,87 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:53
*/
public class CalculatorEditorImpl implements CalculatorEditor {
@Nullable
private CalculatorEditorView view;
@NotNull
private final Object viewLock = new Object();
@NotNull
private CalculatorEditorViewState lastViewState = CalculatorEditorViewStateImpl.newDefaultInstance();
@Override
public void setView(@Nullable CalculatorEditorView view) {
synchronized (viewLock) {
this.view = view;
if ( view != null ) {
view.setState(lastViewState);
}
}
}
@NotNull
@Override
public CalculatorEditorViewState getViewState() {
return lastViewState;
}
@Override
public void setViewState(@NotNull CalculatorEditorViewState viewState) {
synchronized (viewLock) {
this.lastViewState = viewState;
if (this.view != null) {
this.view.setState(viewState);
}
}
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void setCursorOnStart() {
synchronized (viewLock) {
newSelectionViewState(0);
}
}
private void newSelectionViewState(int newSelection) {
setViewState(CalculatorEditorViewStateImpl.newSelection(this.lastViewState, newSelection));
}
public void setCursorOnEnd() {
synchronized (viewLock) {
newSelectionViewState(this.lastViewState.getText().length());
}
}
public void moveCursorLeft() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() > 0) {
newSelectionViewState(this.lastViewState.getSelection() - 1);
}
}
}
public void moveCursorRight() {
synchronized (viewLock) {
if (this.lastViewState.getSelection() < this.lastViewState.getText().length()) {
newSelectionViewState(this.lastViewState.getSelection() + 1);
}
}
}
}

View File

@ -0,0 +1,13 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:48
*/
public interface CalculatorEditorView {
void setState(@NotNull CalculatorEditorViewState viewState);
}

View File

@ -0,0 +1,16 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 11:48
*/
public interface CalculatorEditorViewState {
@NotNull
String getText();
int getSelection();
}

View File

@ -0,0 +1,49 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 12:02
*/
public class CalculatorEditorViewStateImpl implements CalculatorEditorViewState {
@NotNull
private String text = "";
private int selection = 0;
private CalculatorEditorViewStateImpl() {
}
public CalculatorEditorViewStateImpl(@NotNull CalculatorEditorViewState viewState) {
this.text = viewState.getText();
this.selection = viewState.getSelection();
}
@NotNull
@Override
public String getText() {
return this.text;
}
@Override
public int getSelection() {
return this.selection;
}
@NotNull
public static CalculatorEditorViewState newDefaultInstance() {
return new CalculatorEditorViewStateImpl();
}
@NotNull
public static CalculatorEditorViewState newSelection(@NotNull CalculatorEditorViewState viewState, int newSelection) {
final CalculatorEditorViewStateImpl result = new CalculatorEditorViewStateImpl(viewState);
result.selection = newSelection;
return result;
}
}

View File

@ -1,58 +1,63 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
/** /**
* User: serso * User: serso
* Date: 9/20/12 * Date: 9/20/12
* Time: 10:01 PM * Time: 10:01 PM
*/ */
public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData { public class CalculatorEvaluationEventDataImpl implements CalculatorEvaluationEventData {
@NotNull @NotNull
private final CalculatorEventData calculatorEventData; private final CalculatorEventData calculatorEventData;
@NotNull @NotNull
private final JsclOperation operation; private final JsclOperation operation;
@NotNull @NotNull
private final String expression; private final String expression;
public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData, public CalculatorEvaluationEventDataImpl(@NotNull CalculatorEventData calculatorEventData,
@NotNull JsclOperation operation, @NotNull JsclOperation operation,
@NotNull String expression) { @NotNull String expression) {
this.calculatorEventData = calculatorEventData; this.calculatorEventData = calculatorEventData;
this.operation = operation; this.operation = operation;
this.expression = expression; this.expression = expression;
} }
@NotNull @NotNull
@Override @Override
public JsclOperation getOperation() { public JsclOperation getOperation() {
return this.operation; return this.operation;
} }
@NotNull @NotNull
@Override @Override
public String getExpression() { public String getExpression() {
return this.expression; return this.expression;
} }
@Override @Override
public long getEventId() { public long getEventId() {
return calculatorEventData.getEventId(); return calculatorEventData.getEventId();
} }
@Override @Override
@Nullable @Nullable
public Long getSequenceId() { public Long getSequenceId() {
return calculatorEventData.getSequenceId(); return calculatorEventData.getSequenceId();
} }
@Override @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { public boolean isAfter(@NotNull CalculatorEventDataId that) {
return calculatorEventData.isAfter(calculatorEventDataId); return calculatorEventData.isAfter(that);
} }
}
@Override
public boolean isSameSequence(@NotNull CalculatorEventDataId that) {
return this.calculatorEventData.isSameSequence(that);
}
}

View File

@ -1,21 +1,23 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 18:18 * Time: 18:18
*/ */
public interface CalculatorEventDataId { public interface CalculatorEventDataId {
// the higher id => the later event // the higher id => the later event
long getEventId(); long getEventId();
// the higher id => the later event // the higher id => the later event
@Nullable @Nullable
Long getSequenceId(); Long getSequenceId();
boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId); boolean isAfter(@NotNull CalculatorEventDataId that);
}
boolean isSameSequence(@NotNull CalculatorEventDataId that);
}

View File

@ -1,64 +1,69 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 18:18 * Time: 18:18
*/ */
class CalculatorEventDataIdImpl implements CalculatorEventDataId { class CalculatorEventDataIdImpl implements CalculatorEventDataId {
private final long eventId; private final long eventId;
@Nullable @Nullable
private final Long sequenceId; private final Long sequenceId;
private CalculatorEventDataIdImpl(long id, @Nullable Long sequenceId) { private CalculatorEventDataIdImpl(long id, @Nullable Long sequenceId) {
this.eventId = id; this.eventId = id;
this.sequenceId = sequenceId; this.sequenceId = sequenceId;
} }
@NotNull @NotNull
static CalculatorEventDataId newInstance(long id, @Nullable Long sequenceId) { static CalculatorEventDataId newInstance(long id, @Nullable Long sequenceId) {
return new CalculatorEventDataIdImpl(id, sequenceId); return new CalculatorEventDataIdImpl(id, sequenceId);
} }
@Override @Override
public long getEventId() { public long getEventId() {
return this.eventId; return this.eventId;
} }
@Nullable @Nullable
@Override @Override
public Long getSequenceId() { public Long getSequenceId() {
return this.sequenceId; return this.sequenceId;
} }
@Override @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { public boolean isAfter(@NotNull CalculatorEventDataId that) {
return this.eventId > calculatorEventDataId.getEventId(); return this.eventId > that.getEventId();
} }
@Override @Override
public boolean equals(Object o) { public boolean isSameSequence(@NotNull CalculatorEventDataId that) {
if (this == o) return true; return this.sequenceId != null && this.sequenceId.equals(that.getSequenceId());
if (!(o instanceof CalculatorEventDataIdImpl)) return false; }
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o; @Override
public boolean equals(Object o) {
if (eventId != that.eventId) return false; if (this == o) return true;
if (sequenceId != null ? !sequenceId.equals(that.sequenceId) : that.sequenceId != null) if (!(o instanceof CalculatorEventDataIdImpl)) return false;
return false;
CalculatorEventDataIdImpl that = (CalculatorEventDataIdImpl) o;
return true;
} if (eventId != that.eventId) return false;
if (sequenceId != null ? !sequenceId.equals(that.sequenceId) : that.sequenceId != null)
@Override return false;
public int hashCode() {
int result = (int) (eventId ^ (eventId >>> 32)); return true;
result = 31 * result + (sequenceId != null ? sequenceId.hashCode() : 0); }
return result;
} @Override
} public int hashCode() {
int result = (int) (eventId ^ (eventId >>> 32));
result = 31 * result + (sequenceId != null ? sequenceId.hashCode() : 0);
return result;
}
}

View File

@ -1,57 +1,62 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:54 * Time: 16:54
*/ */
class CalculatorEventDataImpl implements CalculatorEventData { class CalculatorEventDataImpl implements CalculatorEventData {
@NotNull @NotNull
private CalculatorEventDataId calculatorEventDataId; private CalculatorEventDataId calculatorEventDataId;
private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) { private CalculatorEventDataImpl(@NotNull CalculatorEventDataId calculatorEventDataId) {
this.calculatorEventDataId = calculatorEventDataId; this.calculatorEventDataId = calculatorEventDataId;
} }
@NotNull @NotNull
public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) { public static CalculatorEventData newInstance(@NotNull CalculatorEventDataId calculatorEventDataId) {
return new CalculatorEventDataImpl(calculatorEventDataId); return new CalculatorEventDataImpl(calculatorEventDataId);
} }
@Override @Override
public long getEventId() { public long getEventId() {
return calculatorEventDataId.getEventId(); return calculatorEventDataId.getEventId();
} }
@Override @Override
@Nullable @Nullable
public Long getSequenceId() { public Long getSequenceId() {
return calculatorEventDataId.getSequenceId(); return calculatorEventDataId.getSequenceId();
} }
@Override @Override
public boolean isAfter(@NotNull CalculatorEventDataId calculatorEventDataId) { public boolean isAfter(@NotNull CalculatorEventDataId that) {
return this.calculatorEventDataId.isAfter(calculatorEventDataId); return this.calculatorEventDataId.isAfter(that);
} }
@Override @Override
public boolean equals(Object o) { public boolean isSameSequence(@NotNull CalculatorEventDataId that) {
if (this == o) return true; return this.calculatorEventDataId.isSameSequence(that);
if (!(o instanceof CalculatorEventDataImpl)) return false; }
CalculatorEventDataImpl that = (CalculatorEventDataImpl) o; @Override
public boolean equals(Object o) {
if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false; if (this == o) return true;
if (!(o instanceof CalculatorEventDataImpl)) return false;
return true;
} CalculatorEventDataImpl that = (CalculatorEventDataImpl) o;
@Override if (!calculatorEventDataId.equals(that.calculatorEventDataId)) return false;
public int hashCode() {
return calculatorEventDataId.hashCode(); return true;
} }
}
@Override
public int hashCode() {
return calculatorEventDataId.hashCode();
}
}

View File

@ -1,22 +1,25 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 12:45 * Time: 12:45
*/ */
public interface CalculatorLocator { public interface CalculatorLocator {
@NotNull @NotNull
JCalculatorEngine getCalculatorEngine(); JCalculatorEngine getCalculatorEngine();
@NotNull @NotNull
Calculator getCalculator(); Calculator getCalculator();
@NotNull @NotNull
CalculatorDisplay getCalculatorDisplay(); CalculatorDisplay getCalculatorDisplay();
void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine); @NotNull
} CalculatorEditor getCalculatorEditor();
void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine);
}

View File

@ -1,54 +1,63 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 12:45 * Time: 12:45
*/ */
public class CalculatorLocatorImpl implements CalculatorLocator { public class CalculatorLocatorImpl implements CalculatorLocator {
@NotNull @NotNull
private JCalculatorEngine calculatorEngine; private JCalculatorEngine calculatorEngine;
@NotNull @NotNull
private CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl(); private final CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl();
@NotNull @NotNull
private Calculator calculator = new CalculatorImpl(); private final CalculatorEditor calculatorEditor = new CalculatorEditorImpl();
@NotNull @NotNull
private static final CalculatorLocator instance = new CalculatorLocatorImpl(); private final Calculator calculator = new CalculatorImpl();
private CalculatorLocatorImpl() { @NotNull
} private static final CalculatorLocator instance = new CalculatorLocatorImpl();
@NotNull private CalculatorLocatorImpl() {
public static CalculatorLocator getInstance() { }
return instance;
} @NotNull
public static CalculatorLocator getInstance() {
@NotNull return instance;
@Override }
public JCalculatorEngine getCalculatorEngine() {
return calculatorEngine; @NotNull
} @Override
public JCalculatorEngine getCalculatorEngine() {
@NotNull return calculatorEngine;
@Override }
public Calculator getCalculator() {
return this.calculator; @NotNull
} @Override
public Calculator getCalculator() {
@Override return this.calculator;
public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) { }
this.calculatorEngine = calculatorEngine;
} @Override
public void setCalculatorEngine(@NotNull JCalculatorEngine calculatorEngine) {
@Override this.calculatorEngine = calculatorEngine;
@NotNull }
public CalculatorDisplay getCalculatorDisplay() {
return calculatorDisplay; @Override
} @NotNull
} public CalculatorDisplay getCalculatorDisplay() {
return calculatorDisplay;
}
@NotNull
@Override
public CalculatorEditor getCalculatorEditor() {
return calculatorEditor;
}
}

View File

@ -1,144 +1,142 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import jscl.math.Generic; import jscl.math.Generic;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Element; import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root; import org.simpleframework.xml.Root;
import org.simpleframework.xml.Transient; import org.simpleframework.xml.Transient;
import org.solovyev.android.calculator.CalculatorDisplay; import org.solovyev.android.calculator.CalculatorDisplay;
import org.solovyev.android.calculator.CalculatorDisplayViewState; import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorDisplayViewStateImpl; import org.solovyev.android.calculator.CalculatorDisplayViewStateImpl;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.StringUtils; import org.solovyev.common.text.StringUtils;
/** /**
* User: serso * User: serso
* Date: 9/17/11 * Date: 9/17/11
* Time: 11:05 PM * Time: 11:05 PM
*/ */
@Root @Root
public class CalculatorDisplayHistoryState implements Cloneable { public class CalculatorDisplayHistoryState implements Cloneable {
@Transient @Transient
private boolean valid = true; private boolean valid = true;
@Transient @Transient
@Nullable @Nullable
private String errorMessage = null; private String errorMessage = null;
@Element @Element
@NotNull @NotNull
private EditorHistoryState editorState; private EditorHistoryState editorState;
@Element @Element
@NotNull @NotNull
private JsclOperation jsclOperation; private JsclOperation jsclOperation;
@Transient @Transient
@Nullable @Nullable
private Generic genericResult; private Generic genericResult;
private CalculatorDisplayHistoryState() { private CalculatorDisplayHistoryState() {
// for xml // for xml
} }
@NotNull @NotNull
public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplay display) { public static CalculatorDisplayHistoryState newInstance(@NotNull CalculatorDisplayViewState viewState) {
final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState(); final CalculatorDisplayHistoryState result = new CalculatorDisplayHistoryState();
result.editorState = EditorHistoryState.newInstance(display.getViewState()); result.editorState = EditorHistoryState.newInstance(viewState);
final CalculatorDisplayViewState displayViewState = display.getViewState(); result.valid = viewState.isValid();
result.jsclOperation = viewState.getOperation();
result.valid = displayViewState.isValid(); result.genericResult = viewState.getResult();
result.jsclOperation = displayViewState.getOperation(); result.errorMessage = viewState.getErrorMessage();
result.genericResult = displayViewState.getResult();
result.errorMessage = displayViewState.getErrorMessage(); return result;
}
return result;
} public void setValuesFromHistory(@NotNull CalculatorDisplay display) {
if ( this.isValid() ) {
public void setValuesFromHistory(@NotNull CalculatorDisplay display) { display.setViewState(CalculatorDisplayViewStateImpl.newValidState(this.getJsclOperation(), this.getGenericResult(), StringUtils.getNotEmpty(this.getEditorState().getText(), ""), this.getEditorState().getCursorPosition()));
if ( this.isValid() ) { } else {
display.setViewState(CalculatorDisplayViewStateImpl.newValidState(this.getJsclOperation(), this.getGenericResult(), StringUtils.getNotEmpty(this.getEditorState().getText(), ""), this.getEditorState().getCursorPosition())); display.setViewState(CalculatorDisplayViewStateImpl.newErrorState(this.getJsclOperation(), StringUtils.getNotEmpty(this.getErrorMessage(), "")));
} else { }
display.setViewState(CalculatorDisplayViewStateImpl.newErrorState(this.getJsclOperation(), StringUtils.getNotEmpty(this.getErrorMessage(), ""))); }
}
}
public boolean isValid() {
return valid;
public boolean isValid() { }
return valid;
} @NotNull
public EditorHistoryState getEditorState() {
@NotNull return editorState;
public EditorHistoryState getEditorState() { }
return editorState;
} @NotNull
public JsclOperation getJsclOperation() {
@NotNull return jsclOperation;
public JsclOperation getJsclOperation() { }
return jsclOperation;
} @Nullable
public String getErrorMessage() {
@Nullable return errorMessage;
public String getErrorMessage() { }
return errorMessage;
} @Nullable
public Generic getGenericResult() {
@Nullable return genericResult;
public Generic getGenericResult() { }
return genericResult;
}
@Override
public boolean equals(Object o) {
@Override if (this == o) return true;
public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o;
CalculatorDisplayHistoryState that = (CalculatorDisplayHistoryState) o; if (!editorState.equals(that.editorState)) return false;
if (jsclOperation != that.jsclOperation) return false;
if (!editorState.equals(that.editorState)) return false;
if (jsclOperation != that.jsclOperation) return false; return true;
}
return true;
} @Override
public int hashCode() {
@Override int result = editorState.hashCode();
public int hashCode() { result = 31 * result + jsclOperation.hashCode();
int result = editorState.hashCode(); return result;
result = 31 * result + jsclOperation.hashCode(); }
return result;
} @Override
public String toString() {
@Override return "CalculatorDisplayHistoryState{" +
public String toString() { "valid=" + valid +
return "CalculatorDisplayHistoryState{" + ", errorMessage='" + errorMessage + '\'' +
"valid=" + valid + ", editorHistoryState=" + editorState +
", errorMessage='" + errorMessage + '\'' + ", jsclOperation=" + jsclOperation +
", editorHistoryState=" + editorState + '}';
", jsclOperation=" + jsclOperation + }
'}';
} @Override
protected CalculatorDisplayHistoryState clone() {
@Override try {
protected CalculatorDisplayHistoryState clone() { final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone();
try {
final CalculatorDisplayHistoryState clone = (CalculatorDisplayHistoryState) super.clone(); clone.editorState = this.editorState.clone();
clone.editorState = this.editorState.clone(); return clone;
} catch (CloneNotSupportedException e) {
return clone; throw new RuntimeException(e);
} catch (CloneNotSupportedException e) { }
throw new RuntimeException(e); }
} }
}
}

View File

@ -1,6 +1,7 @@
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.common.history.HistoryHelper; import org.solovyev.common.history.HistoryHelper;
/** /**
@ -8,7 +9,7 @@ import org.solovyev.common.history.HistoryHelper;
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:11 * Time: 16:11
*/ */
public interface CalculatorHistory extends HistoryHelper<CalculatorHistoryState> { public interface CalculatorHistory extends HistoryHelper<CalculatorHistoryState>, CalculatorEventListener {
void fromXml(@NotNull String xml); void fromXml(@NotNull String xml);

View File

@ -2,6 +2,7 @@ package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.history.HistoryHelper; import org.solovyev.common.history.HistoryHelper;
import org.solovyev.common.history.SimpleHistoryHelper; import org.solovyev.common.history.SimpleHistoryHelper;
@ -26,6 +27,9 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
@NotNull @NotNull
private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState>(); private final List<CalculatorHistoryState> savedHistory = new ArrayList<CalculatorHistoryState>();
@NotNull
private CalculatorEventDataId lastEventDataId = CalculatorLocatorImpl.getInstance().getCalculator().createFirstEventDataId();
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return this.history.isEmpty(); return this.history.isEmpty();
@ -128,4 +132,26 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) { public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
this.savedHistory.remove(historyState); this.savedHistory.remove(historyState);
} }
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEventType calculatorEventType,
@Nullable Object data) {
if (calculatorEventType.isOfType(CalculatorEventType.calculation_started, CalculatorEventType.calculation_result, CalculatorEventType.calculation_failed)) {
if ( calculatorEventData.isAfter(this.lastEventDataId) ) {
switch (calculatorEventType) {
case calculation_started:
CalculatorHistoryState.newInstance()
break;
}
CalculatorLocatorImpl.getInstance().getCalculatorDisplay().get
CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), display);
this.lastEventDataId = calculatorEventData;
}
}
}
} }

View File

@ -1,111 +1,115 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.simpleframework.xml.Element; import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root; import org.simpleframework.xml.Root;
import org.solovyev.android.calculator.CalculatorDisplay; import org.solovyev.android.calculator.CalculatorDisplay;
import org.solovyev.android.calculator.Editor; import org.solovyev.android.calculator.CalculatorEditor;
import org.solovyev.android.calculator.Editor;
/**
* User: serso /**
* Date: 9/11/11 * User: serso
* Time: 12:16 AM * Date: 9/11/11
*/ * Time: 12:16 AM
*/
@Root
public class CalculatorHistoryState extends AbstractHistoryState { @Root
public class CalculatorHistoryState extends AbstractHistoryState {
@Element
@NotNull @Element
private EditorHistoryState editorState; @NotNull
private EditorHistoryState editorState;
@Element
@NotNull @Element
private CalculatorDisplayHistoryState displayState; @NotNull
private CalculatorDisplayHistoryState displayState;
private CalculatorHistoryState() {
// for xml private CalculatorHistoryState() {
} // for xml
}
private CalculatorHistoryState(@NotNull EditorHistoryState editorState,
@NotNull CalculatorDisplayHistoryState displayState) { private CalculatorHistoryState(@NotNull EditorHistoryState editorState,
this.editorState = editorState; @NotNull CalculatorDisplayHistoryState displayState) {
this.displayState = displayState; this.editorState = editorState;
} this.displayState = displayState;
}
public static CalculatorHistoryState newInstance(@NotNull Editor editor, @NotNull CalculatorDisplay display) {
final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor); public static CalculatorHistoryState newInstance(@NotNull CalculatorEditor editor,
final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display); @NotNull CalculatorDisplay display) {
return new CalculatorHistoryState(editorHistoryState, displayHistoryState); final EditorHistoryState editorHistoryState = EditorHistoryState.newInstance(editor.getViewState());
}
final CalculatorDisplayHistoryState displayHistoryState = CalculatorDisplayHistoryState.newInstance(display.getViewState());
@NotNull
public EditorHistoryState getEditorState() { return new CalculatorHistoryState(editorHistoryState, displayHistoryState);
return editorState; }
}
@NotNull
public void setEditorState(@NotNull EditorHistoryState editorState) { public EditorHistoryState getEditorState() {
this.editorState = editorState; return editorState;
} }
@NotNull public void setEditorState(@NotNull EditorHistoryState editorState) {
public CalculatorDisplayHistoryState getDisplayState() { this.editorState = editorState;
return displayState; }
}
@NotNull
public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) { public CalculatorDisplayHistoryState getDisplayState() {
this.displayState = displayState; return displayState;
} }
@Override public void setDisplayState(@NotNull CalculatorDisplayHistoryState displayState) {
public String toString() { this.displayState = displayState;
return "CalculatorHistoryState{" + }
"editorState=" + editorState +
", displayState=" + displayState + @Override
'}'; public String toString() {
} return "CalculatorHistoryState{" +
"editorState=" + editorState +
@Override ", displayState=" + displayState +
public boolean equals(Object o) { '}';
if (this == o) return true; }
if (o == null || getClass() != o.getClass()) return false;
@Override
CalculatorHistoryState that = (CalculatorHistoryState) o; public boolean equals(Object o) {
if (this == o) return true;
if (this.isSaved() != that.isSaved()) return false; if (o == null || getClass() != o.getClass()) return false;
if (this.getId() != that.getId()) return false;
if (!displayState.equals(that.displayState)) return false; CalculatorHistoryState that = (CalculatorHistoryState) o;
if (!editorState.equals(that.editorState)) return false;
if (this.isSaved() != that.isSaved()) return false;
return true; if (this.getId() != that.getId()) return false;
} if (!displayState.equals(that.displayState)) return false;
if (!editorState.equals(that.editorState)) return false;
@Override
public int hashCode() { return true;
int result = Boolean.valueOf(isSaved()).hashCode(); }
result = 31 * result + getId();
result = 31 * result + editorState.hashCode(); @Override
result = 31 * result + displayState.hashCode(); public int hashCode() {
return result; int result = Boolean.valueOf(isSaved()).hashCode();
} result = 31 * result + getId();
result = 31 * result + editorState.hashCode();
public void setValuesFromHistory(@NotNull Editor editor, @NotNull CalculatorDisplay display) { result = 31 * result + displayState.hashCode();
this.getEditorState().setValuesFromHistory(editor); return result;
this.getDisplayState().setValuesFromHistory(display); }
}
public void setValuesFromHistory(@NotNull Editor editor, @NotNull CalculatorDisplay display) {
@Override this.getEditorState().setValuesFromHistory(editor);
protected CalculatorHistoryState clone() { this.getDisplayState().setValuesFromHistory(display);
final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone(); }
clone.editorState = this.editorState.clone(); @Override
clone.displayState = this.displayState.clone(); protected CalculatorHistoryState clone() {
final CalculatorHistoryState clone = (CalculatorHistoryState)super.clone();
return clone;
} clone.editorState = this.editorState.clone();
} clone.displayState = this.displayState.clone();
return clone;
}
}

View File

@ -1,99 +1,100 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Element; import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root; import org.simpleframework.xml.Root;
import org.solovyev.android.calculator.CalculatorDisplayViewState; import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.Editor; import org.solovyev.android.calculator.CalculatorEditorViewState;
import org.solovyev.android.calculator.Editor;
@Root
public class EditorHistoryState implements Cloneable{ @Root
public class EditorHistoryState implements Cloneable{
@Element
private int cursorPosition; @Element
private int cursorPosition;
@Element(required = false)
@Nullable @Element(required = false)
private String text; @Nullable
private String text;
private EditorHistoryState() {
// for xml private EditorHistoryState() {
} // for xml
}
@NotNull
public static EditorHistoryState newInstance(@NotNull Editor editor) { @NotNull
final EditorHistoryState result = new EditorHistoryState(); public static EditorHistoryState newInstance(@NotNull CalculatorEditorViewState viewState) {
final EditorHistoryState result = new EditorHistoryState();
result.text = String.valueOf(editor.getText());
result.cursorPosition = editor.getSelection(); result.text = String.valueOf(viewState.getText());
result.cursorPosition = viewState.getSelection();
return result;
} return result;
}
@NotNull
public static EditorHistoryState newInstance(@NotNull CalculatorDisplayViewState viewState) { @NotNull
final EditorHistoryState result = new EditorHistoryState(); public static EditorHistoryState newInstance(@NotNull CalculatorDisplayViewState viewState) {
final EditorHistoryState result = new EditorHistoryState();
result.text = viewState.getText();
result.cursorPosition = viewState.getSelection(); result.text = viewState.getText();
result.cursorPosition = viewState.getSelection();
return result;
} return result;
}
public void setValuesFromHistory(@NotNull Editor editor) {
editor.setText(this.getText()); public void setValuesFromHistory(@NotNull Editor editor) {
editor.setSelection(this.getCursorPosition()); editor.setText(this.getText());
} editor.setSelection(this.getCursorPosition());
}
@Nullable
public String getText() { @Nullable
return text; public String getText() {
} return text;
}
public int getCursorPosition() {
return cursorPosition; public int getCursorPosition() {
} return cursorPosition;
}
@Override
public boolean equals(Object o) { @Override
if (this == o) return true; public boolean equals(Object o) {
if (!(o instanceof EditorHistoryState)) return false; if (this == o) return true;
if (!(o instanceof EditorHistoryState)) return false;
EditorHistoryState that = (EditorHistoryState) o;
EditorHistoryState that = (EditorHistoryState) o;
if (cursorPosition != that.cursorPosition) return false;
if (text != null ? !text.equals(that.text) : that.text != null) return false; if (cursorPosition != that.cursorPosition) return false;
if (text != null ? !text.equals(that.text) : that.text != null) return false;
return true;
} return true;
}
@Override
public int hashCode() { @Override
int result = cursorPosition; public int hashCode() {
result = 31 * result + (text != null ? text.hashCode() : 0); int result = cursorPosition;
return result; result = 31 * result + (text != null ? text.hashCode() : 0);
} return result;
}
@Override
public String toString() { @Override
return "EditorHistoryState{" + public String toString() {
"cursorPosition=" + cursorPosition + return "EditorHistoryState{" +
", text='" + text + '\'' + "cursorPosition=" + cursorPosition +
'}'; ", text='" + text + '\'' +
} '}';
}
@Override
protected EditorHistoryState clone() { @Override
try { protected EditorHistoryState clone() {
return (EditorHistoryState)super.clone(); try {
} catch (CloneNotSupportedException e) { return (EditorHistoryState)super.clone();
throw new UnsupportedOperationException(e); } catch (CloneNotSupportedException e) {
} throw new UnsupportedOperationException(e);
} }
} }
}

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
~ Copyright (c) 2009-2011. Created by serso aka se.solovyev. ~ Copyright (c) 2009-2011. Created by serso aka se.solovyev.
~ For more information, please, contact se.solovyev@gmail.com ~ For more information, please, contact se.solovyev@gmail.com
~ or visit http://se.solovyev.org ~ or visit http://se.solovyev.org
--> -->
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_weight="2" a:layout_weight="2"
a:layout_width="fill_parent" a:layout_width="fill_parent"
a:layout_height="0dp"> a:layout_height="0dp">
<org.solovyev.android.calculator.CalculatorEditor <org.solovyev.android.calculator.AndroidCalculatorEditorView
a:id="@+id/calculatorEditor" a:id="@+id/calculatorEditor"
style="@style/editor_style" style="@style/editor_style"
a:textIsSelectable="true" a:textIsSelectable="true"
a:singleLine="false" a:singleLine="false"
a:scrollbars="vertical" a:scrollbars="vertical"
a:hint ="@string/c_calc_editor_hint"/> a:hint ="@string/c_calc_editor_hint"/>
</LinearLayout> </LinearLayout>

View File

@ -1,235 +1,150 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Html; import android.os.Handler;
import android.util.AttributeSet; import android.text.Html;
import android.util.Log; import android.util.AttributeSet;
import jscl.math.Generic; import android.util.Log;
import jscl.math.function.Constant; import jscl.math.Generic;
import jscl.math.function.IConstant; import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull; import jscl.math.function.IConstant;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem; import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.view.AutoResizeTextView; import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.collections.CollectionsUtils; import org.solovyev.android.view.AutoResizeTextView;
import org.solovyev.common.collections.CollectionsUtils;
import java.util.HashSet;
import java.util.Set; import java.util.HashSet;
import java.util.Set;
/**
* User: serso /**
* Date: 9/17/11 * User: serso
* Time: 10:58 PM * Date: 9/17/11
*/ * Time: 10:58 PM
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { */
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
public static enum MenuItem implements LabeledMenuItem<CalculatorDisplayViewState> {
/*
copy(R.string.c_copy) { **********************************************************************
@Override *
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { * STATIC FIELDS
CalculatorModel.copyResult(context, data); *
} **********************************************************************
}, */
convert_to_bin(R.string.convert_to_bin) { @NotNull
@Override private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_bin.onClick(data, context); /*
} **********************************************************************
*
@Override * FIELDS
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { *
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation); **********************************************************************
} */
},
@NotNull
convert_to_dec(R.string.convert_to_dec) { private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { @NotNull
ConversionMenuItem.convert_to_dec.onClick(data, context); private final Object lock = new Object();
}
@NotNull
@Override private final Handler handler = new Handler();
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation); /*
} **********************************************************************
}, *
* CONSTRUCTORS
convert_to_hex(R.string.convert_to_hex) { *
@Override **********************************************************************
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { */
ConversionMenuItem.convert_to_hex.onClick(data, context);
} public AndroidCalculatorDisplayView(Context context) {
super(context);
@Override }
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation); public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
} super(context, attrs);
}, }
convert(R.string.c_convert) { public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
@Override super(context, attrs, defStyle);
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { }
final Generic result = data.getResult();
if (result != null) { /*
new NumeralBaseConverterDialog(result.toString()).show(context); **********************************************************************
} *
} * METHODS
*
@Override **********************************************************************
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { */
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
} public boolean isValid() {
}, synchronized (lock) {
return this.state.isValid();
plot(R.string.c_plot) { }
@Override }
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic generic = data.getResult(); @Override
assert generic != null; public void setState(@NotNull final CalculatorDisplayViewState state) {
handler.postDelayed(new Runnable() {
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic)); @Override
assert constant != null; public void run() {
CalculatorActivityLauncher.plotGraph(context, generic, constant); synchronized (lock) {
} AndroidCalculatorDisplayView.this.state = state;
if ( state.isValid() ) {
@Override setTextColor(getResources().getColor(R.color.default_text_color));
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { setText(state.getStringResult());
boolean result = false; redraw();
} else {
if (operation == JsclOperation.simplify) { setTextColor(getResources().getColor(R.color.display_error_text_color));
if (getNotSystemConstants(generic).size() == 1) { setText(state.getErrorMessage());
result = true; redraw();
} }
} }
}
return result; }, 1);
} }
@NotNull @NotNull
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) { @Override
final Set<Constant> notSystemConstants = new HashSet<Constant>(); public CalculatorDisplayViewState getState() {
synchronized (lock) {
for (Constant constant : generic.getConstants()) { return this.state;
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName()); }
if (var != null && !var.isSystem() && !var.isDefined()) { }
notSystemConstants.add(constant);
} private synchronized void redraw() {
} if (isValid()) {
String text = getText().toString();
return notSystemConstants;
} Log.d(this.getClass().getName(), text);
};
try {
private final int captionId; TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
MenuItem(int captionId) { } catch (CalculatorParseException e) {
this.captionId = captionId; Log.e(this.getClass().getName(), e.getMessage(), e);
} }
public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) { Log.d(this.getClass().getName(), text);
//noinspection ConstantConditions super.setText(Html.fromHtml(text), BufferType.EDITABLE);
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation()); }
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { setAddEllipsis(false);
return true; setMinTextSize(10);
} resizeText();
}
@NotNull }
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
@NotNull
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
public AndroidCalculatorDisplayView(Context context) {
super(context);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public boolean isValid() {
return this.state.isValid();
}
@Override
public void setState(@NotNull CalculatorDisplayViewState state) {
this.state = state;
if ( state.isValid() ) {
setTextColor(getResources().getColor(R.color.default_text_color));
setText(state.getStringResult());
} else {
setTextColor(getResources().getColor(R.color.display_error_text_color));
setText(state.getErrorMessage());
}
}
@NotNull
@Override
public CalculatorDisplayViewState getState() {
return this.state;
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
public synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
Log.d(this.getClass().getName(), text);
try {
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
@Override
public int getSelection() {
return this.getSelectionStart();
}
@Override
public void setSelection(int selection) {
// not supported by TextView
}
}

View File

@ -25,7 +25,7 @@ import org.solovyev.common.collections.CollectionsUtils;
* Date: 9/17/11 * Date: 9/17/11
* Time: 12:25 AM * Time: 12:25 AM
*/ */
public class CalculatorEditor extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener { public class AndroidCalculatorEditorView extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEditorView {
private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display"; private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display";
private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true; private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true;
@ -35,39 +35,16 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
@NotNull @NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine()); private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
public CalculatorEditor(Context context) { public AndroidCalculatorEditorView(Context context) {
super(context); super(context);
init();
} }
public CalculatorEditor(Context context, AttributeSet attrs) { public AndroidCalculatorEditorView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
init();
} }
private void init() { public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) {
// NOTE: in this solution cursor is missing super(context, attrs, defStyle);
/*this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final TextView textView = (TextView)v;
// backup the input type
int inputType = textView.getInputType();
// disable soft input
textView.setInputType(InputType.TYPE_NULL);
// call native handler
textView.onTouchEvent(event);
// restore input type
textView.setInputType(inputType);
// consume touch even
return true;
}
});*/
} }
@ -95,11 +72,6 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
} }
} }
public CalculatorEditor(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
@Override @Override
protected void onCreateContextMenu(ContextMenu menu) { protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu); super.onCreateContextMenu(menu);
@ -107,11 +79,6 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
menu.removeItem(android.R.id.selectAll); menu.removeItem(android.R.id.selectAll);
} }
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
public synchronized void redraw() { public synchronized void redraw() {
String text = getText().toString(); String text = getText().toString();

View File

@ -0,0 +1,141 @@
package org.solovyev.android.calculator;
import android.content.Context;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.collections.CollectionsUtils;
import java.util.HashSet;
import java.util.Set;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 10:55
*/
public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDisplayViewState> {
copy(R.string.c_copy) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
CalculatorModel.copyResult(context, data);
}
},
convert_to_bin(R.string.convert_to_bin) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_bin.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
}
},
convert_to_dec(R.string.convert_to_dec) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_dec.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
}
},
convert_to_hex(R.string.convert_to_hex) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_hex.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
}
},
convert(R.string.c_convert) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic result = data.getResult();
if (result != null) {
new NumeralBaseConverterDialog(result.toString()).show(context);
}
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
}
},
plot(R.string.c_plot) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic generic = data.getResult();
assert generic != null;
final Constant constant = CollectionsUtils.getFirstCollectionElement(getNotSystemConstants(generic));
assert constant != null;
CalculatorActivityLauncher.plotGraph(context, generic, constant);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
if (operation == JsclOperation.simplify) {
if (getNotSystemConstants(generic).size() == 1) {
result = true;
}
}
return result;
}
@NotNull
private Set<Constant> getNotSystemConstants(@NotNull Generic generic) {
final Set<Constant> notSystemConstants = new HashSet<Constant>();
for (Constant constant : generic.getConstants()) {
IConstant var = CalculatorEngine.instance.getVarsRegistry().get(constant.getName());
if (var != null && !var.isSystem() && !var.isDefined()) {
notSystemConstants.add(constant);
}
}
return notSystemConstants;
}
};
private final int captionId;
CalculatorDisplayMenuItem(int captionId) {
this.captionId = captionId;
}
public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) {
//noinspection ConstantConditions
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return true;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}

View File

@ -0,0 +1,53 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.view.View;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
import java.util.ArrayList;
import java.util.List;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 10:58
*/
public class CalculatorDisplayOnClickListener implements View.OnClickListener {
@NotNull
private final Activity activity;
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View v) {
if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
final CalculatorDisplayViewState displayViewState = cd.getViewState();
if (displayViewState.isValid()) {
final List<CalculatorDisplayMenuItem> filteredMenuItems = new ArrayList<CalculatorDisplayMenuItem>(CalculatorDisplayMenuItem.values().length);
for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) {
if (menuItem.isItemVisible(displayViewState)) {
filteredMenuItems.add(menuItem);
}
}
if (!filteredMenuItems.isEmpty()) {
AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(displayViewState).show();
}
} else {
final String errorMessage = displayViewState.getErrorMessage();
if (errorMessage != null) {
CalculatorModel.showEvaluationError(activity, errorMessage);
}
}
}
}
}

View File

@ -1,411 +1,356 @@
/* /*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev. * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com * For more information, please, contact se.solovyev@gmail.com
*/ */
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.text.ClipboardManager; import android.text.ClipboardManager;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.CursorControl; import org.solovyev.android.CursorControl;
import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl; import org.solovyev.android.calculator.history.AndroidCalculatorHistoryImpl;
import org.solovyev.android.calculator.history.CalculatorHistoryState; import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.history.TextViewEditorAdapter; import org.solovyev.android.calculator.history.TextViewEditorAdapter;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.history.HistoryControl; import org.solovyev.android.history.HistoryControl;
import org.solovyev.android.menu.AMenuBuilder; import org.solovyev.common.MutableObject;
import org.solovyev.android.menu.MenuImpl; import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.MutableObject; import org.solovyev.common.msg.Message;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.text.StringUtils;
import org.solovyev.common.msg.Message;
import org.solovyev.common.text.StringUtils; /**
* User: serso
import java.util.ArrayList; * Date: 9/12/11
import java.util.List; * Time: 11:15 PM
*/
/** public enum CalculatorModel implements HistoryControl<CalculatorHistoryState>, CalculatorEngineControl {
* User: serso
* Date: 9/12/11 instance;
* Time: 11:15 PM
*/ // millis to wait before evaluation after user edit action
public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorHistoryState>, CalculatorEngineControl { public static final int EVAL_DELAY_MILLIS = 0;
instance; @NotNull
private final CalculatorEditor editor;
// millis to wait before evaluation after user edit action
public static final int EVAL_DELAY_MILLIS = 0; @NotNull
private final CalculatorDisplay display;
@NotNull
private CalculatorEditor editor; @NotNull
private CalculatorEngine calculatorEngine;
@NotNull
private AndroidCalculatorDisplayView display; private CalculatorModel() {
display = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
@NotNull editor = CalculatorLocatorImpl.getInstance().getCalculatorEditor();
private CalculatorEngine calculatorEngine; }
public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) { public CalculatorModel init(@NotNull final Activity activity, @NotNull SharedPreferences preferences, @NotNull CalculatorEngine calculator) {
Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity);
this.calculatorEngine = calculator; this.calculatorEngine = calculator;
this.editor = (CalculatorEditor) activity.findViewById(R.id.calculatorEditor); final AndroidCalculatorEditorView editorView = (AndroidCalculatorEditorView) activity.findViewById(R.id.calculatorEditor);
this.editor.init(preferences); editorView.init(preferences);
preferences.registerOnSharedPreferenceChangeListener(editor); preferences.registerOnSharedPreferenceChangeListener(editorView);
editor.setView(editorView);
this.display = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); final AndroidCalculatorDisplayView displayView = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay);
displayView.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState(); display.setView(displayView);
if (lastState == null) {
saveHistoryState(); final CalculatorHistoryState lastState = AndroidCalculatorHistoryImpl.instance.getLastHistoryState();
} else { if (lastState == null) {
setCurrentHistoryState(lastState); saveHistoryState();
} } else {
setCurrentHistoryState(lastState);
}
return this;
}
return this;
private static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { }
final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
public static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) {
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null); final LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(activity) ((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
.setPositiveButton(R.string.c_cancel, null)
.setView(errorMessageView); final AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setPositiveButton(R.string.c_cancel, null)
builder.create().show(); .setView(errorMessageView);
}
builder.create().show();
public void copyResult(@NotNull Context context) { }
copyResult(context, display);
} public void copyResult(@NotNull Context context) {
copyResult(context, display.getViewState());
public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayViewState viewState) { }
if (viewState.isValid()) {
final CharSequence text = viewState.getText(); public static void copyResult(@NotNull Context context, @NotNull final CalculatorDisplayViewState viewState) {
if (!StringUtils.isEmpty(text)) { if (viewState.isValid()) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE); final CharSequence text = viewState.getText();
clipboard.setText(text.toString()); if (!StringUtils.isEmpty(text)) {
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show(); final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
} clipboard.setText(text.toString());
} Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
} }
}
private void saveHistoryState() { }
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
} private void saveHistoryState() {
AndroidCalculatorHistoryImpl.instance.addState(getCurrentHistoryState());
public void setCursorOnStart() { }
editor.setSelection(0);
} public void doTextOperation(@NotNull TextOperation operation) {
doTextOperation(operation, true);
public void setCursorOnEnd() { }
editor.setSelection(editor.getText().length());
} public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) {
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
public void moveCursorLeft() { }
if (editor.getSelectionStart() > 0) {
editor.setSelection(editor.getSelectionStart() - 1); public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
} final String editorStateBefore = this.editor.getText().toString();
}
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'");
public void moveCursorRight() { operation.doOperation(this.editor);
if (editor.getSelectionStart() < editor.getText().length()) { //Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
editor.setSelection(editor.getSelectionStart() + 1);
} final String editorStateAfter = this.editor.getText().toString();
} if (forceEval ||!editorStateBefore.equals(editorStateAfter)) {
public void doTextOperation(@NotNull TextOperation operation) { editor.redraw();
doTextOperation(operation, true);
} evaluate(delayEvaluate, editorStateAfter, jsclOperation, null);
}
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate) { }
doTextOperation(operation, delayEvaluate, JsclOperation.numeric, false);
} @NotNull
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
public void doTextOperation(@NotNull TextOperation operation, boolean delayEvaluate, @NotNull JsclOperation jsclOperation, boolean forceEval) {
final String editorStateBefore = this.editor.getText().toString(); private void evaluate(boolean delayEvaluate,
@NotNull final String expression,
Log.d(CalculatorModel.class.getName(), "Editor state changed before '" + editorStateBefore + "'"); @NotNull final JsclOperation operation,
operation.doOperation(this.editor); @Nullable CalculatorHistoryState historyState) {
//Log.d(CalculatorModel.class.getName(), "Doing text operation" + StringUtils.fromStackTrace(Thread.currentThread().getStackTrace()));
final CalculatorHistoryState localHistoryState;
final String editorStateAfter = this.editor.getText().toString(); if (historyState == null) {
if (forceEval ||!editorStateBefore.equals(editorStateAfter)) { //this.display.setText("");
localHistoryState = getCurrentHistoryState();
editor.redraw(); } else {
this.display.setText(historyState.getDisplayState().getEditorState().getText());
evaluate(delayEvaluate, editorStateAfter, jsclOperation, null); localHistoryState = historyState;
} }
}
pendingOperation.setObject(new Runnable() {
@NotNull @Override
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>(); public void run() {
// allow only one runner at one time
private void evaluate(boolean delayEvaluate, synchronized (pendingOperation) {
@NotNull final String expression, //lock all operations with history
@NotNull final JsclOperation operation, if (pendingOperation.getObject() == this) {
@Nullable CalculatorHistoryState historyState) { // actually nothing shall be logged while text operations are done
evaluate(expression, operation, this);
final CalculatorHistoryState localHistoryState;
if (historyState == null) { if (pendingOperation.getObject() == this) {
//this.display.setText(""); // todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null)
localHistoryState = getCurrentHistoryState(); pendingOperation.setObject(null);
} else { localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState());
this.display.setText(historyState.getDisplayState().getEditorState().getText()); }
localHistoryState = historyState; }
} }
}
pendingOperation.setObject(new Runnable() { });
@Override
public void run() { if (delayEvaluate) {
// allow only one runner at one time if (historyState == null) {
synchronized (pendingOperation) { AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
//lock all operations with history }
if (pendingOperation.getObject() == this) { // todo serso: this is not correct - operation is processing still in the same thread
// actually nothing shall be logged while text operations are done new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
evaluate(expression, operation, this); } else {
pendingOperation.getObject().run();
if (pendingOperation.getObject() == this) { if (historyState == null) {
// todo serso: of course there is small probability that someone will set pendingOperation after if statement but before .setObject(null) AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
pendingOperation.setObject(null); }
localHistoryState.setDisplayState(getCurrentHistoryState().getDisplayState()); }
} }
}
} @Override
} public void evaluate() {
}); evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null);
}
if (delayEvaluate) {
if (historyState == null) { public void evaluate(@NotNull JsclOperation operation) {
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState); evaluate(false, this.editor.getText().toString(), operation, null);
} }
// todo serso: this is not correct - operation is processing still in the same thread
new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS); @Override
} else { public void simplify() {
pendingOperation.getObject().run(); evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null);
if (historyState == null) { }
AndroidCalculatorHistoryImpl.instance.addState(localHistoryState);
} private void evaluate(@Nullable final String expression,
} @NotNull JsclOperation operation,
} @NotNull Runnable currentRunner) {
@Override if (!StringUtils.isEmpty(expression)) {
public void evaluate() { try {
evaluate(false, this.editor.getText().toString(), JsclOperation.numeric, null); Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
} final CalculatorOutput result = calculatorEngine.evaluate(operation, expression);
public void evaluate(@NotNull JsclOperation operation) { // todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
evaluate(false, this.editor.getText().toString(), operation, null); if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) {
} display.setText(result.getStringResult());
} else {
@Override display.setText("");
public void simplify() { }
evaluate(false, this.editor.getText().toString(), JsclOperation.simplify, null); display.setJsclOperation(result.getOperation());
} display.setGenericResult(result.getResult());
} catch (CalculatorParseException e) {
private void evaluate(@Nullable final String expression, handleEvaluationException(expression, display, operation, e);
@NotNull JsclOperation operation, } catch (CalculatorEvalException e) {
@NotNull Runnable currentRunner) { handleEvaluationException(expression, display, operation, e);
}
if (!StringUtils.isEmpty(expression)) { } else {
try { this.display.setText("");
Log.d(CalculatorModel.class.getName(), "Trying to evaluate '" + operation + "': " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); this.display.setJsclOperation(operation);
final CalculatorOutput result = calculatorEngine.evaluate(operation, expression); this.display.setGenericResult(null);
}
// todo serso: second condition might replaced with expression.equals(this.editor.getText().toString()) ONLY if expression will be formatted with text highlighter
if (currentRunner == pendingOperation.getObject() && this.editor.getText().length() > 0) {
display.setText(result.getStringResult());
} else { this.display.redraw();
display.setText(""); }
}
display.setJsclOperation(result.getOperation()); private void handleEvaluationException(@NotNull String expression,
display.setGenericResult(result.getResult()); @NotNull AndroidCalculatorDisplayView localDisplay,
} catch (CalculatorParseException e) { @NotNull JsclOperation operation,
handleEvaluationException(expression, display, operation, e); @NotNull Message e) {
} catch (CalculatorEvalException e) { Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e);
handleEvaluationException(expression, display, operation, e); if ( StringUtils.isEmpty(localDisplay.getText()) ) {
} // if previous display state was empty -> show error
} else { localDisplay.setText(R.string.c_syntax_error);
this.display.setText(""); } else {
this.display.setJsclOperation(operation); // show previous result instead of error caption (actually previous result will be greyed)
this.display.setGenericResult(null); }
} localDisplay.setJsclOperation(operation);
localDisplay.setGenericResult(null);
localDisplay.setValid(false);
localDisplay.setErrorMessage(e.getLocalizedMessage());
this.display.redraw(); }
}
public void clear() {
private void handleEvaluationException(@NotNull String expression, if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) {
@NotNull AndroidCalculatorDisplayView localDisplay, editor.getText().clear();
@NotNull JsclOperation operation, display.setText("");
@NotNull Message e) { saveHistoryState();
Log.d(CalculatorModel.class.getName(), "Evaluation failed for : " + expression + ". Error message: " + e); }
if ( StringUtils.isEmpty(localDisplay.getText()) ) { }
// if previous display state was empty -> show error
localDisplay.setText(R.string.c_syntax_error); public void processDigitButtonAction(@Nullable final String text) {
} else { processDigitButtonAction(text, true);
// show previous result instead of error caption (actually previous result will be greyed) }
}
localDisplay.setJsclOperation(operation); public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) {
localDisplay.setGenericResult(null);
localDisplay.setValid(false); if (!StringUtils.isEmpty(text)) {
localDisplay.setErrorMessage(e.getLocalizedMessage()); doTextOperation(new CalculatorModel.TextOperation() {
}
@Override
public void clear() { public void doOperation(@NotNull EditText editor) {
if (!StringUtils.isEmpty(editor.getText()) || !StringUtils.isEmpty(display.getText())) { int cursorPositionOffset = 0;
editor.getText().clear(); final StringBuilder textToBeInserted = new StringBuilder(text);
display.setText("");
saveHistoryState(); final MathType.Result mathType = MathType.getType(text, 0, false);
} switch (mathType.getMathType()) {
} case function:
textToBeInserted.append("()");
public void processDigitButtonAction(@Nullable final String text) { cursorPositionOffset = -1;
processDigitButtonAction(text, true); break;
} case operator:
textToBeInserted.append("()");
public void processDigitButtonAction(@Nullable final String text, boolean delayEvaluate) { cursorPositionOffset = -1;
break;
if (!StringUtils.isEmpty(text)) { case comma:
doTextOperation(new CalculatorModel.TextOperation() { textToBeInserted.append(" ");
break;
@Override }
public void doOperation(@NotNull EditText editor) {
int cursorPositionOffset = 0; if (cursorPositionOffset == 0) {
final StringBuilder textToBeInserted = new StringBuilder(text); if (MathType.openGroupSymbols.contains(text)) {
cursorPositionOffset = -1;
final MathType.Result mathType = MathType.getType(text, 0, false); }
switch (mathType.getMathType()) { }
case function:
textToBeInserted.append("()"); editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
cursorPositionOffset = -1; editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset);
break; }
case operator: }, delayEvaluate);
textToBeInserted.append("()"); }
cursorPositionOffset = -1; }
break;
case comma: public static interface TextOperation {
textToBeInserted.append(" ");
break; void doOperation(@NotNull EditText editor);
}
}
if (cursorPositionOffset == 0) {
if (MathType.openGroupSymbols.contains(text)) { @Override
cursorPositionOffset = -1; public void doHistoryAction(@NotNull HistoryAction historyAction) {
} synchronized (AndroidCalculatorHistoryImpl.instance) {
} if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState());
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); if (newState != null) {
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); setCurrentHistoryState(newState);
} }
}, delayEvaluate); }
} }
} }
public static interface TextOperation { @Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
void doOperation(@NotNull EditText editor); synchronized (AndroidCalculatorHistoryImpl.instance) {
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState);
}
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display);
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) { final String expression = this.editor.getText().toString();
synchronized (AndroidCalculatorHistoryImpl.instance) { if ( !StringUtils.isEmpty(expression) ) {
if (AndroidCalculatorHistoryImpl.instance.isActionAvailable(historyAction)) { if ( StringUtils.isEmpty(this.display.getText().toString()) ) {
final CalculatorHistoryState newState = AndroidCalculatorHistoryImpl.instance.doAction(historyAction, getCurrentHistoryState()); evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState);
if (newState != null) { }
setCurrentHistoryState(newState); }
}
} editor.redraw();
} //display.redraw();
} }
}
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) { @Override
synchronized (AndroidCalculatorHistoryImpl.instance) { @NotNull
Log.d(this.getClass().getName(), "Saved history found: " + editorHistoryState); public CalculatorHistoryState getCurrentHistoryState() {
synchronized (AndroidCalculatorHistoryImpl.instance) {
editorHistoryState.setValuesFromHistory(new TextViewEditorAdapter(this.editor), this.display); return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), display);
}
final String expression = this.editor.getText().toString(); }
if ( !StringUtils.isEmpty(expression) ) {
if ( StringUtils.isEmpty(this.display.getText().toString()) ) { @NotNull
evaluate(false, expression, this.display.getJsclOperation(), editorHistoryState); public CalculatorDisplay getDisplay() {
} return display;
} }
editor.redraw(); }
display.redraw();
}
}
@Override
@NotNull
public CalculatorHistoryState getCurrentHistoryState() {
synchronized (AndroidCalculatorHistoryImpl.instance) {
return CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), this.display);
}
}
@NotNull
public AndroidCalculatorDisplayView getDisplay() {
return display;
}
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
@NotNull
private final Activity activity;
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View v) {
if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getCalculatorDisplay();
final CalculatorDisplayViewState displayViewState = cd.getViewState();
if (displayViewState.isValid()) {
final List<AndroidCalculatorDisplayView.MenuItem> filteredMenuItems = new ArrayList<AndroidCalculatorDisplayView.MenuItem>(AndroidCalculatorDisplayView.MenuItem.values().length);
for (AndroidCalculatorDisplayView.MenuItem menuItem : AndroidCalculatorDisplayView.MenuItem.values()) {
if (menuItem.isItemVisible(displayViewState)) {
filteredMenuItems.add(menuItem);
}
}
if (!filteredMenuItems.isEmpty()) {
AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(cd).show();
}
} else {
final String errorMessage = displayViewState.getErrorMessage();
if (errorMessage != null) {
showEvaluationError(activity, errorMessage);
}
}
}
}
}
}

View File

@ -11,6 +11,8 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction; import org.solovyev.common.history.HistoryAction;
@ -142,4 +144,9 @@ public enum AndroidCalculatorHistoryImpl implements AndroidCalculatorHistory {
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) { public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
calculatorHistory.removeSavedHistory(historyState); calculatorHistory.removeSavedHistory(historyState);
} }
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
calculatorHistory.onCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
} }

View File

@ -173,7 +173,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
text2.append(s.charAt(i)); text2.append(s.charAt(i));
} }
//Log.d(CalculatorEditor.class.getName(), text2.toString()); //Log.d(AndroidCalculatorEditorView.class.getName(), text2.toString());
result = text2.toString(); result = text2.toString();
} else { } else {