Calculator editor

This commit is contained in:
serso 2012-09-21 13:54:35 +04:00
parent 30fbc24f81
commit 0c698a6d39
15 changed files with 906 additions and 442 deletions

View File

@ -1,30 +1,34 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.NumeralBase; import jscl.NumeralBase;
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.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.msg.MessageRegistry;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:38 * Time: 16:38
*/ */
public interface Calculator extends CalculatorEventContainer { public interface Calculator extends CalculatorEventContainer {
@NotNull @NotNull
CalculatorEventDataId createFirstEventDataId(); CalculatorEventDataId createFirstEventDataId();
void evaluate(@NotNull JsclOperation operation, void evaluate(@NotNull JsclOperation operation,
@NotNull String expression); @NotNull String expression);
@NotNull @NotNull
CalculatorEventDataId evaluate(@NotNull JsclOperation operation, CalculatorEventDataId evaluate(@NotNull JsclOperation operation,
@NotNull String expression, @NotNull String expression,
@Nullable MessageRegistry mr); @Nullable MessageRegistry mr);
@NotNull @NotNull
CalculatorEventDataId convert(@NotNull Generic generic, @NotNull NumeralBase to); CalculatorEventDataId convert(@NotNull Generic generic, @NotNull NumeralBase to);
}
@NotNull
CalculatorEventDataId fireCalculatorEvent(@NotNull CalculatorEventType calculatorEventType, @Nullable Object data);
}

View File

@ -16,4 +16,58 @@ public interface CalculatorEditor extends CalculatorEventListener/*, CursorContr
CalculatorEditorViewState getViewState(); CalculatorEditorViewState getViewState();
void setViewState(@NotNull CalculatorEditorViewState viewState); void setViewState(@NotNull CalculatorEditorViewState viewState);
/*
**********************************************************************
*
* CURSOR CONTROL
*
**********************************************************************
*/
/**
* Method sets the cursor to the beginning
*/
@NotNull
public CalculatorEditorViewState setCursorOnStart();
/**
* Method sets the cursor to the end
*/
@NotNull
public CalculatorEditorViewState setCursorOnEnd();
/**
* Method moves cursor to the left of current position
*/
@NotNull
public CalculatorEditorViewState moveCursorLeft();
/**
* Method moves cursor to the right of current position
*/
@NotNull
public CalculatorEditorViewState moveCursorRight();
/*
**********************************************************************
*
* EDITOR OPERATIONS
*
**********************************************************************
*/
@NotNull
CalculatorEditorViewState erase();
@NotNull
CalculatorEditorViewState setText(@NotNull String text);
@NotNull
CalculatorEditorViewState setText(@NotNull String text, int selection);
@NotNull
CalculatorEditorViewState insert(@NotNull String text);
@NotNull
CalculatorEditorViewState moveSelection(int offset);
} }

View File

@ -0,0 +1,17 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 13:46
*/
public interface CalculatorEditorChangeEventData {
@NotNull
CalculatorEditorViewState getOldState();
@NotNull
CalculatorEditorViewState getNewState();
}

View File

@ -0,0 +1,34 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 13:46
*/
public class CalculatorEditorChangeEventDataImpl implements CalculatorEditorChangeEventData {
@NotNull
private CalculatorEditorViewState oldState;
@NotNull
private CalculatorEditorViewState newState;
public CalculatorEditorChangeEventDataImpl(@NotNull CalculatorEditorViewState oldState, @NotNull CalculatorEditorViewState newState) {
this.oldState = oldState;
this.newState = newState;
}
@NotNull
@Override
public CalculatorEditorViewState getOldState() {
return this.oldState;
}
@NotNull
@Override
public CalculatorEditorViewState getNewState() {
return this.newState;
}
}

View File

