Merge branch 'master' of github.com:numworks/epsilon into f7

This commit is contained in:
Émilie Feral
2019-03-14 11:41:01 +01:00
617 changed files with 11104 additions and 7728 deletions

25
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,25 @@
---
name: Bug report
about: Epsilon is not working like it should? Let us know!
labels: 'bug'
---
#### Describe the bug
A clear and concise description of what the bug is. Please describe a **single** bug per issue. Feel free to create multiple issues though!
#### Screenshots
Please provide at least one screenshot of the issue happening. This is by far the best way to quickly show any issue! To attach a screenshot, just go to our [online simulator](https://www.numworks.com/simulator), navigate to reproduce your issue, and click the "screenshot" button. Then drag'n'drop the file here!
#### To Reproduce
Steps to reproduce the behavior:
1. Go to the '...' app
2. Type '....'
3. Scroll down to '....'
4. See error
#### Expected behavior
A clear and concise description of what you expected to happen.
#### Environment
- Epsilon version (Settings > About > Software version).
- The platform(s) on which the problem happens: online simulator, actual device, etc...

View File

@@ -0,0 +1,22 @@
---
name: Feature request
about: Suggest an idea for an improvement of Epsilon
labels: 'enhancement'
---
#### Problem you'd like to fix
Is your feature request related to a problem? Please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Please describe a **single** improvement per issue. Feel free to open multiple issues though!
#### Screenshots
If possible, please attach a screenshot. You can go on our [online simulator](https://www.numworks.com/simulator), use the screenshot button, and drag'n'drop the file here.
#### Describe the solution you'd like
A clear and concise description of what you want to happen.
#### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
#### Additional context
Add any other context or screenshots about the feature request here.

50
.gitignore vendored
View File

@@ -1,52 +1,4 @@
# No objects files.
*.o
*.elf
*.exe
# No dependency files
*.d
# No lex / yacc generated files.
poincare/src/expression_lexer.cpp
poincare/src/expression_lexer.hpp
poincare/src/expression_parser.cpp
poincare/src/expression_parser.hpp
# No rulegen generated files
poincare/src/simplification/rulegen/rules_tokens.h
poincare/src/simplification/rulegen/rules_lexer.cpp
poincare/src/simplification/rulegen/rules_parser.cpp
poincare/src/simplification/rulegen/rulegen
poincare/src/simplification/demo_ruleset.h
# Font related generated files.
kandinsky/fonts/rasterizer
kandinsky/src/font_large.cpp
kandinsky/src/font_small.cpp
# No i18n headers
apps/i18n.h
apps/i18n.cpp
# No PicView generated files
apps/picview/image.raw
apps/picview/image.c
# No AST file
*.ast
*.ast.json
# Quiz output
quiz/src/symbols.c
# No generated icon & font files
*_icon.cpp
*_icon.h
*_font.c
*_font.h
# Ignore inliner binary
escher/image/inliner
# Ignore generated qtsrdefs file
python/port/genhdr/qstrdefs.generated.h
build

View File

@@ -1,5 +1,9 @@
language: cpp
env:
global:
- MAKEFLAGS="-j 2"
addons:
apt:
sources:
@@ -19,4 +23,4 @@ os: linux
script:
- set -e
- make clean && make epsilon.$EXT test.$EXT
- if [ "$PLATFORM" = "blackbox" ]; then ./test.$EXT; PLATFORM=blackbox make integration_tests; fi
- if [ "$PLATFORM" = "blackbox" ]; then build/blackbox/test.$EXT; PLATFORM=blackbox make integration_tests; fi

123
Makefile
View File

@@ -1,6 +1,27 @@
include build/config.mak
include scripts/config.mak
default: epsilon.$(EXE)
# Disable default Make rules
.SUFFIXES:
object_for = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1))))
default: $(BUILD_DIR)/epsilon.$(EXE)
# Define a standard rule helper
# If passed a last parameter value of with_local_version, we also define an
# extra rule that can build source files within the $(BUILD_DIR). This is useful
# for rules that can be applied for intermediate objects (for example, when
# going .png -> .cpp -> .o).
define rule_for
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/.
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
ifeq ($(strip $(5)),with_local_version)
$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3)))
@ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
$(Q) $(4)
endif
endef
.PHONY: info
info:
@@ -10,16 +31,24 @@ info:
@echo "EPSILON_APPS = $(EPSILON_APPS)"
@echo "EPSILON_I18N = $(EPSILON_I18N)"
# Each sub-Makefile can either add objects to the $(objs) variable or define a
# new executable target. The $(objs) variable lists the objects that will be
# linked to every executable being generated. Each Makefile is also responsible
# for keeping the $(product) variable updated. This variable lists all files
# that could be generated during the build and that needs to be cleaned up
# afterwards.
# Since we're building out-of-tree, we need to make sure the output directories
# are created, otherwise the receipes will fail (e.g. gcc will fail to create
# "output/foo/bar.o" because the directory "output/foo" doesn't exist).
# We need to mark those directories as precious, otherwise Make will try to get
# rid of them upon completion (and fail, since those folders won't be empty).
.PRECIOUS: $(BUILD_DIR)/. $(BUILD_DIR)%/.
$(BUILD_DIR)/.:
$(Q) mkdir -p $(dir $@)
$(BUILD_DIR)%/.:
$(Q) mkdir -p $(dir $@)
products :=
# To make objects dependent on their directory, we need a second expansion
.SECONDEXPANSION:
# Each sub-Makefile can either add sources to the $(src) variable or define a
# new executable target. The $(src) variable lists the sources that will be
# built and linked to every executable being generated.
# Library Makefiles
ifeq ($(USE_LIBA),0)
include liba/Makefile.bridge
else
@@ -34,43 +63,61 @@ include python/Makefile
include escher/Makefile
# Executable Makefiles
include apps/Makefile
include build/struct_layout/Makefile
include build/scenario/Makefile
include scripts/struct_layout/Makefile
include scripts/scenario/Makefile
include quiz/Makefile # Quiz needs to be included at the end
products += $(objs)
all_objs = $(filter %.o, $(products))
dependencies = $(all_objs:.o=.d)
-include $(dependencies)
products += $(dependencies)
$(all_objs): $(generated_headers)
epsilon.$(EXE): $(objs)
test.$(EXE): $(objs)
objs = $(call object_for,$(src))
.SECONDARY: $(objs)
%.$(EXE):
@echo "LD $@"
$(Q) $(LD) $^ $(LDFLAGS) -o $@
%.o: %.c
@echo "CC $@"
$(Q) $(CC) $(SFLAGS) $(CFLAGS) -c $< -o $@
# Load source-based dependencies
# Compilers can generate Makefiles that states the dependencies of a given
# objet to other source and headers. This serve no purpose for a clean build,
# but allows correct yet optimal incremental builds.
-include $(objs:.o=.d)
%.o: %.s
@echo "AS $@"
$(Q) $(CC) $(SFLAGS) -c $< -o $@
# Define rules for executables
# Those can be built directly with make executable.exe as a shortcut. They also
# depends on $(objs)
%.o: %.cpp
@echo "CXX $@"
$(Q) $(CXX) $(SFLAGS) $(CXXFLAGS) -c $< -o $@
executables = epsilon test flasher
define rules_for_executable
$$(BUILD_DIR)/$(1).$$(EXE): $$(objs)
.PHONY: $(1).$$(EXE)
$(1).$$(EXE): $$(BUILD_DIR)/$(1).$$(EXE)
endef
$(foreach executable,$(executables),$(eval $(call rules_for_executable,$(executable))))
# Define standard compilation rules
$(eval $(call rule_for, \
AS, %.o, %.s, \
$$(CC) $$(SFLAGS) -c $$< -o $$@ \
))
$(eval $(call rule_for, \
CC, %.o, %.c, \
$$(CC) $$(SFLAGS) $$(CFLAGS) -c $$< -o $$@, \
with_local_version \
))
$(eval $(call rule_for, \
CXX, %.o, %.cpp, \
$$(CC) $$(SFLAGS) $$(CXXFLAGS) -c $$< -o $$@, \
with_local_version \
))
$(eval $(call rule_for, \
LD, %.$$(EXE), , \
$$(LD) $$^ $$(LDFLAGS) -o $$@ \
))
.PHONY: clean
clean:
@echo "CLEAN"
$(Q) rm -f $(products)
$(Q) rm -rf $(BUILD_DIR)
.PHONY: cowsay_%
cowsay_%:
@@ -86,4 +133,4 @@ cowsay_%:
.PHONY: clena
clena: cowsay_CLENA clean
-include build/targets.$(PLATFORM).mak
-include scripts/targets.$(PLATFORM).mak

View File