@ -19,6 +19,13 @@ public class CalculatorEditorImpl implements CalculatorEditor {
@NotNull @NotNull
private CalculatorEditorViewState lastViewState = CalculatorEditorViewStateImpl.newDefaultInstance(); private CalculatorEditorViewState lastViewState = CalculatorEditorViewStateImpl.newDefaultInstance();
@NotNull
private final Calculator calculator;
public CalculatorEditorImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
}
@Override @Override
public void setView(@Nullable CalculatorEditorView view) { public void setView(@Nullable CalculatorEditorView view) {
synchronized (viewLock) { synchronized (viewLock) {
@ -37,12 +44,16 @@ public class CalculatorEditorImpl implements CalculatorEditor {
} }
@Override @Override
public void setViewState(@NotNull CalculatorEditorViewState viewState) { public void setViewState(@NotNull CalculatorEditorViewState newViewState) {
synchronized (viewLock) { synchronized (viewLock) {
this.lastViewState = viewState; final CalculatorEditorViewState oldViewState = this.lastViewState;
this.lastViewState = newViewState;
if (this.view != null) { if (this.view != null) {
this.view.setState(viewState); this.view.setState(newViewState);
} }
calculator.fireCalculatorEvent(CalculatorEventType.editor_state_changed, new CalculatorEditorChangeEventDataImpl(oldViewState, newViewState));
} }
} }
@ -53,35 +64,130 @@ public class CalculatorEditorImpl implements CalculatorEditor {
//To change body of implemented methods use File | Settings | File Templates. //To change body of implemented methods use File | Settings | File Templates.
} }
public void setCursorOnStart() { @NotNull
public CalculatorEditorViewState setCursorOnStart() {
synchronized (viewLock) { synchronized (viewLock) {
newSelectionViewState(0); return newSelectionViewState(0);
} }
} }
private void newSelectionViewState(int newSelection) { @NotNull
setViewState(CalculatorEditorViewStateImpl.newSelection(this.lastViewState, newSelection)); private CalculatorEditorViewState newSelectionViewState(int newSelection) {
} if (this.lastViewState.getSelection() != newSelection) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, newSelection);
public void setCursorOnEnd() { setViewState(result);
synchronized (viewLock) { return result;
newSelectionViewState(this.lastViewState.getText().length()); } else {
return this.lastViewState;
} }
} }
public void moveCursorLeft() { @NotNull
public CalculatorEditorViewState setCursorOnEnd() {
synchronized (viewLock) {
return newSelectionViewState(this.lastViewState.getText().length());
}
}
@NotNull
public CalculatorEditorViewState moveCursorLeft() {
synchronized (viewLock) { synchronized (viewLock) {
if (this.lastViewState.getSelection() > 0) { if (this.lastViewState.getSelection() > 0) {
newSelectionViewState(this.lastViewState.getSelection() - 1); return newSelectionViewState(this.lastViewState.getSelection() - 1);
} else {
return this.lastViewState;
} }
} }
} }
public void moveCursorRight() { @NotNull
public CalculatorEditorViewState moveCursorRight() {
synchronized (viewLock) { synchronized (viewLock) {
if (this.lastViewState.getSelection() < this.lastViewState.getText().length()) { if (this.lastViewState.getSelection() < this.lastViewState.getText().length()) {
newSelectionViewState(this.lastViewState.getSelection() + 1); return newSelectionViewState(this.lastViewState.getSelection() + 1);
} else {
return this.lastViewState;
} }
} }
} }
@NotNull
@Override
public CalculatorEditorViewState erase() {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection();
final String text = this.lastViewState.getText();
if (selection > 0 && text.length() > 0 && selection <= text.length()) {
final StringBuilder newText = new StringBuilder(text.length() - 1);
newText.append(text.substring(0, selection - 1)).append(text.substring(selection, text.length()));
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), selection - 1);
setViewState(result);
return result;
} else {
return this.lastViewState;
}
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text) {
synchronized (viewLock) {
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, text.length());
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState setText(@NotNull String text, int selection) {
synchronized (viewLock) {
selection = correctSelection(selection, text);
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(text, selection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState insert(@NotNull String text) {
synchronized (viewLock) {
final int selection = this.lastViewState.getSelection();
final String oldText = this.lastViewState.getText();
final StringBuilder newText = new StringBuilder(text.length() + oldText.length());
newText.append(oldText.substring(0, selection));
newText.append(text);
newText.append(oldText.substring(selection));
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newInstance(newText.toString(), text.length() + selection);
setViewState(result);
return result;
}
}
@NotNull
@Override
public CalculatorEditorViewState moveSelection(int offset) {
synchronized (viewLock) {
int selection = this.lastViewState.getSelection() + offset;
selection = correctSelection(selection, this.lastViewState.getText());
final CalculatorEditorViewState result = CalculatorEditorViewStateImpl.newSelection(this.lastViewState, selection);
setViewState(result);
return result;
}
}
private int correctSelection(int selection, @NotNull String text) {
int result = Math.max(selection, 0);
result = Math.min(result, text.length());
return result;
}
} }

View File

@ -46,4 +46,12 @@ public class CalculatorEditorViewStateImpl implements CalculatorEditorViewState
return result; return result;
} }
@NotNull
public static CalculatorEditorViewState newInstance(@NotNull String text, int selection) {
final CalculatorEditorViewStateImpl result = new CalculatorEditorViewStateImpl();
result.text = text;
result.selection = selection;
return result;
}
} }

View File