@@ -10,26 +10,24 @@ apps =
# (path to the apps header).
$(foreach i,${EPSILON_APPS},$(eval include apps/$(i)/Makefile))
app_objs += $(addprefix apps/,\
apps_container.o\
apps_container_storage.o\
apps_window.o\
battery_timer.o\
battery_view.o\
constant.o\
backlight_dimming_timer.o\
empty_battery_window.o\
exam_pop_up_controller.o\
global_preferences.o\
i18n.o\
lock_view.o\
main.o\
math_toolbox.o\
shift_alpha_lock_view.o\
suspend_timer.o\
title_bar_view.o\
variable_box_controller.o\
variable_box_empty_controller.o\
app_src += $(addprefix apps/,\
apps_container.cpp \
apps_container_storage.cpp \
apps_window.cpp \
backlight_dimming_timer.cpp \
battery_timer.cpp \
battery_view.cpp \
constant.cpp \
empty_battery_window.cpp \
exam_pop_up_controller.cpp \
global_preferences.cpp \
lock_view.cpp \
math_toolbox.cpp \
shift_alpha_lock_view.cpp \
suspend_timer.cpp \
title_bar_view.cpp \
variable_box_controller.cpp \
variable_box_empty_controller.cpp \
)
snapshots_declaration = $(foreach i,$(apps),$(i)::Snapshot m_snapshot$(subst :,,$(i))Snapshot;)
@@ -40,8 +38,12 @@ snapshots_count = $(words $(apps))
snapshot_includes = $(foreach i,$(app_headers),-include $(i) )
epsilon_app_names = '$(foreach i,${EPSILON_APPS},"$(i)", )'
apps/apps_container_storage.o apps/main.o: CXXFLAGS += $(snapshot_includes) -DAPPS_CONTAINER_APPS_DECLARATION="$(apps_declaration)" -DAPPS_CONTAINER_SNAPSHOT_DECLARATIONS="$(snapshots_declaration)" -DAPPS_CONTAINER_SNAPSHOT_CONSTRUCTORS="$(snapshots_construction)" -DAPPS_CONTAINER_SNAPSHOT_LIST="$(snapshots_list)" -DAPPS_CONTAINER_SNAPSHOT_COUNT=$(snapshots_count) -DEPSILON_APPS_NAMES=$(epsilon_app_names)
$(call object_for,apps/apps_container_storage.cpp apps/main.cpp): CXXFLAGS += $(snapshot_includes) -DAPPS_CONTAINER_APPS_DECLARATION="$(apps_declaration)" -DAPPS_CONTAINER_SNAPSHOT_DECLARATIONS="$(snapshots_declaration)" -DAPPS_CONTAINER_SNAPSHOT_CONSTRUCTORS="$(snapshots_construction)" -DAPPS_CONTAINER_SNAPSHOT_LIST="$(snapshots_list)" -DAPPS_CONTAINER_SNAPSHOT_COUNT=$(snapshots_count) -DEPSILON_APPS_NAMES=$(epsilon_app_names)
# I18n file generation
# The header is refered to as <apps/i18n.h> so make sure it's findable this way
SFLAGS += -I$(BUILD_DIR)
i18n_files += $(addprefix apps/language_,$(addsuffix .universal.i18n, $(EPSILON_I18N)))
i18n_files += $(addprefix apps/,\
@@ -63,32 +65,35 @@ i18n_files += $(addprefix apps/,\
variables.pt.i18n\
)
apps/i18n.h: apps/i18n.cpp
apps/i18n.cpp: $(i18n_files)
@echo "I18N $@"
$(Q) $(PYTHON) apps/i18n.py --header $(subst .cpp,.h,$@) --implementation $@ --locales $(EPSILON_I18N) --files $^
$(eval $(call rule_for, \
I18N, \
apps/i18n.cpp, \
$(i18n_files), \
$$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ \
))
$(app_objs): apps/i18n.h
$(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp
products += apps/i18n.h apps/i18n.cpp
# Handle PNG files
app_images += apps/exam_icon.png
$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
# Tracking which source file uses which image is painful. But we need to ensure
# that a .png file has been inlined before building any source file that uses
# said image (because it will expect the ".h" file to be there).
# As a shortcut, we simply say that every app file depends on every image. In
# practice, this forces all the images to be before the app.
# Handle epsilon-only sources
# Other executables may want to do their own i18n and main. That's the case for
# the test runner. Let's add those files to a specific epsilon-only src. When
# building apps/i18n.o, the extension doesn't really matter since it'll be
# processed by the object_for function.
app_image_objs := $(app_images:.png=.o)
.SECONDARY: $(app_images:.png=.cpp)
$(app_objs): $(app_image_objs)
epsilon_src += $(addprefix apps/, \
main.cpp \
i18n.py \
)
epsilon.$(EXE): $(app_objs) $(app_image_objs)
all_app_src = $(app_src) $(epsilon_src)
TO_REMOVE := apps/main.o apps/i18n.o
TMP := $(app_objs) $(app_image_objs)
VAR := $(filter-out $(TO_REMOVE), $(TMP))
test.$(EXE): $(VAR)
$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
products += epsilon.$(EXE) $(app_objs) $(call INLINER_PRODUCTS,$(app_images))
$(BUILD_DIR)/epsilon.$(EXE): $(call object_for,$(epsilon_src))
src += $(app_src)

View File

@@ -84,7 +84,7 @@ AppsContainer::AppsContainer() :
* We just remove the circuit breaker for now.
* TODO: Put the Poincare circuit breaker back on epsilon's web emulator */
#else
Poincare::Expression::setCircuitBreaker(AppsContainer::poincareCircuitBreaker);
Poincare::Expression::SetCircuitBreaker(AppsContainer::poincareCircuitBreaker);
#endif
Ion::Storage::sharedStorage()->setDelegate(this);
}
@@ -152,18 +152,19 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
if (event == Ion::Events::USBEnumeration) {
if (Ion::USB::isPlugged()) {
App::Snapshot * activeSnapshot = (activeApp() == nullptr ? appSnapshotAtIndex(0) : activeApp()->snapshot());
if (activeApp() == nullptr || activeApp()->prepareForExit()) {
if (switchTo(usbConnectedAppSnapshot())) {
/* Just after a software update, the battery timer does not have time to
* fire before the calculator enters DFU mode. As the DFU mode blocks the
* event loop, we update the battery state "manually" here. */
updateBatteryState();
switchTo(usbConnectedAppSnapshot());
Ion::USB::DFU();
switchTo(activeSnapshot);
bool switched = switchTo(activeSnapshot);
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
didProcessEvent = true;
} else {
/* activeApp()->prepareForExit() returned false, which means that the
* app needs another event loop to prepare for being switched off.
/* We could not switch apps, which means that the current app needs
* another event loop to prepare for being switched off.
* Discard the current enumeration interruption.
* The USB host tries a few times in a row to enumerate the device, so
* hopefully the device will get another enumeration event soon and this
@@ -222,8 +223,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
return false;
}
void AppsContainer::switchTo(App::Snapshot * snapshot) {
assert(activeApp() == nullptr || activeApp()->prepareForExit());
bool AppsContainer::switchTo(App::Snapshot * snapshot) {
if (activeApp() && snapshot != activeApp()->snapshot()) {
resetShiftAlphaStatus();
}
@@ -235,7 +235,7 @@ void AppsContainer::switchTo(App::Snapshot * snapshot) {
if (snapshot) {
m_window.setTitle(snapshot->descriptor()->upperName());
}
Container::switchTo(snapshot);
return Container::switchTo(snapshot);
}
void AppsContainer::run() {
@@ -252,15 +252,14 @@ void AppsContainer::run() {
/* Normal execution. The exception checkpoint must be created before
* switching to the first app, because the first app might create nodes on
* the pool. */
bool switched =
#if EPSILON_ONBOARDING_APP
switchTo(onBoardingAppSnapshot());
switchTo(onBoardingAppSnapshot());
#else
if (numberOfApps() == 2) {
switchTo(appSnapshotAtIndex(1));
} else {
switchTo(appSnapshotAtIndex(0));
}
switchTo(appSnapshotAtIndex(numberOfApps() == 2 ? 1 : 0));
#endif
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
} else {
// Exception
if (activeApp() != nullptr) {
@@ -277,7 +276,9 @@ void AppsContainer::run() {
* history here, we will be stuck outside the calculation app. */
activeApp()->snapshot()->reset();
}
switchTo(appSnapshotAtIndex(0));
bool switched = switchTo(appSnapshotAtIndex(0));
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
Poincare::Tidy();
activeApp()->displayWarning(I18n::Message::PoolMemoryFull1, I18n::Message::PoolMemoryFull2, true);
}

View File

@@ -16,11 +16,6 @@
#include "backlight_dimming_timer.h"
#include "shared/global_context.h"
#define USE_PIC_VIEW_APP 0
#if USE_PIC_VIEW_APP
#include "picview/picview_app.h"
#endif
#ifdef EPSILON_BOOT_PROMPT
#include "on_boarding/pop_up_controller.h"
#endif
@@ -42,7 +37,7 @@ public:
VariableBoxController * variableBoxController();
void suspend(bool checkIfPowerKeyReleased = false);
virtual bool dispatchEvent(Ion::Events::Event event) override;
void switchTo(App::Snapshot * snapshot) override;
bool switchTo(App::Snapshot * snapshot) override;
void run() override;
bool updateBatteryState();
void refreshPreferences();
@@ -70,9 +65,6 @@ private:
AppsWindow m_window;
EmptyBatteryWindow m_emptyBatteryWindow;
#if USE_PIC_VIEW_APP
PicViewApp m_picViewApp;
#endif
Shared::GlobalContext m_globalContext;
MathToolbox m_mathToolbox;
VariableBoxController m_variableBoxController;

View File

@@ -1,16 +1,16 @@
apps += Calculation::App
app_headers += apps/calculation/app.h
app_objs += $(addprefix apps/calculation/,\
app.o\
calculation.o\
calculation_store.o\
edit_expression_controller.o\
expression_field.o\
history_view_cell.o\
history_controller.o\
scrollable_expression_view.o\
selectable_table_view.o\
app_src += $(addprefix apps/calculation/,\
app.cpp \
calculation.cpp \
calculation_store.cpp \
edit_expression_controller.cpp \
expression_field.cpp \
history_view_cell.cpp \
history_controller.cpp \
scrollable_expression_view.cpp \
selectable_table_view.cpp \
)
i18n_files += $(addprefix apps/calculation/,\
@@ -25,4 +25,4 @@ tests += $(addprefix apps/calculation/test/,\
calculation_store.cpp\
)
app_images += apps/calculation/calculation_icon.png
$(eval $(call depends_on_image,apps/calculation/app.cpp,apps/calculation/calculation_icon.png))

View File

@@ -1,7 +1,7 @@
#include "app.h"
#include "../apps_container.h"
#include "calculation_icon.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include <poincare/symbol.h>
using namespace Poincare;

View File

@@ -3,6 +3,7 @@
#include "../shared/poincare_helpers.h"
#include <poincare/symbol.h>
#include <poincare/undefined.h>
#include <poincare/unreal.h>
#include <string.h>
#include <cmath>
@@ -22,7 +23,14 @@ Calculation::Calculation() :
bool Calculation::operator==(const Calculation& c) {
return strcmp(m_inputText, c.m_inputText) == 0
&& strcmp(m_approximateOutputText, c.m_approximateOutputText) == 0;
&& strcmp(m_approximateOutputText, c.m_approximateOutputText) == 0
/* Some calculations can make appear trigonometric functions in their
* exact output. Their argument will be different with the angle unit
* preferences but both input and approximate output will be the same.
* For example, i^(sqrt(3)) = cos(sqrt(3)*pi/2)+i*sin(sqrt(3)*pi/2) if
* angle unit is radian and i^(sqrt(3)) = cos(sqrt(3)*90+i*sin(sqrt(3)*90)
* in degree. */
&& strcmp(m_exactOutputText, c.m_exactOutputText) == 0;
}
void Calculation::reset() {
@@ -41,9 +49,10 @@ void Calculation::setContent(const char * c, Context * context, Expression ansEx
* to keep Ans symbol in the calculation store. */
PoincareHelpers::Serialize(input, m_inputText, sizeof(m_inputText));
}
Expression exactOutput = PoincareHelpers::ParseAndSimplify(m_inputText, *context);
Expression exactOutput;
Expression approximateOutput;
PoincareHelpers::ParseAndSimplifyAndApproximate(m_inputText, &exactOutput, &approximateOutput, *context);
PoincareHelpers::Serialize(exactOutput, m_exactOutputText, sizeof(m_exactOutputText));
Expression approximateOutput = PoincareHelpers::Approximate<double>(exactOutput, *context);
PoincareHelpers::Serialize(approximateOutput, m_approximateOutputText, sizeof(m_approximateOutputText));
}
@@ -116,7 +125,7 @@ Expression Calculation::exactOutput() {
* 'cos(pi/4) = 0.999906' (which is true in degree). */
Expression exactOutput = Expression::Parse(m_exactOutputText);
if (exactOutput.isUninitialized()) {
return Undefined();
return Undefined::Builder();
}
return exactOutput;
}
@@ -142,21 +151,21 @@ bool Calculation::shouldOnlyDisplayApproximateOutput(Context * context) {
}
if (strcmp(m_exactOutputText, m_approximateOutputText) == 0) {
/* If the exact and approximate results' texts are equal and their layouts
* too, do not display the exact result. If, because of the number of
* significant digits, the two layouts are not equal, we display both. */
* too, do not display the exact result. If the two layouts are not equal
* because of the number of significant digits, we display both. */
return exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal;
}
if (strcmp(m_exactOutputText, Undefined::Name()) == 0) {
if (strcmp(m_exactOutputText, Undefined::Name()) == 0 || strcmp(m_approximateOutputText, Unreal::Name()) == 0) {
// If the approximate result is 'unreal' or the exact result is 'undef'
return true;
}
return input().isApproximate(*context) || exactOutput().isApproximate(*context);
}
bool Calculation::shouldOnlyDisplayExactOutput() {
/* If the approximateOutput is undef, we not not want to display it.
* This prevents:
/* If the approximateOutput is undef, do not display it. This prevents:
* x->f(x) from displaying x = undef
* x+x form displaying 2x = undef */
* x+x from displaying 2x = undef */
return strcmp(m_approximateOutputText, Undefined::Name()) == 0;
}
@@ -167,11 +176,12 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
constexpr int bufferSize = Constant::MaxSerializedExpressionSize;
char buffer[bufferSize];
Preferences * preferences = Preferences::sharedPreferences();
Expression exactOutputExpression = Expression::ParseAndSimplify(m_exactOutputText, *context, preferences->angleUnit());
Expression exactOutputExpression = PoincareHelpers::ParseAndSimplify(m_exactOutputText, *context);
if (exactOutputExpression.isUninitialized()) {
exactOutputExpression = Undefined();
exactOutputExpression = Undefined::Builder();
}
m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context), buffer, bufferSize, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context) ? EqualSign::Equal : EqualSign::Approximation;
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context), buffer, bufferSize, complexFormat, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context) ? EqualSign::Equal : EqualSign::Approximation;
return m_equalSign;
}

View File

@@ -88,14 +88,14 @@ void CalculationStore::tidy() {
Expression CalculationStore::ansExpression(Context * context) {
if (numberOfCalculations() == 0) {
return Rational(0);
return Rational::Builder(0);
}
Calculation * lastCalculation = calculationAtIndex(numberOfCalculations()-1);
/* Special case: the exact output is a Store/Equal expression.
* Store/Equal expression must be final root of an expression.
* To avoid turning 'ans->A' in '2->A->A' (or 2->A=A) which cannot be parsed),
* ans is replaced by the approximation output in when any Store or Equal
* expression appears.*/
* Store/Equal expression can only be at the root of an expression.
* To avoid turning 'ans->A' in '2->A->A' or '2=A->A' (which cannot be
* parsed), ans is replaced by the approximation output when any Store or
* Equal expression appears. */
bool exactOuptutInvolvesStoreEqual = lastCalculation->exactOutput().recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) {
return e.type() == ExpressionNode::Type::Store || e.type() == ExpressionNode::Type::Equal;
}, *context, false);

View File

@@ -150,7 +150,7 @@ KDCoordinate HistoryController::rowHeight(int j) {
}
Calculation * calculation = m_calculationStore->calculationAtIndex(j);
App * calculationApp = (App *)app();
return calculation->height(calculationApp->localContext()) + 3*HistoryViewCell::k_digitVerticalMargin;
return calculation->height(calculationApp->localContext()) + 4 * Metric::CommonSmallMargin;
}
int HistoryController::typeAtLocation(int i, int j) {

View File

@@ -10,9 +10,9 @@ namespace Calculation {
/* HistoryViewCellDataSource */
HistoryViewCellDataSource::HistoryViewCellDataSource() :
m_selectedSubviewType(HistoryViewCellDataSource::SubviewType::Output) {}
m_selectedSubviewType(SubviewType::Output) {}
void HistoryViewCellDataSource::setSelectedSubviewType(HistoryViewCellDataSource::SubviewType subviewType, HistoryViewCell * cell) {
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, HistoryViewCell * cell) {
m_selectedSubviewType = subviewType;
if (cell) {
cell->setHighlighted(cell->isHighlighted());
@@ -39,17 +39,18 @@ Shared::ScrollableExactApproximateExpressionsView * HistoryViewCell::outputView(
void HistoryViewCell::setEven(bool even) {
EvenOddCell::setEven(even);
m_inputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.setBackgroundColor(backgroundColor());
m_scrollableOutputView.evenOddCell()->setEven(even);
}
void HistoryViewCell::setHighlighted(bool highlight) {
assert(m_dataSource);
m_highlighted = highlight;
m_inputView.setBackgroundColor(backgroundColor());
m_inputView.setExpressionBackgroundColor(backgroundColor());
m_scrollableOutputView.evenOddCell()->setHighlighted(false);
if (isHighlighted()) {
if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input) {
m_inputView.setBackgroundColor(Palette::Select);
m_inputView.setExpressionBackgroundColor(Palette::Select);
} else {
m_scrollableOutputView.evenOddCell()->setHighlighted(true);
}
@@ -93,20 +94,21 @@ View * HistoryViewCell::subviewAtIndex(int index) {
}
void HistoryViewCell::layoutSubviews() {
KDCoordinate width = bounds().width();
KDCoordinate height = bounds().height();
KDCoordinate maxFrameWidth = bounds().width();
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
if (inputSize.width() + Metric::HistoryHorizontalMargin > width) {
m_inputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, inputSize.height()));
} else {
m_inputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, k_digitVerticalMargin, inputSize.width(), inputSize.height()));
}
m_inputView.setFrame(KDRect(
0,
0,
min(maxFrameWidth, inputSize.width()),
inputSize.height()
));
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
if (outputSize.width() + Metric::HistoryHorizontalMargin > width) {
m_scrollableOutputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, height - inputSize.height() - 3*k_digitVerticalMargin));
} else {
m_scrollableOutputView.setFrame(KDRect(width - outputSize.width() - Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, outputSize.width(), height - inputSize.height() - 3*k_digitVerticalMargin));
}
m_scrollableOutputView.setFrame(KDRect(
max(0, maxFrameWidth - outputSize.width()),
inputSize.height(),
min(maxFrameWidth, outputSize.width()),
bounds().height() - inputSize.height()
));
}
void HistoryViewCell::setCalculation(Calculation * calculation) {

View File

@@ -17,7 +17,7 @@ public:
Output
};
HistoryViewCellDataSource();
void setSelectedSubviewType(HistoryViewCellDataSource::SubviewType subviewType, HistoryViewCell * cell = nullptr);
void setSelectedSubviewType(SubviewType subviewType, HistoryViewCell * cell = nullptr);
SubviewType selectedSubviewType() { return m_selectedSubviewType; }
private:
SubviewType m_selectedSubviewType;
@@ -42,7 +42,6 @@ public:
void layoutSubviews() override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
constexpr static KDCoordinate k_digitVerticalMargin = 5;
Shared::ScrollableExactApproximateExpressionsView * outputView();
private:
constexpr static KDCoordinate k_resultWidth = 80;

View File

@@ -8,11 +8,17 @@ ScrollableExpressionView::ScrollableExpressionView(Responder * parentResponder)
ScrollableView(parentResponder, &m_expressionView, this),
m_expressionView()
{
setDecoratorType(ScrollView::Decorator::Type::Arrows);
setMargins(
Metric::CommonSmallMargin,
Metric::CommonLargeMargin,
Metric::CommonSmallMargin,
Metric::CommonLargeMargin
);
}
void ScrollableExpressionView::setLayout(Layout layout) {
m_expressionView.setLayout(layout);
layoutSubviews();
}
void ScrollableExpressionView::setBackgroundColor(KDColor backgroundColor) {
@@ -20,8 +26,8 @@ void ScrollableExpressionView::setBackgroundColor(KDColor backgroundColor) {
ScrollableView::setBackgroundColor(backgroundColor);
}
KDSize ScrollableExpressionView::minimalSizeForOptimalDisplay() const {
return m_expressionView.minimalSizeForOptimalDisplay();
void ScrollableExpressionView::setExpressionBackgroundColor(KDColor backgroundColor) {
m_expressionView.setBackgroundColor(backgroundColor);
}
}

View File

@@ -10,7 +10,7 @@ public:
ScrollableExpressionView(Responder * parentResponder);
void setLayout(Poincare::Layout layout);
void setBackgroundColor(KDColor backgroundColor) override;
KDSize minimalSizeForOptimalDisplay() const override;
void setExpressionBackgroundColor(KDColor backgroundColor);
private:
ExpressionView m_expressionView;
};

View File

@@ -8,7 +8,7 @@ CalculationSelectableTableView::CalculationSelectableTableView(Responder * paren
{
setVerticalCellOverlap(0);
setMargins(0);
setShowsIndicators(false);
setDecoratorType(ScrollView::Decorator::Type::None);
}
void CalculationSelectableTableView::scrollToCell(int i, int j) {

View File

@@ -1,5 +1,6 @@
#include <quiz.h>
#include <apps/shared/global_context.h>
#include <poincare/test/helper.h>
#include <string.h>
#include <assert.h>
#include "../calculation_store.h"
@@ -13,7 +14,7 @@ void assert_store_is(CalculationStore * store, const char * result[10]) {
}
}
QUIZ_CASE(calculation_store) {
QUIZ_CASE(calculation_store_ring_buffer) {
Shared::GlobalContext globalContext;
CalculationStore store;
quiz_assert(CalculationStore::k_maxNumberOfCalculations == 10);
@@ -50,7 +51,7 @@ QUIZ_CASE(calculation_store) {
store.deleteAll();
}
QUIZ_CASE(calculation_display_exact_approximate) {
QUIZ_CASE(calculation_ans) {
Shared::GlobalContext globalContext;
CalculationStore store;
@@ -65,49 +66,84 @@ QUIZ_CASE(calculation_display_exact_approximate) {
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true);
quiz_assert(strcmp(lastCalculation->approximateOutputText(),"2.6366666666667") == 0);
store.deleteAll();
store.push("1/2", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(&globalContext) == ::Calculation::Calculation::EqualSign::Equal);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false);
store.deleteAll();
store.push("1/3", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(&globalContext) == ::Calculation::Calculation::EqualSign::Approximation);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false);
store.deleteAll();
store.push("1/0", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == true);
quiz_assert(strcmp(lastCalculation->approximateOutputText(),"undef") == 0);
store.deleteAll();
store.push("2x-x", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == true);
quiz_assert(strcmp(lastCalculation->exactOutputText(),"x") == 0);
store.deleteAll();
store.push("[[1,2,3]]", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true);
store.deleteAll();
store.push("[[1,x,3]]", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true);
store.deleteAll();
store.push("28^7", &globalContext);
lastCalculation = store.calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false);
store.deleteAll();
}
void assertCalculationDisplay(const char * input, bool displayExactOutput, bool displayApproximateOutput, ::Calculation::Calculation::EqualSign sign, const char * exactOutput, const char * approximateOutput, Context * context, CalculationStore * store) {
char buffer[500];
strlcpy(buffer, input, sizeof(buffer));
translate_in_special_chars(buffer);
store->push(buffer, context);
::Calculation::Calculation * lastCalculation = store->calculationAtIndex(1);
quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == displayExactOutput);
quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(context) == displayApproximateOutput);
if (sign != ::Calculation::Calculation::EqualSign::Unknown) {
quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(context) == sign);
}
if (exactOutput) {
strlcpy(buffer, exactOutput, sizeof(buffer));
translate_in_special_chars(buffer);
quiz_assert(strcmp(lastCalculation->exactOutputText(), buffer) == 0);
}
if (approximateOutput) {
strlcpy(buffer, approximateOutput, sizeof(buffer));
translate_in_special_chars(buffer);
quiz_assert(strcmp(lastCalculation->approximateOutputText(),buffer) == 0);
}
store->deleteAll();
}
QUIZ_CASE(calculation_display_exact_approximate) {
Shared::GlobalContext globalContext;
CalculationStore store;
assertCalculationDisplay("1/2", false, false, ::Calculation::Calculation::EqualSign::Equal, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("1/3", false, false, ::Calculation::Calculation::EqualSign::Approximation, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("1/0", true, false, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store);
assertCalculationDisplay("2x-x", true, false, ::Calculation::Calculation::EqualSign::Unknown, "x", "undef", &globalContext, &store);
assertCalculationDisplay("[[1,2,3]]", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("[[1,x,3]]", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("28^7", false, false, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store);
assertCalculationDisplay("3+R(2)>a", false, false, ::Calculation::Calculation::EqualSign::Approximation, "R(2)+3", nullptr, &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3+2>a", false, true, ::Calculation::Calculation::EqualSign::Equal, "5", "5", &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3>a", false, true, ::Calculation::Calculation::EqualSign::Equal, "3", "3", &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy();
assertCalculationDisplay("3+x>f(x)", true, false, ::Calculation::Calculation::EqualSign::Unknown, "x+3", nullptr, &globalContext, &store);
Ion::Storage::sharedStorage()->recordNamed("f.func").destroy();
}
QUIZ_CASE(calculation_complex_format) {
Shared::GlobalContext globalContext;
CalculationStore store;
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real);
assertCalculationDisplay("1+I", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+I", &globalContext, &store);
assertCalculationDisplay("R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, "unreal", nullptr, &globalContext, &store);
assertCalculationDisplay("ln(-2)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
assertCalculationDisplay("R(-1)*R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-2", &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "4", &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
assertCalculationDisplay("1+I", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+I", &globalContext, &store);
assertCalculationDisplay("R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "I", &globalContext, &store);
assertCalculationDisplay("ln(-2)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store);
assertCalculationDisplay("R(-1)*R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-1", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "1+R(3)*I", nullptr, &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "-2+2*R(3)*I", nullptr, &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "root(8,4)/2+root(8,4)/2*I", nullptr, &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar);
assertCalculationDisplay("1+I", false, false, ::Calculation::Calculation::EqualSign::Approximation, "R(2)*X^(P/4*I)", nullptr, &globalContext, &store);
assertCalculationDisplay("R(-1)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "X^(P/2*I)", nullptr, &globalContext, &store);
assertCalculationDisplay("ln(-2)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store);
assertCalculationDisplay("R(-1)*R(-1)", false, false, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "X^(3.1415926535898*I)", &globalContext, &store);
assertCalculationDisplay("(-8)^(1/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "2*X^(P/3*I)", nullptr, &globalContext, &store);
assertCalculationDisplay("(-8)^(2/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "4*X^((2*P)/3*I)", nullptr, &globalContext, &store);
assertCalculationDisplay("(-2)^(1/4)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "root(2,4)*X^(P/4*I)", nullptr, &globalContext, &store);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
}

View File

@@ -1,26 +1,26 @@
apps += Code::App
app_headers += apps/code/app.h
app_objs += $(addprefix apps/code/,\
app.o\
console_controller.o\
console_edit_cell.o\
console_line_cell.o\
console_store.o\
editor_controller.o\
editor_view.o\
helpers.o\
menu_controller.o\
python_toolbox.o\
python_text_area.o\
sandbox_controller.o\
script.o\
script_name_cell.o\
script_node_cell.o\
script_parameter_controller.o\
script_store.o\
script_template.o\
variable_box_controller.o\
app_src += $(addprefix apps/code/,\
app.cpp \
console_controller.cpp \
console_edit_cell.cpp \
console_line_cell.cpp \
console_store.cpp \
editor_controller.cpp \
editor_view.cpp \
helpers.cpp \
menu_controller.cpp \
python_toolbox.cpp \
python_text_area.cpp \
sandbox_controller.cpp \
script.cpp \
script_name_cell.cpp \
script_node_cell.cpp \
script_parameter_controller.cpp \
script_store.cpp \
script_template.cpp \
variable_box_controller.cpp \
)
i18n_files += $(addprefix apps/code/,\
@@ -44,4 +44,4 @@ i18n_files += $(addprefix apps/code/,\
toolbox.universal.i18n\
)
app_images += apps/code/code_icon.png
$(eval $(call depends_on_image,apps/code/app.cpp,apps/code/code_icon.png))

View File

@@ -1,7 +1,7 @@
#include "app.h"
#include "../apps_container.h"
#include "code_icon.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include "helpers.h"
namespace Code {

View File

@@ -5,4 +5,4 @@ ExecuteScript = "Skript ausfuehren"
AutoImportScript = "Automatischer Import in Konsole"
DeleteScript = "Skript loeschen"
FunctionsAndVariables = "Funktionen und Variablen"
NonCompliantName = "Erlaubte Zeichen: a-z, 0-9, _"
AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _"

View File

@@ -5,4 +5,4 @@ ExecuteScript = "Execute script"
AutoImportScript = "Auto import in shell"
DeleteScript = "Delete script"
FunctionsAndVariables = "Functions and variables"
NonCompliantName = "Allowed characters: a-z, 0-9, _"
AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _"

View File

@@ -5,4 +5,4 @@ ExecuteScript = "Ejecutar el archivo"
AutoImportScript = "Importacion auto en interprete"
DeleteScript = "Eliminar el archivo"
FunctionsAndVariables = "Funciones y variables"
NonCompliantName = "Caracteres permitidos : a-z, 0-9, _"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"

View File

@@ -5,4 +5,4 @@ ExecuteScript = "Executer le script"
AutoImportScript = "Importation auto dans la console"
DeleteScript = "Supprimer le script"
FunctionsAndVariables = "Fonctions et variables"
NonCompliantName = "Caractères autorisés : a-z, 0-9, _"
AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _"

View File

@@ -5,4 +5,4 @@ ExecuteScript = "Executar o script"
AutoImportScript = "Importacao auto no interpretador"
DeleteScript = "Eliminar o script"
FunctionsAndVariables = "Funções e variáveis"
NonCompliantName = "Caracteres permitidos : a-z, 0-9, _"
AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"

View File

@@ -91,25 +91,51 @@ const char * ConsoleController::inputText(const char * prompt) {
AppsContainer * a = (AppsContainer *)(app()->container());
m_inputRunLoopActive = true;
// Set the prompt text
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
m_editCell.setPrompt(prompt);
const char * promptText = prompt;
char * s = const_cast<char *>(prompt);
if (promptText != nullptr) {
/* Set the prompt text. If the prompt text has a '\n', put the prompt text in
* the history until the last '\n', and put the remaining prompt text in the
* edit cell's prompt. */
char * lastCarriageReturn = nullptr;
while (*s != 0) {
if (*s == '\n') {
lastCarriageReturn = s;
}
s++;
}
if (lastCarriageReturn != nullptr) {
printText(prompt, lastCarriageReturn-prompt+1);
promptText = lastCarriageReturn+1;
}
}
m_editCell.setPrompt(promptText);
m_editCell.setText("");
// Run new input loop
// Reload the history
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
a->redrawWindow();
// Launch a new input loop
a->runWhile([](void * a){
ConsoleController * c = static_cast<ConsoleController *>(a);
return c->inputRunLoopActive();
}, this);
// Reset the prompt line
// Handle the input text
if (promptText != nullptr) {
printText(promptText, s - promptText);
}
const char * text = m_editCell.text();
printText(text, strlen(text));
flushOutputAccumulationBufferToStore();
m_consoleStore.deleteLastLineIfEmpty();
m_editCell.setPrompt(sStandardPromptText);
return m_editCell.text();
return text;
}
void ConsoleController::viewWillAppear() {
@@ -242,7 +268,11 @@ bool ConsoleController::textFieldShouldFinishEditing(TextField * textField, Ion:
}
bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
if (event == Ion::Events::Up && m_inputRunLoopActive) {
if (m_inputRunLoopActive
&& (event == Ion::Events::Up
|| event == Ion::Events::OK
|| event == Ion::Events::EXE))
{
m_inputRunLoopActive = false;
/* We need to return true here because we want to actually exit from the
* input run loop, which requires ending a dispatchEvent cycle. */
@@ -302,6 +332,13 @@ void ConsoleController::displaySandbox() {
stackViewController()->push(&m_sandboxController);
}
void ConsoleController::hideSandbox() {
if (!sandboxIsDisplayed()) {
return;
}
m_sandboxController.hide();
}
void ConsoleController::resetSandbox() {
if (!sandboxIsDisplayed()) {
return;
@@ -313,28 +350,33 @@ void ConsoleController::resetSandbox() {
* The text argument is not always null-terminated. */
void ConsoleController::printText(const char * text, size_t length) {
size_t textCutIndex = firstNewLineCharIndex(text, length);
// If there is no new line in text, just append it to the output accumulation
// buffer.
if (textCutIndex >= length) {
/* If there is no new line in text, just append it to the output
* accumulation buffer. */
appendTextToOutputAccumulationBuffer(text, length);
return;
}
// If there is a new line in the middle of the text, we have to store at least
// two new console lines in the console store.
if (textCutIndex < length - 1) {
/* If there is a new line in the middle of the text, we have to store at
* least two new console lines in the console store. */
printText(text, textCutIndex + 1);
printText(&text[textCutIndex+1], length - (textCutIndex + 1));
return;
}
// If there is a new line at the end of the text, we have to store the line in
// the console store.
if (textCutIndex == length - 1) {
appendTextToOutputAccumulationBuffer(text, length-1);
flushOutputAccumulationBufferToStore();
}
/* There is a new line at the end of the text, we have to store the line in
* the console store. */
assert(textCutIndex == length - 1);
appendTextToOutputAccumulationBuffer(text, length-1);
flushOutputAccumulationBufferToStore();
}
void ConsoleController::autoImportScript(Script script, bool force) {
if (sandboxIsDisplayed()) {
/* The sandbox might be displayed, for instance if we are auto-importing
* several scripts that draw at importation. In this case, we want to remove
* the sandbox. */
hideSandbox();
}
if (script.importationStatus() || force) {
// Step 1 - Create the command "from scriptName import *".
@@ -357,7 +399,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
// Step 2 - Run the command
runAndPrintForCommand(command);
}
if (force) {
if (!sandboxIsDisplayed() && force) {
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
m_editCell.setEditing(true);

View File

@@ -62,6 +62,7 @@ public:
// MicroPython::ExecutionEnvironment
void displaySandbox() override;
void hideSandbox() override;
void resetSandbox() override;
void printText(const char * text, size_t length) override;
const char * inputText(const char * prompt) override;

View File

@@ -31,10 +31,6 @@ ConsoleLineCell::ScrollableConsoleLineView::ScrollableConsoleLineView(Responder
{
}
KDSize ConsoleLineCell::ScrollableConsoleLineView::minimalSizeForOptimalDisplay() const {
return m_consoleLineView.minimalSizeForOptimalDisplay();
}
ConsoleLineCell::ConsoleLineCell(Responder * parentResponder) :
HighlightCell(),
Responder(parentResponder),

View File

@@ -48,7 +48,6 @@ private:
};
ScrollableConsoleLineView(Responder * parentResponder);
KDSize minimalSizeForOptimalDisplay() const override;
ConsoleLineView * consoleLineView() { return &m_consoleLineView; }
private:
ConsoleLineView m_consoleLineView;

View File

@@ -1,21 +1,10 @@
#include "console_store.h"
#include <assert.h>
#include <string.h>
namespace Code {
static inline int min(int x, int y) { return (x<y ? x : y); }
ConsoleStore::ConsoleStore() :
m_history{0}
{
}
void ConsoleStore::clear() {
assert(k_historySize > 0);
m_history[0] = 0;
}
void ConsoleStore::startNewSession() {
if (k_historySize < 1) {
return;

View File

@@ -2,14 +2,15 @@
#define CODE_CONSOLE_STORE_H
#include "console_line.h"
#include <assert.h>
#include <stddef.h>
namespace Code {
class ConsoleStore {
public:
ConsoleStore();
void clear();
ConsoleStore() : m_history{0} {}
void clear() { assert(k_historySize > 0); m_history[0] = 0; }
void startNewSession();
ConsoleLine lineAtIndex(int i) const;
int numberOfLines() const;

View File

@@ -1,6 +1,6 @@
#include "menu_controller.h"
#include "app.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include "../apps_container.h"
#include <assert.h>
#include <escher/metric.h>
@@ -26,7 +26,7 @@ MenuController::MenuController(Responder * parentResponder, App * pythonDelegate
m_shouldDisplayAddScriptRow(true)
{
m_selectableTableView.setMargins(0);
m_selectableTableView.setShowsIndicators(false);
m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None);
m_addNewScriptCell.setMessage(I18n::Message::AddScript);
for (int i = 0; i < k_maxNumberOfDisplayableScriptCells; i++) {
m_scriptCells[i].setParentResponder(&m_selectableTableView);
@@ -49,7 +49,7 @@ void MenuController::willExitResponderChain(Responder * nextFirstResponder) {
TextField * tf = static_cast<ScriptNameCell *>(m_selectableTableView.selectedCell())->textField();
if (tf->isEditing()) {
tf->setEditing(false, false);
textFieldDidAbortEditing(tf);
privateTextFieldDidAbortEditing(tf, false);
}
}
}
@@ -69,7 +69,8 @@ void MenuController::didBecomeFirstResponder() {
assert(m_selectableTableView.selectedRow() < m_scriptStore->numberOfScripts() + 1);
app()->setFirstResponder(&m_selectableTableView);
#if EPSILON_GETOPT
if (consoleController()->locked() && consoleController()->loadPythonEnvironment()) {
if (consoleController()->locked()) {
consoleController()->setAutoImport(true);
stackViewController()->push(consoleController());
return;
}
@@ -337,7 +338,7 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
} else if (error == Script::ErrorStatus::NameTaken) {
app()->displayWarning(I18n::Message::NameTaken);
} else if (error == Script::ErrorStatus::NonCompliantName) {
app()->displayWarning(I18n::Message::NonCompliantName);
app()->displayWarning(I18n::Message::AllowedCharactersaz09, I18n::Message::NameCannotStartWithNumber);
} else {
assert(error == Script::ErrorStatus::NotEnoughSpaceAvailable);
app()->displayWarning(I18n::Message::NameTooLong);
@@ -345,34 +346,6 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
return false;
}
bool MenuController::textFieldDidAbortEditing(TextField * textField) {
Script script = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow());
const char * scriptName = script.fullName();
if (strlen(scriptName) <= 1 + strlen(ScriptStore::k_scriptExtension)) {
// The previous text was an empty name. Use a numbered default script name.
char numberedDefaultName[Script::k_defaultScriptNameMaxSize];
bool foundDefaultName = Script::DefaultName(numberedDefaultName, Script::k_defaultScriptNameMaxSize);
if (!foundDefaultName) {
// If we did not find a default name, delete the script
deleteScript(script);
return true;
}
Script::ErrorStatus error = script.setBaseNameWithExtension(numberedDefaultName, ScriptStore::k_scriptExtension);
scriptName = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).fullName();
/* Because we use the numbered default name, the name should not be
* already taken. Plus, the script could be added only if the storage has
* enough available space to add a script named 'script99.py' */
(void) error; // Silence the "variable unused" warning if assertions are not enabled
assert(error == Script::ErrorStatus::None);
updateAddScriptRowDisplay();
}
textField->setText(scriptName);
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow());
app()->setFirstResponder(&m_selectableTableView);
static_cast<AppsContainer *>(const_cast<Container *>(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
return true;
}
bool MenuController::textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textSizeDidChange) {
int scriptExtensionLength = 1 + strlen(ScriptStore::k_scriptExtension);
if (textField->isEditing() && textField->cursorLocation() > textField->draftTextLength() - scriptExtensionLength) {
@@ -410,4 +383,39 @@ void MenuController::updateAddScriptRowDisplay() {
m_selectableTableView.reloadData();
}
bool MenuController::privateTextFieldDidAbortEditing(TextField * textField, bool menuControllerStaysInResponderChain) {
/* If menuControllerStaysInResponderChain is false, we do not want to use
* methods that might call setFirstResponder, because we might be in the
* middle of another setFirstResponder call. */
Script script = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow());
const char * scriptName = script.fullName();
if (strlen(scriptName) <= 1 + strlen(ScriptStore::k_scriptExtension)) {
// The previous text was an empty name. Use a numbered default script name.
char numberedDefaultName[Script::k_defaultScriptNameMaxSize];
bool foundDefaultName = Script::DefaultName(numberedDefaultName, Script::k_defaultScriptNameMaxSize);
if (!foundDefaultName) {
// If we did not find a default name, delete the script
deleteScript(script);
return true;
}
Script::ErrorStatus error = script.setBaseNameWithExtension(numberedDefaultName, ScriptStore::k_scriptExtension);
scriptName = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).fullName();
/* Because we use the numbered default name, the name should not be
* already taken. Plus, the script could be added only if the storage has
* enough available space to add a script named 'script99.py' */
(void) error; // Silence the "variable unused" warning if assertions are not enabled
assert(error == Script::ErrorStatus::None);
if (menuControllerStaysInResponderChain) {
updateAddScriptRowDisplay();
}
}
textField->setText(scriptName);
if (menuControllerStaysInResponderChain) {
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow());
app()->setFirstResponder(&m_selectableTableView);
}
static_cast<AppsContainer *>(const_cast<Container *>(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
return true;
}
}

View File

@@ -52,7 +52,9 @@ public:
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
bool textFieldDidAbortEditing(TextField * textField) override;
bool textFieldDidAbortEditing(TextField * textField) override {
return privateTextFieldDidAbortEditing(textField, true);
}
bool textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textSizeDidChange) override;
/* ButtonRowDelegate */
@@ -74,6 +76,7 @@ private:
void editScriptAtIndex(int scriptIndex);
void numberedDefaultScriptName(char * buffer);
void updateAddScriptRowDisplay();
bool privateTextFieldDidAbortEditing(TextField * textField, bool menuControllerStaysInResponderChain);
ScriptStore * m_scriptStore;
ScriptNameCell m_scriptCells[k_maxNumberOfDisplayableScriptCells];
EvenOddCellWithEllipsis m_scriptParameterCells[k_maxNumberOfDisplayableScriptCells];

View File

@@ -20,6 +20,10 @@ void SandboxController::reset() {
redrawWindow();
}
void SandboxController::hide() {
stackViewController()->pop();
}
void SandboxController::viewWillAppear() {
assert(m_executionEnvironment != nullptr);
m_executionEnvironment->setSandboxIsDisplayed(true);

View File

@@ -14,6 +14,7 @@ public:
SandboxController(Responder * parentResponder, MicroPython::ExecutionEnvironment * executionEnvironment);
StackViewController * stackViewController();
void reset();
void hide();
// ViewController
View * view() override { return &m_solidColorView; }

View File

@@ -37,17 +37,38 @@ bool Script::DefaultName(char buffer[], size_t bufferSize) {
return false;
}
bool isSmallLetterOrUnderscoreChar(const char c) {
return (c >= 'a' && c <= 'z') || c == '_';
}
bool isNumberChar(const char c) {
return c >= '0' && c <= '9';
}
bool Script::nameCompliant(const char * name) {
/* The name format is [a-z0-9_\.]+ */
const char * currentChar = name;
while (*currentChar != 0) {
if ((*currentChar >= 'a' && *currentChar <= 'z') || *currentChar == '_' || (*currentChar >= '0' && *currentChar <= '9') || *currentChar == '.') {
currentChar++;
continue;
}
/* We allow here the empty script name ".py", because it is the name used to
* create a new empty script. When naming or renaming a script, we check
* elsewhere that the name is no longer empty.
* The name format is ([a-z_][a-z0-9_]*)*\.py
*
* We do not allow upper cases in the script names because script names are
* used in the URLs of the NumWorks workshop website and we do not want
* problems with case sensitivity. */
const char * c = name;
if (*c == 0 || (!isSmallLetterOrUnderscoreChar(*c) && *c != '.')) {
/* The name cannot be empty. Its first letter must be in [a-z_] or the
* extension dot. */
return false;
}
return name != currentChar;
while (*c != 0) {
if (*c == '.' && strcmp(c+1, ScriptStore::k_scriptExtension) == 0) {
return true;
}
if (!isSmallLetterOrUnderscoreChar(*c) && !isNumberChar(*c)) {
return false;
}
c++;
}
return false;
}
bool Script::importationStatus() const {

View File

@@ -38,7 +38,7 @@ public:
private:
constexpr static size_t k_extensionLength = 1+ScriptStore::k_scriptExtensionLength; // '.' + "py"
constexpr static KDCoordinate k_leftMargin = Metric::HistoryHorizontalMargin;
constexpr static KDCoordinate k_leftMargin = Metric::CommonLargeMargin;
// View
int numberOfSubviews() const override { return 1; }

View File

@@ -2,7 +2,7 @@
#define CODE_SCRIPT_PARAMETER_CONTROLLER_H
#include <escher.h>
#include "../i18n.h"
#include <apps/i18n.h>
#include "script_store.h"
namespace Code {

View File

@@ -1,6 +1,6 @@
#include "empty_battery_window.h"
#include "global_preferences.h"
#include "i18n.h"
#include <apps/i18n.h>
extern "C" {
#include <assert.h>
}

View File

@@ -1,6 +1,6 @@
#include "exam_pop_up_controller.h"
#include "apps_container.h"
#include "i18n.h"
#include <apps/i18n.h>
#include "global_preferences.h"
#include <assert.h>

View File

@@ -1,7 +1,7 @@
#ifndef APPS_GLOBAL_PREFERENCES_H
#define APPS_GLOBAL_PREFERENCES_H
#include "i18n.h"
#include <apps/i18n.h>
class GlobalPreferences {
public:

View File

@@ -1,27 +1,27 @@
apps += Graph::App
app_headers += apps/graph/app.h
app_objs += $(addprefix apps/graph/,\
app.o\
storage_cartesian_function_store.o\
graph/banner_view.o\
graph/calculation_graph_controller.o\
graph/calculation_parameter_controller.o\
graph/curve_parameter_controller.o\
graph/extremum_graph_controller.o\
graph/graph_controller.o\
graph/graph_controller_helper.o\
graph/graph_view.o\
graph/integral_graph_controller.o\
graph/intersection_graph_controller.o\
graph/root_graph_controller.o\
graph/tangent_graph_controller.o\
list/list_parameter_controller.o\
list/storage_list_controller.o\
list/text_field_function_title_cell.o\
values/storage_derivative_parameter_controller.o\
values/storage_function_parameter_controller.o\
values/storage_values_controller.o\
app_src += $(addprefix apps/graph/,\
app.cpp \
storage_cartesian_function_store.cpp \
graph/banner_view.cpp \
graph/calculation_graph_controller.cpp \
graph/calculation_parameter_controller.cpp \
graph/curve_parameter_controller.cpp \
graph/extremum_graph_controller.cpp \
graph/graph_controller.cpp \
graph/graph_controller_helper.cpp \
graph/graph_view.cpp \
graph/integral_graph_controller.cpp \
graph/intersection_graph_controller.cpp \
graph/root_graph_controller.cpp \
graph/tangent_graph_controller.cpp \
list/list_parameter_controller.cpp \
list/storage_list_controller.cpp \
list/text_field_function_title_cell.cpp \
values/storage_derivative_parameter_controller.cpp \
values/storage_function_parameter_controller.cpp \
values/storage_values_controller.cpp \
)
i18n_files += $(addprefix apps/graph/,\
@@ -32,4 +32,4 @@ i18n_files += $(addprefix apps/graph/,\
base.pt.i18n\
)
app_images += apps/graph/graph_icon.png
$(eval $(call depends_on_image,apps/graph/app.cpp,apps/graph/graph_icon.png))

View File

@@ -1,7 +1,7 @@
#include "app.h"
#include "../apps_container.h"
#include "graph_icon.h"
#include "../i18n.h"
#include <apps/i18n.h>
using namespace Poincare;
using namespace Shared;

View File

@@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Spalte der Ableitungsfunktion"
HideDerivativeColumn = "Ableitungsfunktion ausblenden"
AllowedCharactersAZaz09 = "Erlaubte Zeichen: A-Z, a-z, 0-9, _"
ReservedName = "Reserviertes Wort"
NameCannotStartWithNumber = "Ein name darf nicht mit einer Zahl beginnen"

View File

@@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Derivative function column"
HideDerivativeColumn = "Hide the derivative function"
AllowedCharactersAZaz09 = "Allowed characters: A-Z, a-z, 0-9, _"
ReservedName = "Reserved name"
NameCannotStartWithNumber = "A name cannot start with a number"

View File

@@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Columna de la derivada"
HideDerivativeColumn = "Ocultar la derivada"
AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _"
ReservedName = "Nombre reservado"
NameCannotStartWithNumber = "Un nombre no puede empezar con un número"

View File

@@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Colonne de la fonction derivee"
HideDerivativeColumn = "Masquer la fonction derivee"
AllowedCharactersAZaz09 = "Caractères autorisés : A-Z, a-z, 0-9, _"
ReservedName = "Nom réservé"
NameCannotStartWithNumber = "Un nom ne peut pas commencer par un chiffre"

View File

@@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Coluna da funcao derivada"
HideDerivativeColumn = "Esconder funcao derivada"
AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _"
ReservedName = "Nome reservado"
NameCannotStartWithNumber = "Um nome não pode começar com um número"

View File

@@ -1,5 +1,5 @@
#include "banner_view.h"
#include "../../i18n.h"
#include <apps/i18n.h>
namespace Graph {

View File

@@ -10,7 +10,7 @@
#include "root_graph_controller.h"
#include "graph_view.h"
#include "banner_view.h"
#include "../../i18n.h"
#include <apps/i18n.h>
namespace Graph {

View File

@@ -1,6 +1,6 @@
#include "curve_parameter_controller.h"
#include "graph_controller.h"
#include "../../i18n.h"
#include <apps/i18n.h>
#include <assert.h>
using namespace Shared;

View File

@@ -5,6 +5,8 @@ using namespace Shared;
namespace Graph {
static inline float max(float x, float y) { return (x>y ? x : y); }
GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageCartesianFunctionStore * functionStore, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
StorageFunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion),
m_bannerView(),
@@ -37,17 +39,17 @@ void GraphController::setDisplayDerivativeInBanner(bool displayDerivative) {
m_displayDerivativeInBanner = displayDerivative;
}
float GraphController::interestingXRange() {
float GraphController::interestingXHalfRange() const {
float characteristicRange = 0.0f;
TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app();
for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) {
ExpiringPointer<StorageCartesianFunction> f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
float fRange = f->expressionReduced(myApp->localContext()).characteristicXRange(*(myApp->localContext()), Poincare::Preferences::sharedPreferences()->angleUnit());
if (!std::isnan(fRange)) {
characteristicRange = fRange > characteristicRange ? fRange : characteristicRange;
characteristicRange = max(fRange, characteristicRange);
}
}
return (characteristicRange > 0.0f ? 1.6f*characteristicRange : 10.0f);
return (characteristicRange > 0.0f ? 1.6f*characteristicRange : InteractiveCurveViewRangeDelegate::interestingXHalfRange());
}
int GraphController::estimatedBannerNumberOfLines() const {

View File

@@ -20,7 +20,7 @@ public:
void viewWillAppear() override;
bool displayDerivativeInBanner() const;
void setDisplayDerivativeInBanner(bool displayDerivative);
float interestingXRange() override;
float interestingXHalfRange() const override;
private:
int estimatedBannerNumberOfLines() const override;
void selectFunctionWithCursor(int functionIndex) override;

View File

@@ -1,6 +1,6 @@
#include "storage_list_controller.h"
#include "../app.h"
#include "../../i18n.h"
#include <apps/i18n.h>
#include <assert.h>
#include <escher/metric.h>
#include <apps/apps_container.h>
@@ -33,13 +33,20 @@ const char * StorageListController::title() {
void StorageListController::renameSelectedFunction() {
assert(selectedColumn() == 0);
assert(selectedRow() >= 0 && selectedRow() < numberOfRows()-1); // TODO change if sometimes the addFunction row is not displayed
// Increase the size of the name column
computeTitlesColumnWidth(true);
selectableTableView()->reloadData();
static_cast<AppsContainer *>(const_cast<Container *>(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::AlphaLock);
TextFieldFunctionTitleCell * selectedTitleCell = (TextFieldFunctionTitleCell *)(selectableTableView()->selectedCell());
selectedTitleCell->setHorizontalAlignment(1.0f);
app()->setFirstResponder(selectedTitleCell);
selectedTitleCell->setEditing(true);
}
bool StorageListController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
assert(textField != nullptr);
// Compute the new name
size_t textLength = strlen(text);
size_t argumentLength = StorageFunction::k_parenthesedArgumentLength;
@@ -107,6 +114,10 @@ bool StorageListController::textFieldDidFinishEditing(TextField * textField, con
}
bool StorageListController::textFieldDidAbortEditing(TextField * textField) {
assert(textField != nullptr);
// Put the name column back to normal size
computeTitlesColumnWidth();
selectableTableView()->reloadData();
ExpiringPointer<StorageFunction> function = modelStore()->modelForRecord(modelStore()->recordAtIndex(selectedRow()));
setFunctionNameInTextField(function, textField);
m_selectableTableView.selectedCell()->setHighlighted(true);
@@ -120,6 +131,7 @@ bool StorageListController::textFieldShouldFinishEditing(TextField * textField,
}
bool StorageListController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
assert(textField != nullptr);
if (textField->isEditing() && textField->shouldFinishEditing(event)) {
return false;
}
@@ -145,8 +157,14 @@ HighlightCell * StorageListController::expressionCells(int index) {
}
void StorageListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
assert(cell != nullptr);
assert(j >= 0 && j < modelStore()->numberOfModels());
TextFieldFunctionTitleCell * titleCell = static_cast<TextFieldFunctionTitleCell *>(cell);
// Update the corresponding expression cell in order to get the baseline
StorageExpressionModelListController::willDisplayExpressionCellAtIndex(m_selectableTableView.cellAtLocation(1, j), j);
titleCell->setBaseline(baseline(j));
if (!titleCell->isEditing()) {
// Set name and color if the name is not being edited
ExpiringPointer<StorageFunction> function = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
setFunctionNameInTextField(function, titleCell->textField());
KDColor functionNameColor = function->isActive() ? function->color() : Palette::GreyDark;
@@ -155,6 +173,8 @@ void StorageListController::willDisplayTitleCellAtIndex(HighlightCell * cell, in
}
void StorageListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) {
assert(cell != nullptr);
assert(j >= 0 && j < modelStore()->numberOfModels());
Shared::StorageFunctionListController::willDisplayExpressionCellAtIndex(cell, j);
FunctionExpressionCell * myCell = (FunctionExpressionCell *)cell;
ExpiringPointer<StorageFunction> f = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
@@ -163,9 +183,20 @@ void StorageListController::willDisplayExpressionCellAtIndex(HighlightCell * cel
}
void StorageListController::setFunctionNameInTextField(ExpiringPointer<StorageFunction> function, TextField * textField) {
assert(textField != nullptr);
char bufferName[BufferTextView::k_maxNumberOfChar];
function->nameWithArgument(bufferName, BufferTextView::k_maxNumberOfChar, modelStore()->symbol());
textField->setText(bufferName);
}
KDCoordinate StorageListController::privateBaseline(int j) const {
assert(j >= 0 && j < const_cast<StorageListController *>(this)->modelStore()->numberOfModels());
Shared::FunctionExpressionCell * cell = static_cast<Shared::FunctionExpressionCell *>((const_cast<SelectableTableView *>(&m_selectableTableView))->cellAtLocation(1, j));
Poincare::Layout layout = cell->layout();
if (layout.isUninitialized()) {
return -1;
}
return 0.5*(const_cast<StorageListController *>(this)->rowHeight(j)-layout.layoutSize().height())+layout.baseline();
}
}

View File

@@ -33,6 +33,7 @@ private:
return static_cast<Shared::TextFieldDelegateApp *>(app());
}
void setFunctionNameInTextField(Shared::ExpiringPointer<Shared::StorageFunction> function, TextField * textField);
KDCoordinate privateBaseline(int j) const override;
TextFieldFunctionTitleCell m_functionTitleCells[k_maxNumberOfDisplayableRows];
Shared::FunctionExpressionCell m_expressionCells[k_maxNumberOfDisplayableRows];
ListParameterController m_parameterController;

View File

@@ -4,11 +4,15 @@
namespace Graph {
static inline float min(float x, float y) { return (x<y ? x : y); }
static inline float max(float x, float y) { return (x>y ? x : y); }
TextFieldFunctionTitleCell::TextFieldFunctionTitleCell(StorageListController * listController, Orientation orientation, const KDFont * font) :
Shared::FunctionTitleCell(orientation),
Responder(listController),
m_textField(Shared::StorageFunction::k_parenthesedArgumentLength, this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, nullptr, listController, false, font, 0.5f, 0.5f)
{}
m_textField(Shared::StorageFunction::k_parenthesedArgumentLength, this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, nullptr, listController, false, font, 1.0f, 0.5f)
{
}
void TextFieldFunctionTitleCell::setHighlighted(bool highlight) {
EvenOddCell::setHighlighted(highlight);
@@ -44,8 +48,21 @@ void TextFieldFunctionTitleCell::setText(const char * title) {
m_textField.setText(title);
}
void TextFieldFunctionTitleCell::setHorizontalAlignment(float alignment) {
assert(alignment >= 0.0f && alignment <= 1.0f);
m_textField.setAlignment(alignment, verticalAlignment());
}
void TextFieldFunctionTitleCell::layoutSubviews() {
m_textField.setFrame(textFieldFrame());
KDRect frame = subviewFrame();
m_textField.setFrame(frame);
KDCoordinate maxTextFieldX = frame.width() - m_textField.minimalSizeForOptimalDisplay().width();
float horizontalAlignment = max(
0.0f,
min(
1.0f,
((float)(maxTextFieldX - k_textFieldRightMargin))/((float)maxTextFieldX)));
m_textField.setAlignment(horizontalAlignment, verticalAlignment());
}
void TextFieldFunctionTitleCell::didBecomeFirstResponder() {
@@ -54,12 +71,10 @@ void TextFieldFunctionTitleCell::didBecomeFirstResponder() {
}
}
KDRect TextFieldFunctionTitleCell::textFieldFrame() const {
KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness);
if (m_orientation == Orientation::VerticalIndicator){
textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness-k_separatorThickness, bounds().height()-k_separatorThickness);
}
return textFrame;
float TextFieldFunctionTitleCell::verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const {
assert(m_orientation == Orientation::VerticalIndicator);
KDCoordinate glyphHeight = font()->glyphSize().height();
return ((float)(expressionBaseline - glyphHeight/2))/((float)rowHeight+1-glyphHeight);
}
}

View File

@@ -16,6 +16,7 @@ public:
void setEditing(bool editing);
bool isEditing() const;
void setText(const char * textContent);
void setHorizontalAlignment(float alignment);
// FunctionTitleCell
void setColor(KDColor color) override;
@@ -38,10 +39,10 @@ public:
// Responder
void didBecomeFirstResponder() override;
protected:
KDRect textFieldFrame() const;
private:
constexpr static KDCoordinate k_textFieldRightMargin = 4;
constexpr static int k_textFieldBufferSize = Shared::StorageFunction::k_maxNameWithArgumentSize;
float verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const override;
Shared::TextFieldWithExtension m_textField;
char m_textFieldBuffer[k_textFieldBufferSize];
};

View File

@@ -19,7 +19,9 @@ bool StorageFunctionParameterController::handleEvent(Ion::Events::Event event) {
switch (selectedRow()) {
case 0:
{
function()->setDisplayDerivative(!function()->displayDerivative());
bool isDisplayingDerivative = function()->displayDerivative();
function()->setDisplayDerivative(!isDisplayingDerivative);
m_valuesController->selectCellAtLocation(isDisplayingDerivative ? m_selectedFunctionColumn : m_selectedFunctionColumn + 1, m_valuesController->selectedRow());
m_selectableTableView.reloadData();
return true;
}
@@ -57,9 +59,7 @@ int StorageFunctionParameterController::reusableCellCount() {
void StorageFunctionParameterController::viewWillAppear() {
StorageValuesFunctionParameterController::viewWillAppear();
if (function()->displayDerivative()) {
m_valuesController->selectCellAtLocation(m_valuesController->selectedColumn()+1, m_valuesController->selectedRow());
}
m_selectedFunctionColumn = m_valuesController->selectedColumn();
}
void StorageFunctionParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) {

View File

@@ -27,6 +27,8 @@ private:
#endif
MessageTableCellWithSwitch m_displayDerivativeColumn;
StorageValuesController * m_valuesController;
// Index of the column corresponding to the function in the values controller
int m_selectedFunctionColumn;
};
}

View File

@@ -1,15 +1,15 @@
app_objs += $(addprefix apps/hardware_test/,\
app.o\
arrow_view.o\
battery_test_controller.o\
code_128b_view.o\
keyboard_test_controller.o\
keyboard_view.o\
led_test_controller.o\
pattern.o\
pattern_view.o\
pop_up_controller.o\
screen_test_controller.o\
screen_test_controller.o\
serial_number_controller.o\
app_src += $(addprefix apps/hardware_test/,\
app.cpp \
arrow_view.cpp \
battery_test_controller.cpp \
code_128b_view.cpp \
keyboard_test_controller.cpp \
keyboard_view.cpp \
led_test_controller.cpp \
pattern.cpp \
pattern_view.cpp \
pop_up_controller.cpp \
screen_test_controller.cpp \
screen_test_controller.cpp \
serial_number_controller.cpp \
)

View File

@@ -1,5 +1,5 @@
#include "pop_up_controller.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include "../apps_container.h"
#include <assert.h>
@@ -41,7 +41,9 @@ PopUpController::ContentView::ContentView(Responder * parentResponder) :
m_okButton(this, I18n::Message::Ok, Invocation([](void * context, void * sender) {
PopUpController::ContentView * view = (PopUpController::ContentView *)context;
AppsContainer * appsContainer = (AppsContainer *)view->app()->container();
appsContainer->switchTo(appsContainer->hardwareTestAppSnapshot());
bool switched = appsContainer->switchTo(appsContainer->hardwareTestAppSnapshot());
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
return true;
}, this), KDFont::SmallFont),
m_warningTextView(KDFont::SmallFont, I18n::Message::Warning, 0.5, 0.5, KDColorWhite, KDColorBlack),

View File

@@ -1,13 +1,13 @@
app_objs += $(addprefix apps/home/,\
app.o\
app_cell.o\
controller.o\
app_src += $(addprefix apps/home/,\
app.cpp \
app_cell.cpp \
controller.cpp \
)
i18n_files += $(addprefix apps/home/,\
base.de.i18n\
base.en.i18n\
base.es.i18n\
base.fr.i18n\
base.pt.i18n\
base.de.i18n \
base.en.i18n \
base.es.i18n \
base.fr.i18n \
base.pt.i18n \
)

View File

@@ -1,5 +1,5 @@
#include "app.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include "../apps_container.h"
extern "C" {

View File

@@ -11,9 +11,8 @@ Controller::ContentView::ContentView(Controller * controller, SelectableTableVie
{
m_selectableTableView.setVerticalCellOverlap(0);
m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin);
m_selectableTableView.setColorsBackground(false);
m_selectableTableView.setIndicatorThickness(k_indicatorThickness);
m_selectableTableView.verticalScrollIndicator()->setMargin(k_indicatorMargin);
m_selectableTableView.setBackgroundColor(KDColorWhite);
static_cast<ScrollView::BarDecorator *>(m_selectableTableView.decorator())->verticalBar()->setMargin(k_indicatorMargin);
}
SelectableTableView * Controller::ContentView::selectableTableView() {
@@ -58,7 +57,9 @@ Controller::Controller(Responder * parentResponder, ::AppsContainer * container,
bool Controller::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedRow()*k_numberOfColumns+m_selectionDataSource->selectedColumn()+1));
bool switched = m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedRow()*k_numberOfColumns+m_selectionDataSource->selectedColumn()+1));
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
return true;
}

View File

@@ -43,7 +43,6 @@ private:
AppsContainer * m_container;
static constexpr KDCoordinate k_sideMargin = 4;
static constexpr KDCoordinate k_bottomMargin = 14;
static constexpr KDCoordinate k_indicatorThickness = 15;
static constexpr KDCoordinate k_indicatorMargin = 61;
static constexpr int k_numberOfColumns = 3;
static constexpr int k_maxNumberOfCells = 16;

View File

@@ -116,7 +116,7 @@ def print_header(data, path, locales):
def print_implementation(data, path, locales):
f = open(path, 'w')
f.write("#include \"i18n.h\"\n")
f.write("#include \"global_preferences.h\"\n")
f.write("#include <apps/global_preferences.h>\n")
f.write("#include <assert.h>\n\n");
f.write("namespace I18n {\n\n")

View File

@@ -1,9 +1,9 @@
app_objs += $(addprefix apps/on_boarding/,\
app.o\
language_controller.o\
logo_controller.o\
logo_view.o\
pop_up_controller.o\
app_src += $(addprefix apps/on_boarding/,\
app.cpp \
language_controller.cpp \
logo_controller.cpp \
logo_view.cpp \
pop_up_controller.cpp \
)
i18n_files += $(addprefix apps/on_boarding/,\
@@ -14,5 +14,4 @@ i18n_files += $(addprefix apps/on_boarding/,\
base.pt.i18n\
)
app_images += apps/on_boarding/logo_icon.png
$(eval $(call depends_on_image,apps/on_boarding/logo_view.cpp,apps/on_boarding/logo_icon.png))

View File

@@ -1,5 +1,4 @@
#include "logo_controller.h"
#include "logo_icon.h"
namespace OnBoarding {

View File

@@ -55,7 +55,9 @@ bool PopUpController::handleEvent(Ion::Events::Event event) {
app()->dismissModalViewController();
AppsContainer * appsContainer = (AppsContainer *)app()->container();
if (appsContainer->activeApp()->snapshot() == appsContainer->onBoardingAppSnapshot()) {
appsContainer->switchTo(appsContainer->appSnapshotAtIndex(0));
bool switched = appsContainer->switchTo(appsContainer->appSnapshotAtIndex(0));
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
}
return true;
}

View File

@@ -2,7 +2,7 @@
#define ON_BOARDING_POP_UP_CONTROLLER_H
#include <escher.h>
#include "../i18n.h"
#include <apps/i18n.h>
#include "../shared/message_view.h"
#include "../shared/ok_view.h"

View File

@@ -1,18 +0,0 @@
app_objs += $(addprefix apps/picview/,\
pic_view.o\
picview_app.o\
picview_controller.o\
)
apps/picview/pic_view.cpp: apps/picview/image.c
apps/picview/image.c: apps/picview/image.raw
@echo "RAW2C $@"
@echo "const /* Needed otherwise the image will eat up all RAM */" > $@
@xxd -i $^ >> $@
apps/picview/image.raw: apps/picview/image.png
@echo "PNG2RAW $@"
@ffmpeg -loglevel panic -vcodec png -i $^ -vcodec rawvideo -f rawvideo -pix_fmt rgb565 $@
products += $(addprefix apps/picview/, image.raw image.c)

View File

@@ -1,14 +0,0 @@
#include "pic_view.h"
#include <assert.h>
PicView::PicView() :
View() {
}
#include "image.c"
void PicView::drawRect(KDContext * ctx, KDRect rect) const {
KDColor * pixels = (KDColor *)apps_picview_image_raw;
assert(apps_picview_image_raw_len == bounds().width() * bounds().height() * sizeof(KDColor));
ctx->fillRectWithPixels(bounds(), pixels, nullptr);
}

View File

@@ -1,12 +0,0 @@
#ifndef PICVIEW_PIC_VIEW_H
#define PICVIEW_PIC_VIEW_H
#include <escher.h>
class PicView : public View {
public:
PicView();
void drawRect(KDContext * ctx, KDRect rect) const override;
};
#endif

View File

@@ -1,7 +0,0 @@
#include "picview_app.h"
PicViewApp::PicViewApp(Container * container) :
::App(container, &m_picViewController),
m_picViewController(PicViewController(&m_modalViewController))
{
}

View File

@@ -1,14 +0,0 @@
#ifndef PICVIEW_PICVIEW_APP_H
#define PICVIEW_PICVIEW_APP_H
#include <escher.h>
#include "picview_controller.h"
class PicViewApp : public App {
public:
PicViewApp(Container * container);
private:
PicViewController m_picViewController;
};
#endif

View File

@@ -1,18 +0,0 @@
#include "picview_controller.h"
PicViewController::PicViewController(Responder * parentResponder) :
ViewController(parentResponder),
m_view(PicView())
{
}
View * PicViewController::view() {
return &m_view;
}
/*
const char * PicViewController::title() {
return "PicView";
}
*/

View File

@@ -1,15 +0,0 @@
#ifndef PICVIEW_PICVIEW_CONTROLLER_H
#define PICVIEW_PICVIEW_CONTROLLER_H
#include <escher.h>
#include "pic_view.h"
class PicViewController : public ViewController {
public:
PicViewController(Responder * parentResponder);
View * view() override;
private:
PicView m_view;
};
#endif

View File

@@ -1,31 +1,30 @@
apps += Probability::App
app_headers += apps/probability/app.h
app_objs += $(addprefix apps/probability/,\
app.o\
calculation/calculation.o\
calculation/discrete_calculation.o\
calculation/left_integral_calculation.o\
calculation/right_integral_calculation.o\
calculation/finite_integral_calculation.o\
calculation_controller.o\
calculation_cell.o\
calculation_type_controller.o\
cell.o\
image_cell.o\
law/binomial_law.o\
law/erf_inv.o\
law/exponential_law.o\
law/law.o\
law/normal_law.o\
law/one_parameter_law.o\
law/poisson_law.o\
law/two_parameter_law.o\
law/uniform_law.o\
law_controller.o\
law_curve_view.o\
parameters_controller.o\
responder_image_cell.o\
app_src += $(addprefix apps/probability/,\
app.cpp \
calculation/calculation.cpp \
calculation/discrete_calculation.cpp \
calculation/left_integral_calculation.cpp \
calculation/right_integral_calculation.cpp \
calculation/finite_integral_calculation.cpp \
calculation_controller.cpp \
calculation_cell.cpp \
calculation_type_controller.cpp \
cell.cpp \
image_cell.cpp \
law/binomial_law.cpp \
law/erf_inv.cpp \
law/exponential_law.cpp \
law/law.cpp \
law/normal_law.cpp \
law/poisson_law.cpp \
law/two_parameter_law.cpp \
law/uniform_law.cpp \
law_controller.cpp \
law_curve_view.cpp \
parameters_controller.cpp \
responder_image_cell.cpp \
)
i18n_files += $(addprefix apps/probability/,\
@@ -40,25 +39,30 @@ tests += $(addprefix apps/probability/test/,\
erf_inv.cpp\
)
app_images += apps/probability/probability_icon.png
# Image dependencies
app_images += $(addprefix apps/probability/images/,\
binomial_icon.png\
calcul1_icon.png\
calcul2_icon.png\
calcul3_icon.png\
calcul4_icon.png\
exponential_icon.png\
focused_binomial_icon.png\
focused_calcul1_icon.png\
focused_calcul2_icon.png\
focused_calcul3_icon.png\
focused_calcul4_icon.png\
focused_exponential_icon.png\
focused_normal_icon.png\
focused_poisson_icon.png\
focused_uniform_icon.png\
normal_icon.png\
poisson_icon.png\
uniform_icon.png\
)
$(eval $(call depends_on_image,apps/probability/app.cpp,apps/probability/probability_icon.png))
$(eval $(call depends_on_image,apps/probability/law_controller.cpp,$(addprefix apps/probability/images/, \
binomial_icon.png \
exponential_icon.png \
focused_binomial_icon.png \
focused_exponential_icon.png \
focused_normal_icon.png \
focused_poisson_icon.png \
focused_uniform_icon.png \
normal_icon.png \
poisson_icon.png \
uniform_icon.png \
)))
$(eval $(call depends_on_image,$(addprefix apps/probability/,calculation_type_controller.cpp calculation_controller.cpp),$(addprefix apps/probability/images/, \
calcul1_icon.png \
calcul2_icon.png \
calcul3_icon.png \
calcul4_icon.png \
focused_calcul1_icon.png \
focused_calcul2_icon.png \
focused_calcul3_icon.png \
focused_calcul4_icon.png \
)))

View File

@@ -1,5 +1,5 @@
#include "app.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include "probability_icon.h"
#include <new>

View File

@@ -4,11 +4,6 @@
namespace Probability {
Calculation::Calculation():
m_law(nullptr)
{
}
void Calculation::setLaw(Law * law) {
m_law = law;
compute(0);

View File

@@ -13,7 +13,7 @@ public:
RightIntegral,
Discrete,
};
Calculation();
Calculation() : m_law(nullptr) {}
virtual ~Calculation() = default;
virtual Type type() = 0;
void setLaw(Law * law);

View File

@@ -13,18 +13,6 @@ DiscreteCalculation::DiscreteCalculation() :
compute(0);
}
Calculation::Type DiscreteCalculation::type() {
return Type::Discrete;
}
int DiscreteCalculation::numberOfParameters() {
return 2;
}
int DiscreteCalculation::numberOfEditableParameters() {
return 1;
}
I18n::Message DiscreteCalculation::legendForParameterAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
@@ -49,14 +37,6 @@ double DiscreteCalculation::parameterAtIndex(int index) {
return m_result;
}
double DiscreteCalculation::lowerBound() {
return m_abscissa;
}
double DiscreteCalculation::upperBound() {
return m_abscissa;
}
void DiscreteCalculation::compute(int indexKnownElement) {
if (m_law == nullptr) {
return;

View File

@@ -5,17 +5,17 @@
namespace Probability {
class DiscreteCalculation : public Calculation {
class DiscreteCalculation final : public Calculation {
public:
DiscreteCalculation();
Type type() override;
int numberOfParameters() override;
int numberOfEditableParameters() override;
Type type() override { return Type::Discrete; }
int numberOfParameters() override { return 2; }
int numberOfEditableParameters() override { return 1; }
I18n::Message legendForParameterAtIndex(int index) override;
void setParameterAtIndex(double f, int index) override;
double parameterAtIndex(int index) override;
double lowerBound() override;
double upperBound() override;
double lowerBound() override { return m_abscissa; }
double upperBound() override { return m_abscissa; }
private:
void compute(int indexKnownElement) override;
double m_abscissa;

View File

@@ -15,14 +15,6 @@ FiniteIntegralCalculation::FiniteIntegralCalculation() :
compute(0);
}
Calculation::Type FiniteIntegralCalculation::type() {
return Type::FiniteIntegral;
}
int FiniteIntegralCalculation::numberOfParameters() {
return 3;
}
int FiniteIntegralCalculation::numberOfEditableParameters() {
if (m_law->type() == Law::Type::Normal) {
return 3;
@@ -67,14 +59,6 @@ double FiniteIntegralCalculation::parameterAtIndex(int index) {
return m_result;
}
double FiniteIntegralCalculation::lowerBound() {
return m_lowerBound;
}
double FiniteIntegralCalculation::upperBound() {
return m_upperBound;
}
void FiniteIntegralCalculation::compute(int indexKnownElement) {
if (m_law == nullptr) {
return;

View File

@@ -8,14 +8,14 @@ namespace Probability {
class FiniteIntegralCalculation : public Calculation {
public:
FiniteIntegralCalculation();
Type type() override;
int numberOfParameters() override;
Type type() override { return Type::FiniteIntegral; }
int numberOfParameters() override { return 3; }
int numberOfEditableParameters() override;
I18n::Message legendForParameterAtIndex(int index) override;
void setParameterAtIndex(double f, int index) override;
double parameterAtIndex(int index) override;
double lowerBound() override;
double upperBound() override;
double lowerBound() override { return m_lowerBound; }
double upperBound() override { return m_upperBound; }
private:
void compute(int indexKnownElement) override;
double m_lowerBound;

View File

@@ -13,14 +13,6 @@ LeftIntegralCalculation::LeftIntegralCalculation() :
compute(0);
}
Calculation::Type LeftIntegralCalculation::type() {
return Type::LeftIntegral;
}
int LeftIntegralCalculation::numberOfParameters() {
return 2;
}
I18n::Message LeftIntegralCalculation::legendForParameterAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
@@ -48,10 +40,6 @@ double LeftIntegralCalculation::parameterAtIndex(int index) {
return m_result;
}
double LeftIntegralCalculation::upperBound() {
return m_upperBound;
}
void LeftIntegralCalculation::compute(int indexKnownElement) {
if (m_law == nullptr) {
return;

View File

@@ -5,15 +5,15 @@
namespace Probability {
class LeftIntegralCalculation : public Calculation {
class LeftIntegralCalculation final : public Calculation {
public:
LeftIntegralCalculation();
Type type() override;
int numberOfParameters() override;
Type type() override { return Type::LeftIntegral; }
int numberOfParameters() override { return 2; }
I18n::Message legendForParameterAtIndex(int index) override;
void setParameterAtIndex(double f, int index) override;
double parameterAtIndex(int index) override;
double upperBound() override;
double upperBound() override { return m_upperBound; }
private:
void compute(int indexKnownElement) override;
double m_upperBound;

View File

@@ -13,14 +13,6 @@ RightIntegralCalculation::RightIntegralCalculation() :
compute(0);
}
Calculation::Type RightIntegralCalculation::type() {
return Type::RightIntegral;
}
int RightIntegralCalculation::numberOfParameters() {
return 2;
}
I18n::Message RightIntegralCalculation::legendForParameterAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
@@ -48,10 +40,6 @@ double RightIntegralCalculation::parameterAtIndex(int index) {
return m_result;
}
double RightIntegralCalculation::lowerBound() {
return m_lowerBound;
}
void RightIntegralCalculation::compute(int indexKnownElement) {
if (m_law == nullptr) {
return;

View File

@@ -5,15 +5,15 @@
namespace Probability {
class RightIntegralCalculation : public Calculation {
class RightIntegralCalculation final : public Calculation {
public:
RightIntegralCalculation();
Type type() override;
int numberOfParameters() override;
Type type() override { return Type::RightIntegral; }
int numberOfParameters() override { return 2; }
I18n::Message legendForParameterAtIndex(int index) override;
void setParameterAtIndex(double f, int index) override;
double parameterAtIndex(int index) override;
double lowerBound() override;
double lowerBound() override { return m_lowerBound; }
private:
void compute(int indexKnownElement) override;
double m_lowerBound;

View File

@@ -1,6 +1,6 @@
#include "calculation_cell.h"
#include "responder_image_cell.h"
#include "../i18n.h"
#include <apps/i18n.h>
#include <assert.h>
namespace Probability {

View File

@@ -66,7 +66,7 @@ CalculationController::CalculationController(Responder * parentResponder, InputE
assert(calculation != nullptr);
m_selectableTableView.setMargins(k_tableMargin);
m_selectableTableView.setVerticalCellOverlap(0);
m_selectableTableView.setShowsIndicators(false);
m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None);
m_selectableTableView.setBackgroundColor(KDColorWhite);

View File

@@ -22,8 +22,7 @@ CalculationTypeController::CalculationTypeController(Responder * parentResponder
assert(m_calculation != nullptr);
m_selectableTableView.setMargins(0);
m_selectableTableView.setVerticalCellOverlap(0);
m_selectableTableView.setShowsIndicators(false);
m_selectableTableView.setColorsBackground(false);
m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None);
}
View * CalculationTypeController::view() {

View File

@@ -4,23 +4,6 @@
namespace Probability {
BinomialLaw::BinomialLaw() :
TwoParameterLaw(20.0, 0.5)
{
}
I18n::Message BinomialLaw::title() {
return I18n::Message::BinomialLaw;
}
Law::Type BinomialLaw::type() const {
return Type::Binomial;
}
bool BinomialLaw::isContinuous() const {
return false;
}
I18n::Message BinomialLaw::parameterNameAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {

View File

@@ -5,12 +5,12 @@
namespace Probability {
class BinomialLaw : public TwoParameterLaw {
class BinomialLaw final : public TwoParameterLaw {
public:
BinomialLaw();
I18n::Message title() override;
Type type() const override;
bool isContinuous() const override;
BinomialLaw() : TwoParameterLaw(20.0, 0.5) {}
I18n::Message title() override { return I18n::Message::BinomialLaw; }
Type type() const override { return Type::Binomial; }
bool isContinuous() const override { return false; }
float xMin() override;
float yMin() override;
float xMax() override;

View File

@@ -1,41 +1,15 @@
#include "exponential_law.h"
#include <assert.h>
#include <cmath>
#include <float.h>
#include <ion.h>
namespace Probability {
ExponentialLaw::ExponentialLaw() :
OneParameterLaw(1.0f)
{
}
I18n::Message ExponentialLaw::title() {
return I18n::Message::ExponentialLaw;
}
Law::Type ExponentialLaw::type() const {
return Type::Exponential;
}
bool ExponentialLaw::isContinuous() const {
return true;
}
I18n::Message ExponentialLaw::parameterNameAtIndex(int index) {
assert(index == 0);
return I18n::Message::Lambda;
}
I18n::Message ExponentialLaw::parameterDefinitionAtIndex(int index) {
assert(index == 0);
return I18n::Message::LambdaExponentialDefinition;
}
float ExponentialLaw::xMin() {
float max = xMax();
return - k_displayLeftMarginRatio * max;
return - k_displayLeftMarginRatio * xMax();
}
float ExponentialLaw::yMin() {
return -k_displayBottomMarginRatio * yMax();
}
float ExponentialLaw::xMax() {
@@ -44,11 +18,7 @@ float ExponentialLaw::xMax() {
if (result <= 0.0f) {
result = 1.0f;
}
return result*(1.0f+ k_displayRightMarginRatio);
}
float ExponentialLaw::yMin() {
return -k_displayBottomMarginRatio*yMax();
return result * (1.0f + k_displayRightMarginRatio);
}
float ExponentialLaw::yMax() {
@@ -59,14 +29,14 @@ float ExponentialLaw::yMax() {
if (result <= 0.0f) {
result = 1.0f;
}
return result*(1.0f+ k_displayTopMarginRatio);
return result * (1.0f + k_displayTopMarginRatio);
}
float ExponentialLaw::evaluateAtAbscissa(float x) const {
if (x < 0.0f) {
return NAN;
}
return m_parameter1*std::exp(-m_parameter1*x);
return m_parameter1 * std::exp(-m_parameter1 * x);
}
bool ExponentialLaw::authorizedValueAtIndex(float x, int index) const {
@@ -77,7 +47,7 @@ bool ExponentialLaw::authorizedValueAtIndex(float x, int index) const {
}
double ExponentialLaw::cumulativeDistributiveFunctionAtAbscissa(double x) const {
return 1.0 - std::exp((double)(-m_parameter1*x));
return 1.0 - std::exp((double)(-m_parameter1 * x));
}
double ExponentialLaw::cumulativeDistributiveInverseForProbability(double * probability) {

View File

@@ -2,21 +2,28 @@
#define PROBABILITE_EXPONENTIAL_LAW_H
#include "one_parameter_law.h"
#include <assert.h>
namespace Probability {
class ExponentialLaw : public OneParameterLaw {
class ExponentialLaw final : public OneParameterLaw {
public:
ExponentialLaw();
I18n::Message title() override;
Type type() const override;
bool isContinuous() const override;
ExponentialLaw() : OneParameterLaw(1.0f) {}
I18n::Message title() override { return I18n::Message::ExponentialLaw; }
Type type() const override { return Type::Exponential; }
bool isContinuous() const override { return true; }
float xMin() override;
float yMin() override;
float xMax() override;
float yMax() override;
I18n::Message parameterNameAtIndex(int index) override;
I18n::Message parameterDefinitionAtIndex(int index) override;
I18n::Message parameterNameAtIndex(int index) override {
assert(index == 0);
return I18n::Message::Lambda;
}
I18n::Message parameterDefinitionAtIndex(int index) override {
assert(index == 0);
return I18n::Message::LambdaExponentialDefinition;
}
float evaluateAtAbscissa(float x) const override;
bool authorizedValueAtIndex(float x, int index) const override;
double cumulativeDistributiveFunctionAtAbscissa(double x) const override;

View File

@@ -4,13 +4,8 @@
namespace Probability {
Law::Law() :
Shared::CurveViewRange()
{
}
float Law::xGridUnit() {
return computeGridUnit(Axis::X, xMin(), xMax());
return computeGridUnit(Axis::X, xMax() - xMin());
}
double Law::cumulativeDistributiveFunctionAtAbscissa(double x) const {

View File

@@ -4,13 +4,13 @@
#include <escher.h>
#include "../../constant.h"
#include "../../shared/curve_view_range.h"
#include "../../i18n.h"
#include <apps/i18n.h>
namespace Probability {
class Law : public Shared::CurveViewRange {
public:
Law();
Law() : Shared::CurveViewRange() {}
enum class Type : uint8_t{
Binomial,
Uniform,

View File

@@ -7,57 +7,8 @@
namespace Probability {
NormalLaw::NormalLaw() :
TwoParameterLaw(0.0f, 1.0f)
{
}
I18n::Message NormalLaw::title() {
return I18n::Message::NormalLaw;
}
Law::Type NormalLaw::type() const {
return Type::Normal;
}
bool NormalLaw::isContinuous() const {
return true;
}
I18n::Message NormalLaw::parameterNameAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
return I18n::Message::Mu;
} else {
return I18n::Message::Sigma;
}
}
I18n::Message NormalLaw::parameterDefinitionAtIndex(int index) {
assert(index >= 0 && index < 2);
if (index == 0) {
return I18n::Message::MeanDefinition;
} else {
return I18n::Message::DeviationDefinition;
}
}
float NormalLaw::xMin() {
if (m_parameter2 == 0.0f) {
return m_parameter1 - 1.0f;
}
return m_parameter1 - 5.0f*std::fabs(m_parameter2);
}
float NormalLaw::xMax() {
if (m_parameter2 == 0.0f) {
return m_parameter1 + 1.0f;
}
return m_parameter1 + 5.0f*std::fabs(m_parameter2);
}
float NormalLaw::yMin() {
return -k_displayBottomMarginRatio*yMax();
return - k_displayBottomMarginRatio * yMax();
}
float NormalLaw::yMax() {
@@ -66,14 +17,30 @@ float NormalLaw::yMax() {
if (std::isnan(result) || result <= 0.0f) {
result = 1.0f;
}
return result*(1.0f+ k_displayTopMarginRatio);
return result * (1.0f + k_displayTopMarginRatio);
}
I18n::Message NormalLaw::parameterNameAtIndex(int index) {
if (index == 0) {
return I18n::Message::Mu;
}
assert(index == 1);
return I18n::Message::Sigma;
}
I18n::Message NormalLaw::parameterDefinitionAtIndex(int index) {
if (index == 0) {
return I18n::Message::MeanDefinition;
}
assert(index == 1);
return I18n::Message::DeviationDefinition;
}
float NormalLaw::evaluateAtAbscissa(float x) const {
if (m_parameter2 == 0.0f) {
return NAN;
}
return (1.0f/(std::fabs(m_parameter2)*std::sqrt(2.0f*M_PI)))*std::exp(-0.5f*std::pow((x-m_parameter1)/m_parameter2,2));
return (1.0f/(std::fabs(m_parameter2) * std::sqrt(2.0f * M_PI))) * std::exp(-0.5f * std::pow((x - m_parameter1)/m_parameter2, 2));
}
bool NormalLaw::authorizedValueAtIndex(float x, int index) const {
@@ -94,17 +61,17 @@ void NormalLaw::setParameterAtIndex(float f, int index) {
}
double NormalLaw::cumulativeDistributiveFunctionAtAbscissa(double x) const {
if (m_parameter2 == 0.0f) {
if (m_parameter2 == 0.0f) {
return NAN;
}
return standardNormalCumulativeDistributiveFunctionAtAbscissa((x-m_parameter1)/std::fabs(m_parameter2));
}
double NormalLaw::cumulativeDistributiveInverseForProbability(double * probability) {
if (m_parameter2 == 0.0f) {
if (m_parameter2 == 0.0f) {
return NAN;
}
return standardNormalCumulativeDistributiveInverseForProbability(*probability)*std::fabs(m_parameter2) + m_parameter1;
return standardNormalCumulativeDistributiveInverseForProbability(*probability) * std::fabs(m_parameter2) + m_parameter1;
}
double NormalLaw::standardNormalCumulativeDistributiveFunctionAtAbscissa(double abscissa) const {
@@ -117,7 +84,7 @@ double NormalLaw::standardNormalCumulativeDistributiveFunctionAtAbscissa(double
if (abscissa > k_boundStandardNormalDistribution) {
return 1.0;
}
return 0.5+0.5*std::erf(abscissa/std::sqrt(2.0));
return 0.5 + 0.5 * std::erf(abscissa/std::sqrt(2.0));
}
double NormalLaw::standardNormalCumulativeDistributiveInverseForProbability(double probability) {
@@ -130,7 +97,15 @@ double NormalLaw::standardNormalCumulativeDistributiveInverseForProbability(doub
if (probability < 0.5) {
return -standardNormalCumulativeDistributiveInverseForProbability(1-probability);
}
return std::sqrt(2.0)*erfInv(2.0*probability-1.0);
return std::sqrt(2.0) * erfInv(2.0 * probability - 1.0);
}
float NormalLaw::xExtremum(bool min) const {
int coefficient = (min ? -1 : 1);
if (m_parameter2 == 0.0f) {
return m_parameter1 + coefficient * 1.0f;
}
return m_parameter1 + coefficient * 5.0f * std::fabs(m_parameter2);
}
}

Some files were not shown because too many files have changed in this diff Show More