@ -1,55 +1,66 @@
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: 16:40 * Time: 16:40
*/ */
public enum CalculatorEventType { public enum CalculatorEventType {
/* /*
********************************************************************** **********************************************************************
* *
* org.solovyev.android.calculator.CalculatorEvaluationEventData * org.solovyev.android.calculator.CalculatorEvaluationEventData
* *
********************************************************************** **********************************************************************
*/ */
// @NotNull org.solovyev.android.calculator.CalculatorInput // @NotNull org.solovyev.android.calculator.CalculatorInput
calculation_started, calculation_started,
// @NotNull org.solovyev.android.calculator.CalculatorOutput // @NotNull org.solovyev.android.calculator.CalculatorOutput
calculation_result, calculation_result,
calculation_cancelled, calculation_cancelled,
calculation_finished, calculation_finished,
// @NotNull org.solovyev.android.calculator.CalculatorFailure // @NotNull org.solovyev.android.calculator.CalculatorFailure
calculation_failed, calculation_failed,
/* /*
********************************************************************** **********************************************************************
* *
* CONVERSION * CONVERSION
* *
********************************************************************** **********************************************************************
*/ */
conversion_started, conversion_started,
// @NotNull String conversion result // @NotNull String conversion result
conversion_finished; conversion_finished,
public boolean isOfType(@NotNull CalculatorEventType... types) { /*
for (CalculatorEventType type : types) { **********************************************************************
if ( this == type ) { *
return true; * EDITOR
} *
} **********************************************************************
*/
return false;
} // @NotNull org.solovyev.android.calculator.CalculatorEditorChangeEventData
editor_state_changed;
}
public boolean isOfType(@NotNull CalculatorEventType... types) {
for (CalculatorEventType type : types) {
if ( this == type ) {
return true;
}
}
return false;
}
}

View File

@ -1,291 +1,316 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.AbstractJsclArithmeticException; import jscl.AbstractJsclArithmeticException;
import jscl.NumeralBase; import jscl.NumeralBase;
import jscl.NumeralBaseException; import jscl.NumeralBaseException;
import jscl.math.Generic; import jscl.math.Generic;
import jscl.text.ParseInterruptedException; import jscl.text.ParseInterruptedException;
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;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.common.msg.MessageRegistry; import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.MessageType; import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils; import org.solovyev.common.text.StringUtils;
import org.solovyev.math.units.UnitConverter; import org.solovyev.math.units.UnitConverter;
import org.solovyev.math.units.UnitImpl; import org.solovyev.math.units.UnitImpl;
import org.solovyev.math.units.UnitType; import org.solovyev.math.units.UnitType;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 20.09.12 * Date: 20.09.12
* Time: 16:42 * Time: 16:42
*/ */
public class CalculatorImpl implements Calculator { public class CalculatorImpl implements Calculator, CalculatorEventListener {
private static final long FIRST_ID = 0; private static final long FIRST_ID = 0;
@NotNull @NotNull
private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer(); private final CalculatorEventContainer calculatorEventContainer = new ListCalculatorEventContainer();
@NotNull @NotNull
private final AtomicLong counter = new AtomicLong(FIRST_ID); private final AtomicLong counter = new AtomicLong(FIRST_ID);
@NotNull @NotNull
private final Object lock = new Object(); private final Object lock = new Object();
@NotNull @NotNull
private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance(); private final TextProcessor<PreparedExpression, String> preprocessor = ToJsclTextProcessor.getInstance();
@NotNull @NotNull
private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10); private final Executor threadPoolExecutor = Executors.newFixedThreadPool(10);
public CalculatorImpl() { public CalculatorImpl() {
} this.addCalculatorEventListener(this);
}
@NotNull
public static String doConversion(@NotNull UnitConverter<String> converter, @NotNull
@Nullable String from, public static String doConversion(@NotNull UnitConverter<String> converter,
@NotNull UnitType<String> fromUnitType, @Nullable String from,
@NotNull UnitType<String> toUnitType) throws ConversionException{ @NotNull UnitType<String> fromUnitType,
final String result; @NotNull UnitType<String> toUnitType) throws ConversionException{
final String result;
if (StringUtils.isEmpty(from)) {
result = ""; if (StringUtils.isEmpty(from)) {
} else { result = "";
} else {
String to = null;
try { String to = null;
if (converter.isSupported(fromUnitType, toUnitType)) { try {
to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue(); if (converter.isSupported(fromUnitType, toUnitType)) {
} to = converter.convert(UnitImpl.newInstance(from, fromUnitType), toUnitType).getValue();
} catch (RuntimeException e) { }
throw new ConversionException(e); } catch (RuntimeException e) {
} throw new ConversionException(e);
}
result = to;
} result = to;
}
return result;
} return result;
}
@NotNull
private CalculatorEventDataId nextCalculatorEventDataId() { @NotNull
long eventId = counter.incrementAndGet(); private CalculatorEventDataId nextCalculatorEventDataId() {
return CalculatorEventDataIdImpl.newInstance(eventId, eventId); long eventId = counter.incrementAndGet();
} return CalculatorEventDataIdImpl.newInstance(eventId, eventId);
}
@NotNull
private CalculatorEventDataId nextEventDataId(@NotNull Long sequenceId) { @NotNull
long eventId = counter.incrementAndGet(); private CalculatorEventDataId nextEventDataId(@NotNull Long sequenceId) {
return CalculatorEventDataIdImpl.newInstance(eventId, sequenceId); long eventId = counter.incrementAndGet();
} return CalculatorEventDataIdImpl.newInstance(eventId, sequenceId);
}
/*
********************************************************************** /*
* **********************************************************************
* CALCULATION *
* * CALCULATION
********************************************************************** *
*/ **********************************************************************
*/
@NotNull
@Override @NotNull
public CalculatorEventDataId createFirstEventDataId() { @Override
return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID); public CalculatorEventDataId createFirstEventDataId() {
} return CalculatorEventDataIdImpl.newInstance(FIRST_ID, FIRST_ID);
}
@Override
public void evaluate(@NotNull JsclOperation operation, @Override
@NotNull String expression) { public void evaluate(@NotNull JsclOperation operation,
evaluate(operation, expression, null); @NotNull String expression) {
} evaluate(operation, expression, null);
}
@Override
@NotNull @Override
public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation, @NotNull
@NotNull final String expression, public CalculatorEventDataId evaluate(@NotNull final JsclOperation operation,
@Nullable final MessageRegistry mr) { @NotNull final String expression,
@Nullable final MessageRegistry mr) {
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
threadPoolExecutor.execute(new Runnable() {
@Override threadPoolExecutor.execute(new Runnable() {
public void run() { @Override
CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, mr); public void run() {
} CalculatorImpl.this.evaluate(eventDataId.getSequenceId(), operation, expression, mr);
}); }
});
return eventDataId;
} return eventDataId;
}
@NotNull
@Override @NotNull
public CalculatorEventDataId convert(@NotNull final Generic generic, @Override
@NotNull final NumeralBase to) { public CalculatorEventDataId convert(@NotNull final Generic generic,
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId(); @NotNull final NumeralBase to) {
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
threadPoolExecutor.execute(new Runnable() {
@Override threadPoolExecutor.execute(new Runnable() {
public void run() { @Override
final Long sequenceId = eventDataId.getSequenceId(); public void run() {
assert sequenceId != null; final Long sequenceId = eventDataId.getSequenceId();
assert sequenceId != null;
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_started, null);
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_started, null);
final NumeralBase from = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().getNumeralBase();
final NumeralBase from = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine().getNumeralBase();
if (from != to) {
String fromString = generic.toString(); if (from != to) {
if (!StringUtils.isEmpty(fromString)) { String fromString = generic.toString();
try { if (!StringUtils.isEmpty(fromString)) {
fromString = ToJsclTextProcessor.getInstance().process(fromString).getExpression(); try {
} catch (CalculatorParseException e) { fromString = ToJsclTextProcessor.getInstance().process(fromString).getExpression();
// ok, problems while processing occurred } catch (CalculatorParseException e) {
} // ok, problems while processing occurred
} }
}
// todo serso: continue
//doConversion(AndroidNumeralBase.getConverter(), fromString, AndroidNumeralBase.valueOf(fromString), AndroidNumeralBase.valueOf(to)); // todo serso: continue
} else { //doConversion(AndroidNumeralBase.getConverter(), fromString, AndroidNumeralBase.valueOf(fromString), AndroidNumeralBase.valueOf(to));
fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_finished, generic.toString()); } else {
} fireCalculatorEvent(newConversionEventData(sequenceId), CalculatorEventType.conversion_finished, generic.toString());
} }
}); }
});
return eventDataId;
} return eventDataId;
}
@NotNull
private CalculatorEventData newConversionEventData(@NotNull Long sequenceId) { @NotNull
return CalculatorEventDataImpl.newInstance(nextEventDataId(sequenceId)); @Override
} public CalculatorEventDataId fireCalculatorEvent(@NotNull final CalculatorEventType calculatorEventType, @Nullable final Object data) {
final CalculatorEventDataId eventDataId = nextCalculatorEventDataId();
private void evaluate(@NotNull Long sequenceId,
@NotNull JsclOperation operation, threadPoolExecutor.execute(new Runnable() {
@NotNull String expression, @Override
@Nullable MessageRegistry mr) { public void run() {
synchronized (lock) { fireCalculatorEvent(CalculatorEventDataImpl.newInstance(eventDataId), calculatorEventType, data);
}
PreparedExpression preparedExpression = null; });
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation)); return eventDataId;
}
try {
preparedExpression = preprocessor.process(expression); @NotNull
private CalculatorEventData newConversionEventData(@NotNull Long sequenceId) {
final String jsclExpression = preparedExpression.toString(); return CalculatorEventDataImpl.newInstance(nextEventDataId(sequenceId));
}
try {
private void evaluate(@NotNull Long sequenceId,
final Generic result = operation.evaluateGeneric(jsclExpression); @NotNull JsclOperation operation,
@NotNull String expression,
// NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!) @Nullable MessageRegistry mr) {
result.toString(); synchronized (lock) {
final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result); PreparedExpression preparedExpression = null;
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_result, data);
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_started, new CalculatorInputImpl(expression, operation));
} catch (AbstractJsclArithmeticException e) {
handleException(sequenceId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression)); try {
} preparedExpression = preprocessor.process(expression);
} catch (ArithmeticException e) { final String jsclExpression = preparedExpression.toString();
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
} catch (StackOverflowError e) { try {
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
} catch (jscl.text.ParseException e) { final Generic result = operation.evaluateGeneric(jsclExpression);
handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(e));
} catch (ParseInterruptedException e) { // NOTE: toString() method must be called here as ArithmeticOperationException may occur in it (just to avoid later check!)
result.toString();
// do nothing - we ourselves interrupt the calculations
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_cancelled, null); final CalculatorOutputImpl data = new CalculatorOutputImpl(operation.getFromProcessor().process(result), operation, result);
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_result, data);
} catch (CalculatorParseException e) {
handleException(sequenceId, operation, expression, mr, preparedExpression, e); } catch (AbstractJsclArithmeticException e) {
} finally { handleException(sequenceId, operation, expression, mr, new CalculatorEvalException(e, e, jsclExpression));
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_finished, null); }
}
} } catch (ArithmeticException e) {
} handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_001, MessageType.error, e.getMessage())));
} catch (StackOverflowError e) {
@NotNull handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(expression, new CalculatorMessage(CalculatorMessages.msg_002, MessageType.error)));
private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation, } catch (jscl.text.ParseException e) {
@NotNull String expression, handleException(sequenceId, operation, expression, mr, preparedExpression, new CalculatorParseException(e));
@NotNull Long calculationId) { } catch (ParseInterruptedException e) {
return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression);
} // do nothing - we ourselves interrupt the calculations
fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_cancelled, null);
private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation, } catch (CalculatorParseException e) {
@NotNull String expression, handleException(sequenceId, operation, expression, mr, preparedExpression, e);
@Nullable MessageRegistry mr, } finally {
@Nullable PreparedExpression preparedExpression, fireCalculatorEvent(newCalculationEventData(operation, expression, sequenceId), CalculatorEventType.calculation_finished, null);
@NotNull CalculatorParseException parseException) { }
}
if (operation == JsclOperation.numeric }
&& preparedExpression != null
&& preparedExpression.isExistsUndefinedVar()) { @NotNull
private CalculatorEventData newCalculationEventData(@NotNull JsclOperation operation,
evaluate(calculationId, JsclOperation.simplify, expression, mr); @NotNull String expression,
@NotNull Long calculationId) {
} return new CalculatorEvaluationEventDataImpl(CalculatorEventDataImpl.newInstance(nextEventDataId(calculationId)), operation, expression);
}
fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException));
} private void handleException(@NotNull Long calculationId,
@NotNull JsclOperation operation,
private void handleException(@NotNull Long calculationId, @NotNull String expression,
@NotNull JsclOperation operation, @Nullable MessageRegistry mr,
@NotNull String expression, @Nullable PreparedExpression preparedExpression,
@Nullable MessageRegistry mr, @NotNull CalculatorParseException parseException) {
@NotNull CalculatorEvalException evalException) {
if (operation == JsclOperation.numeric
if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) { && preparedExpression != null
evaluate(calculationId, JsclOperation.simplify, expression, mr); && preparedExpression.isExistsUndefinedVar()) {
}
evaluate(calculationId, JsclOperation.simplify, expression, mr);
fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException));
} }
/* fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(parseException));
********************************************************************** }
*
* EVENTS private void handleException(@NotNull Long calculationId,
* @NotNull JsclOperation operation,
********************************************************************** @NotNull String expression,
*/ @Nullable MessageRegistry mr,
@NotNull CalculatorEvalException evalException) {
@Override
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { if (operation == JsclOperation.numeric && evalException.getCause() instanceof NumeralBaseException) {
calculatorEventContainer.addCalculatorEventListener(calculatorEventListener); evaluate(calculationId, JsclOperation.simplify, expression, mr);
} }
@Override fireCalculatorEvent(newCalculationEventData(operation, expression, calculationId), CalculatorEventType.calculation_failed, new CalculatorFailureImpl(evalException));
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) { }
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener);
} /*
**********************************************************************
@Override *
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) { * EVENTS
calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data); *
} **********************************************************************
*/
@Override
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) { @Override
calculatorEventContainer.fireCalculatorEvents(calculatorEvents); public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
} calculatorEventContainer.addCalculatorEventListener(calculatorEventListener);
}
public static final class ConversionException extends Exception {
private ConversionException() { @Override
} public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculatorEventContainer.removeCalculatorEventListener(calculatorEventListener);
private ConversionException(Throwable throwable) { }
super(throwable);
} @Override
} public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
} calculatorEventContainer.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
@Override
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
calculatorEventContainer.fireCalculatorEvents(calculatorEvents);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
if ( calculatorEventType == CalculatorEventType.editor_state_changed ) {
final CalculatorEditorChangeEventData changeEventData = (CalculatorEditorChangeEventData) data;
evaluate(JsclOperation.numeric, changeEventData.getNewState().getText());
}
}
public static final class ConversionException extends Exception {
private ConversionException() {
}
private ConversionException(Throwable throwable) {
super(throwable);
}
}
}

View File

@ -16,10 +16,10 @@ public class CalculatorLocatorImpl implements CalculatorLocator {
private final CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl(); private final CalculatorDisplay calculatorDisplay = new CalculatorDisplayImpl();
@NotNull @NotNull
private final CalculatorEditor calculatorEditor = new CalculatorEditorImpl(); private final Calculator calculator = new CalculatorImpl();
@NotNull @NotNull
private final Calculator calculator = new CalculatorImpl(); private final CalculatorEditor calculatorEditor = new CalculatorEditorImpl(calculator);
@NotNull @NotNull
private static final CalculatorLocator instance = new CalculatorLocatorImpl(); private static final CalculatorLocator instance = new CalculatorLocatorImpl();

View File

@ -8,12 +8,7 @@ package org.solovyev.android.calculator;
import jscl.math.function.IConstant; import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.PreparedExpression;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.text.TextProcessor; import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.StartsWithFinder; import org.solovyev.common.StartsWithFinder;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.common.collections.CollectionsUtils; import org.solovyev.common.collections.CollectionsUtils;
@ -57,7 +52,7 @@ public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, St
MathType.Result mathTypeResult = null; MathType.Result mathTypeResult = null;
MathType.Result mathTypeBefore; MathType.Result mathTypeBefore;
final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorEngine.instance.getEngine()); final LiteNumberBuilder nb = new LiteNumberBuilder(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getEngine());
for (int i = 0; i < s.length(); i++) { for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') continue; if (s.charAt(i) == ' ') continue;
startsWithFinder.setI(i); startsWithFinder.setI(i);
@ -80,8 +75,7 @@ public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, St
if (mathTypeBefore != null && if (mathTypeBefore != null &&
(mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) && (mathTypeBefore.getMathType() == MathType.function || mathTypeBefore.getMathType() == MathType.operator) &&
CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) { CollectionsUtils.find(MathType.openGroupSymbols, startsWithFinder) != null) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_5, MessageType.error, CalculatorApplication.getInstance(), mathTypeBefore.getMatch()); throw new CalculatorParseException(i, s, new CalculatorMessage(CalculatorMessages.msg_005, MessageType.error, mathTypeBefore.getMatch()));
throw new CalculatorParseException(i, s, androidMessage);
} }
i = mathTypeResult.processToJscl(result, i); i = mathTypeResult.processToJscl(result, i);
@ -92,8 +86,7 @@ public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, St
@NotNull @NotNull
private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException { private static PreparedExpression replaceVariables(@NotNull final String s, int depth, @NotNull List<IConstant> undefinedVars) throws CalculatorParseException {
if (depth >= MAX_DEPTH) { if (depth >= MAX_DEPTH) {
final AndroidMessage androidMessage = new AndroidMessage(R.string.msg_6, MessageType.error, CalculatorApplication.getInstance()); throw new CalculatorParseException(s, new CalculatorMessage(CalculatorMessages.msg_006, MessageType.error));
throw new CalculatorParseException(s, androidMessage);
} else { } else {
depth++; depth++;
} }
@ -109,9 +102,9 @@ public class ToJsclTextProcessor implements TextProcessor<PreparedExpression, St
if (functionName == null) { if (functionName == null) {
String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder); String operatorName = CollectionsUtils.find(MathType.operator.getTokens(), startsWithFinder);
if (operatorName == null) { if (operatorName == null) {
String varName = CollectionsUtils.find(CalculatorEngine.instance.getVarsRegistry().getNames(), startsWithFinder); String varName = CollectionsUtils.find(CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().getNames(), startsWithFinder);
if (varName != null) { if (varName != null) {
final IConstant var = CalculatorEngine.instance.getVarsRegistry().get(varName); final IConstant var = CalculatorLocatorImpl.getInstance().getCalculatorEngine().getVarsRegistry().get(varName);
if (var != null) { if (var != null) {
if (!var.isDefined()) { if (!var.isDefined()) {
undefinedVars.add(var); undefinedVars.add(var);

View File

@ -140,6 +140,7 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
if (calculatorEventType.isOfType(CalculatorEventType.calculation_started, CalculatorEventType.calculation_result, CalculatorEventType.calculation_failed)) { if (calculatorEventType.isOfType(CalculatorEventType.calculation_started, CalculatorEventType.calculation_result, CalculatorEventType.calculation_failed)) {
if ( calculatorEventData.isAfter(this.lastEventDataId) ) { if ( calculatorEventData.isAfter(this.lastEventDataId) ) {
/*
switch (calculatorEventType) { switch (calculatorEventType) {
case calculation_started: case calculation_started:
@ -149,6 +150,7 @@ public class CalculatorHistoryImpl implements CalculatorHistory {
CalculatorLocatorImpl.getInstance().getCalculatorDisplay().get CalculatorLocatorImpl.getInstance().getCalculatorDisplay().get
CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), display); CalculatorHistoryState.newInstance(new TextViewEditorAdapter(this.editor), display);
*/
this.lastEventDataId = calculatorEventData; this.lastEventDataId = calculatorEventData;
} }

View File

@ -0,0 +1,200 @@
package org.solovyev.android.calculator;
import junit.framework.Assert;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 12:44
*/
public class CalculatorEditorImplTest {
@NotNull
private CalculatorEditor calculatorEditor;
@Before
public void setUp() throws Exception {
this.calculatorEditor = new CalculatorEditorImpl(new CalculatorImpl());
}
@Test
public void testInsert() throws Exception {
CalculatorEditorViewState viewState = this.calculatorEditor.getViewState();
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.insert("");
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.insert("test");
Assert.assertEquals("test", viewState.getText());
Assert.assertEquals(4, viewState.getSelection());
viewState = this.calculatorEditor.insert("test");
Assert.assertEquals("testtest", viewState.getText());
Assert.assertEquals(8, viewState.getSelection());
viewState = this.calculatorEditor.insert("");
Assert.assertEquals("testtest", viewState.getText());
Assert.assertEquals(8, viewState.getSelection());
viewState = this.calculatorEditor.insert("1234567890");
Assert.assertEquals("testtest1234567890", viewState.getText());
Assert.assertEquals(18, viewState.getSelection());
viewState = this.calculatorEditor.moveCursorLeft();
viewState = this.calculatorEditor.insert("9");
Assert.assertEquals("testtest12345678990", viewState.getText());
Assert.assertEquals(18, viewState.getSelection());
viewState = this.calculatorEditor.setCursorOnStart();
viewState = this.calculatorEditor.insert("9");
Assert.assertEquals("9testtest12345678990", viewState.getText());
Assert.assertEquals(1, viewState.getSelection());
viewState = this.calculatorEditor.erase();
viewState = this.calculatorEditor.insert("9");
Assert.assertEquals("9testtest12345678990", viewState.getText());
Assert.assertEquals(1, viewState.getSelection());
}
@Test
public void testErase() throws Exception {
this.calculatorEditor.setText("");
this.calculatorEditor.erase();
Assert.assertEquals("", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.setText("test");
this.calculatorEditor.erase();
Assert.assertEquals("tes", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("te", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("t", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.setText("1234");
this.calculatorEditor.moveCursorLeft();
this.calculatorEditor.erase();
Assert.assertEquals("124", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("14", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.erase();
Assert.assertEquals("4", this.calculatorEditor.getViewState().getText());
this.calculatorEditor.setText("1");
this.calculatorEditor.moveCursorLeft();
this.calculatorEditor.erase();
Assert.assertEquals("1", this.calculatorEditor.getViewState().getText());
}
@Test
public void testMoveSelection() throws Exception {
this.calculatorEditor.setText("");
CalculatorEditorViewState viewState = this.calculatorEditor.moveSelection(0);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(2);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(100);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-3);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-100);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("0123456789");
viewState = this.calculatorEditor.moveSelection(0);
Assert.assertEquals(10, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(1);
Assert.assertEquals(10, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-2);
Assert.assertEquals(8, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(1);
Assert.assertEquals(9, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-9);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-10);
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(2);
Assert.assertEquals(2, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(2);
Assert.assertEquals(4, viewState.getSelection());
viewState = this.calculatorEditor.moveSelection(-6);
Assert.assertEquals(0, viewState.getSelection());
}
@Test
public void testSetText() throws Exception {
CalculatorEditorViewState viewState = this.calculatorEditor.setText("test");
Assert.assertEquals("test", viewState.getText());
Assert.assertEquals(4, viewState.getSelection());
viewState = this.calculatorEditor.setText("testtest");
Assert.assertEquals("testtest", viewState.getText());
Assert.assertEquals(8, viewState.getSelection());
viewState = this.calculatorEditor.setText("");
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("testtest", 0);
Assert.assertEquals("testtest", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("testtest", 2);
Assert.assertEquals("testtest", viewState.getText());
Assert.assertEquals(2, viewState.getSelection());
viewState = this.calculatorEditor.setText("", 0);
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("", 3);
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("", -3);
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
viewState = this.calculatorEditor.setText("test");
Assert.assertEquals("test", viewState.getText());
Assert.assertEquals(4, viewState.getSelection());
viewState = this.calculatorEditor.setText("", 2);
Assert.assertEquals("", viewState.getText());
Assert.assertEquals(0, viewState.getSelection());
}
}

View File

@ -537,10 +537,8 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
public void eraseButtonClickHandler(@NotNull View v) { public void eraseButtonClickHandler(@NotNull View v) {
calculatorModel.doTextOperation(new CalculatorModel.TextOperation() { calculatorModel.doTextOperation(new CalculatorModel.TextOperation() {
@Override @Override
public void doOperation(@NotNull EditText editor) { public void doOperation(@NotNull CalculatorEditor editor) {
if (editor.getSelectionStart() > 0) { editor.erase();
editor.getText().delete(editor.getSelectionStart() - 1, editor.getSelectionStart());
}
} }
}); });
} }
@ -564,10 +562,10 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
public void pasteButtonClickHandler(@NotNull View v) { public void pasteButtonClickHandler(@NotNull View v) {
calculatorModel.doTextOperation(new CalculatorModel.TextOperation() { calculatorModel.doTextOperation(new CalculatorModel.TextOperation() {
@Override @Override
public void doOperation(@NotNull EditText editor) { public void doOperation(@NotNull CalculatorEditor editor) {
final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
if (clipboard.hasText()) { if (clipboard.hasText()) {
editor.getText().insert(editor.getSelectionStart(), clipboard.getText()); editor.insert(String.valueOf(clipboard.getText()));
} }
} }
}); });
@ -754,15 +752,18 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh
if ( dragDirection == DragDirection.left ) { if ( dragDirection == DragDirection.left ) {
CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() { CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() {
@Override @Override
public void doOperation(@NotNull EditText editor) { public void doOperation(@NotNull CalculatorEditor editor) {
final int cursorPosition = editor.getSelectionStart(); CalculatorEditorViewState viewState = editor.getViewState();
final StringBuilder text = new StringBuilder("(");
final String oldText = editor.getText().toString(); final int cursorPosition = viewState.getSelection();
text.append(oldText.substring(0, cursorPosition)); final String oldText = viewState.getText();
text.append(")");
text.append(oldText.substring(cursorPosition)); final StringBuilder newText = new StringBuilder(oldText.length() + 2);
editor.setText(text); newText.append("(");
editor.setSelection(cursorPosition + 2); newText.append(oldText.substring(0, cursorPosition));
newText.append(")");
newText.append(oldText.substring(cursorPosition));
editor.setText(newText.toString(), cursorPosition + 2);
} }
}); });
result = true; result = true;

View File

@ -14,7 +14,6 @@ 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.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -37,7 +36,7 @@ import org.solovyev.common.text.StringUtils;
* Date: 9/12/11 * Date: 9/12/11
* Time: 11:15 PM * Time: 11:15 PM
*/ */
public enum CalculatorModel implements HistoryControl<CalculatorHistoryState>, CalculatorEngineControl { public enum CalculatorModel implements HistoryControl<CalculatorHistoryState>, CalculatorEngineControl, CursorControl {
instance; instance;
@ -271,7 +270,7 @@ public enum CalculatorModel implements HistoryControl<CalculatorHistoryState>, C
doTextOperation(new CalculatorModel.TextOperation() { doTextOperation(new CalculatorModel.TextOperation() {
@Override @Override
public void doOperation(@NotNull EditText editor) { public void doOperation(@NotNull CalculatorEditor editor) {
int cursorPositionOffset = 0; int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text); final StringBuilder textToBeInserted = new StringBuilder(text);
@ -296,16 +295,36 @@ public enum CalculatorModel implements HistoryControl<CalculatorHistoryState>, C
} }
} }
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); editor.insert(textToBeInserted.toString());
editor.setSelection(editor.getSelectionStart() + cursorPositionOffset, editor.getSelectionEnd() + cursorPositionOffset); editor.moveSelection(cursorPositionOffset);
} }
}, delayEvaluate); }, delayEvaluate);
} }
} }
public static interface TextOperation { @Override
public void setCursorOnStart() {
this.editor.setCursorOnStart();
}
void doOperation(@NotNull EditText editor); @Override
public void setCursorOnEnd() {
this.editor.setCursorOnEnd();
}
@Override
public void moveCursorLeft() {
this.editor.moveCursorLeft();
}
@Override
public void moveCursorRight() {
this.editor.moveCursorRight();
}
public static interface TextOperation {
void doOperation(@NotNull CalculatorEditor editor);
} }

View File

@ -14,12 +14,13 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import com.google.ads.AdView; import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.ads.AdsController; import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.CalculatorEditor;
import org.solovyev.android.calculator.CalculatorLocatorImpl;
import org.solovyev.android.calculator.CalculatorModel; import org.solovyev.android.calculator.CalculatorModel;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
@ -175,19 +176,8 @@ public abstract class AbstractHistoryActivity extends ListActivity {
} }
public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) { public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState, @NotNull AbstractHistoryActivity activity) {
final EditorHistoryState editorState = historyState.getEditorState();
// before evaluating history item - clear display (in order to get Error message in display if evaluation fail) CalculatorLocatorImpl.getInstance().getCalculatorEditor().setText(StringUtils.getNotEmpty(editorState.getText(), ""), editorState.getCursorPosition())
CalculatorModel.instance.getDisplay().setText("");
CalculatorModel.instance.doTextOperation(new CalculatorModel.TextOperation() {
@Override
public void doOperation(@NotNull EditText editor) {
final EditorHistoryState editorState = historyState.getEditorState();
editor.setText(editorState.getText());
editor.setSelection(editorState.getCursorPosition());
}
}, false, historyState.getDisplayState().getJsclOperation(), true);
CalculatorModel.instance.setCursorOnEnd();
activity.finish(); activity.finish();
} }