diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..3ba13e0ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index c2111b8dc..8a993098a 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -2,7 +2,25 @@ name: Continuous integration
on: [pull_request, push]
jobs:
- build-simulator-android:
+ # nintendo_3ds:
+ # runs-on: ubuntu-latest
+ # steps:
+ # - run: wget https://github.com/devkitPro/pacman/releases/download/v1.0.2/devkitpro-pacman.amd64.deb -O /tmp/devkitpro-pacman.deb
+ # - run: yes | sudo dpkg -i /tmp/devkitpro-pacman.deb
+ # - run: yes | sudo dkp-pacman -Syu --needed devkitARM 3dstools libctru
+ # - run: echo ::set-env name=DEVKITPRO::/opt/devkitpro
+ # - run: echo ::set-env name=DEVKITARM::/opt/devkitpro/devkitARM
+ # - run: echo ::set-env name=PATH::$DEVKITPRO/tools/bin:$DEVKITARM/bin:$PATH
+
+ # - uses: actions/checkout@v1
+ # with:
+ # submodules: true
+ # - run: make -j2 PLATFORM=simulator TARGET=3ds
+ # - uses: actions/upload-artifact@master
+ # with:
+ # name: epsilon-3ds.3dsx
+ # path: output/release/simulator/3ds/epsilon.3dsx
+ android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
@@ -11,9 +29,9 @@ jobs:
- run: make -j2 PLATFORM=simulator TARGET=android
- uses: actions/upload-artifact@master
with:
- name: epsilon-simulator-android.apk
- path: output/release/simulator/android/app/outputs/apk/release/android-release-unsigned.apk
- build-device-n0100:
+ name: epsilon-android.apk
+ path: output/release/simulator/android/epsilon.apk
+ n0100:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -21,18 +39,40 @@ jobs:
- uses: actions/checkout@v1
with:
submodules: true
- - run: make -j2 MODEL=n0100 epsilon.dfu
- - run: make -j2 MODEL=n0100 epsilon.onboarding.dfu
- - run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu
- - run: make -j2 MODEL=n0100 epsilon.onboarding.beta.dfu
- - run: make -j2 MODEL=n0100 flasher.light.dfu
- - run: make -j2 MODEL=n0100 flasher.verbose.dfu
+ - run: mkdir final-output
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=en output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.en.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=fr output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.fr.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=nl output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.nl.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=pt output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.pt.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=it output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.it.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=de output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.de.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=es output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.es.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 EPSILON_I18N=hu output/release/device/n0100/epsilon.onboarding.two_binaries
+ - run: mv output/release/device/n0100/epsilon.onboarding.internal.bin final-output/epsilon.onboarding.internal.hu.bin
+ - run: rm output/release/device/n0100/apps/i18n.o output/release/device/n0100/apps/i18n.cpp
+ - run: make -j2 MODEL=n0100 output/release/device/n0100/flasher.light.bin
+ - run: mv output/release/device/n0100/flasher.light.bin final-output/flasher.light.bin
+ - run: find final-output/ -type f -exec bash -c "shasum -a 256 -b {} > {}.sha256" \;
+ - run: tar cvfz binpack-n0100.tgz final-output/*
- uses: actions/upload-artifact@master
with:
- name: epsilon-device-n0100.dfu
- path: output/release/device/n0100/epsilon.dfu
- - run: make -j2 MODEL=n0100 test.elf
- build-device-n0110:
+ name: epsilon-binpack-n0100.tgz
+ path: binpack-n0100.tgz
+ n0110:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -48,12 +88,13 @@ jobs:
- run: make -j2 flasher.verbose.dfu
- run: make -j2 bench.ram.dfu
- run: make -j2 bench.flash.dfu
+ - run: make -j2 binpack
+ - run: cp output/release/device/n0110/binpack-n0110-`git rev-parse HEAD | head -c 7`.tgz output/release/device/n0110/binpack-n0110.tgz
- uses: actions/upload-artifact@master
with:
- name: epsilon-device-n0110.dfu
- path: output/release/device/n0110/epsilon.dfu
- - run: make -j2 test.elf
- build-simulator-web:
+ name: epsilon-binpack-n0110.tgz
+ path: output/release/device/n0110/binpack-n0110.tgz
+ web:
runs-on: ubuntu-latest
steps:
- uses: numworks/setup-emscripten@v2
@@ -65,10 +106,9 @@ jobs:
- run: make -j2 PLATFORM=simulator TARGET=web
- uses: actions/upload-artifact@master
with:
- name: epsilon-simulator-web.zip
+ name: epsilon-web.zip
path: output/release/simulator/web/epsilon.zip
- - run: make -j2 PLATFORM=simulator TARGET=web test.headless.js
- build-simulator-linux:
+ linux:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
@@ -78,6 +118,6 @@ jobs:
- run: make -j2 PLATFORM=simulator
- uses: actions/upload-artifact@master
with:
- name: epsilon-simulator-linux.bin
+ name: epsilon-linux.bin
path: output/release/simulator/linux/epsilon.bin
- run: make -j2 PLATFORM=simulator test.headless.bin
diff --git a/.github/workflows/metric-workflow.yml b/.github/workflows/metric-workflow.yml
new file mode 100644
index 000000000..0a837d2d4
--- /dev/null
+++ b/.github/workflows/metric-workflow.yml
@@ -0,0 +1,38 @@
+name: Metrics
+on: [pull_request]
+
+jobs:
+ binary-size:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install dependencies
+ run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
+ - name: Install ARM toolchain
+ uses: numworks/setup-arm-toolchain@v1
+ - name: Checkout PR base
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ ref: ${{ github.event.pull_request.base.sha }}
+ path: base
+ - name: Build base
+ run: make -j2 -C base epsilon.elf
+ - name: Checkout PR head
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ ref: ${{ github.event.pull_request.head.sha }}
+ path: head
+ - name: Build head
+ run: make -j2 -C head epsilon.elf
+ - name: Retrieve binary size analysis
+ id: binary_size
+ run: echo "::set-output name=table::$(python3 head/build/metrics/binary_size.py base/output/release/device/n0110/epsilon.elf head/output/release/device/n0110/epsilon.elf --labels Base Head --sections .text .rodata .bss .data --custom 'Total (RAM)' .data .bss --custom 'Total (ROM)' .text .rodata .data --escape)"
+ - name: Prepare comment auth
+ run: echo "::set-env name=GITHUB_TOKEN::$(echo YjgxYTk1YTQ4YzYxNjU4ZTA3YWQzNDYwNTk3ZTI2MTlkODU5MThlOQo= | base64 --decode)"
+ - name: Add comment
+ uses: actions/github@v1.0.0
+ env:
+ GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }}
+ with:
+ args: comment ${{ steps.binary_size.outputs.table }}
diff --git a/.gitignore b/.gitignore
index 7212d2758..8645c08d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
/build/artifacts/
build/device/**/*.pyc
epsilon.elf
+epsilon.map
.vscode
.DS_Store
.gradle
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..7540a495a
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at getomega.pro@gmail.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
\ No newline at end of file
diff --git a/Makefile b/Makefile
index c260884d4..7587d6b06 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,16 @@
+# Disable default Make rules
+.SUFFIXES:
+
+# Define the default recipe
+default:
+
include build/config.mak
+include build/pimp.mak
+include build/defaults.mak
+include build/platform.$(PLATFORM).mak
+include build/toolchain.$(TOOLCHAIN).mak
+include build/variants.mak
+include build/helpers.mk
ifeq (${MODEL}, n0110)
apps_list = ${EPSILON_APPS}
@@ -10,42 +22,21 @@ ifdef FORCE_EXTERNAL
apps_list = ${EPSILON_APPS}
endif
-# Disable default Make rules
-.SUFFIXES:
-
-object_for = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1))))
-
-# Define the default recipe
-
-default:
-
-# 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_label
-@ echo "$(shell printf "%-8s" $(strip $(1)))$(@:$(BUILD_DIR)/%=%)"
-endef
-
-define rule_for
-ifeq ($(strip $(5)),with_local_version)
-$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) | $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
- @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
- $(Q) $(4)
+ifdef HOME_DISPLAY_EXTERNALS
+ ifneq ($(filter external,$(apps_list)),)
+ SFLAGS += -DHOME_DISPLAY_EXTERNALS
+ else
+ $(warning HOME_DISPLAY_EXTERNALS is set but external isn't included, ignoring flag.)
+ endif
endif
-$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. $(if $(findstring official,${MAKECMDGOALS}),official_authorization)
- @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)"
- $(Q) $(4)
-endef
.PHONY: info
info:
@echo "EPSILON_VERSION = $(EPSILON_VERSION)"
@echo "EPSILON_APPS = $(EPSILON_APPS)"
@echo "EPSILON_I18N = $(EPSILON_I18N)"
- @echo "OMEGA_THEME = $(OMEGA_THEME)"
+ @echo "THEME_NAME = $(THEME_NAME)"
+ @echo "THEME_REPO = $(THEME_REPO)"
@echo "BUILD_DIR = $(BUILD_DIR)"
@echo "PLATFORM" = $(PLATFORM)
@echo "DEBUG" = $(DEBUG)
@@ -78,6 +69,7 @@ help:
@echo " make PLATFORM=simulator TARGET=macos"
@echo " make PLATFORM=simulator TARGET=web"
@echo " make PLATFORM=simulator TARGET=windows"
+ @echo " make PLATFORM=simulator TARGET=3ds"
.PHONY: doc
doc:
@@ -107,7 +99,9 @@ $(BUILD_DIR)%/.:
# Each sub-Makefile can either add sources to $(%_src) variables or define a
# new executable target. The $(%_src) variables list the sources that can be
# built and linked to executables being generated.
-
+ifndef USE_LIBA
+ $(error platform.mak should define USE_LIBA)
+endif
ifeq ($(USE_LIBA),0)
include liba/Makefile.bridge
else
@@ -126,10 +120,10 @@ include build/struct_layout/Makefile
include build/scenario/Makefile
include quiz/Makefile # Quiz needs to be included at the end
-all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src)
+all_src = $(apps_src) $(escher_src) $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_device_flasher_src) $(ion_device_bench_src) $(tests_src)
# Make palette.h a dep for every source-file.
# This ensures that the theming engine works correctly.
-$(call object_for,$(all_app_src)): $(BUILD_DIR)/escher/palette.h
+$(call object_for,$(all_src)): $(BUILD_DIR)/escher/palette.h $(BUILD_DIR)/apps/i18n.h
all_objs = $(call object_for,$(all_src))
.SECONDARY: $(all_objs)
@@ -144,8 +138,7 @@ all_objs = $(call object_for,$(all_src))
include build/targets.mak
# Fill in the default recipe
-DEFAULT ?= $(BUILD_DIR)/epsilon.$(EXE)
-default: $(DEFAULT)
+default: $(firstword $(HANDY_TARGETS)).$(firstword $(HANDY_TARGETS_EXTENSIONS))
# Load standard build rules
include build/rules.mk
diff --git a/README.md b/README.md
index 87b5f20bf..a7de54752 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-

+
-
+
## About
@@ -19,10 +19,18 @@ Omega is a fork of Numworks' Epsilon, the OS that runs on their calculator, whic
- ~~32 KB Python heap instead of 16 KB~~ Now available on Epsilon `>=13.2.0`!
- And more...
-The main new features are listed [here](https://github.com/Omega-Numworks/Omega/wiki/Main-features), and the complete changelog can be found [here](https://github.com/quentinguidee/Omega/wiki/Complete-changelog).
+The main new features are listed [here](https://github.com/Omega-Numworks/Omega/wiki/Main-features), and the complete changelog can be found [here](https://github.com/Omega-Numworks/Omega/wiki/Complete-changelog).
## Installation
+### Automatic
+
+You can install Omega automatically on our website [here](https://getomega.web.app/) in the "install" page.
+
+
+
+### Manual
+
First of all, follow **step 1** [here](https://www.numworks.com/resources/engineering/software/build/). Then:
@@ -110,6 +118,28 @@ Also, you can change the number of processes that run in parallel during the bui
+
+ 3DS Simulator
+
+You need devkitPro and devkitARM installed and in your path (instructions [here](https://devkitpro.org/wiki/Getting_Started))
+```
+git clone --recursive https://github.com/Omega-Numworks/Omega.git
+cd Omega
+git checkout --recursive omega-dev
+make PLATFORM=simulator TARGET=3ds -j
+```
+You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink to launch it over the network:
+```
+3dslink output/release/simulator/3ds/epsilon.3dsx -a <3DS' IP ADDRESS>
+```
+
+
+
+If you need help, you can join our Discord server here : https://discord.gg/X2TWhh9
+
+
+---
+
## Contributing
To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omega/wiki/Contributing)
@@ -126,9 +156,7 @@ To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omeg
* [Omega Website](https://github.com/Omega-Numworks/Omega-Website)
* [Omega RPN `APP`](https://github.com/Omega-Numworks/Omega-RPN)
* [Omega Atom `APP`](https://github.com/Omega-Numworks/Omega-Atom)
-* [Omega Converter `APP`](https://github.com/Omega-Numworks/Omega-Converter)
* [Omega Design](https://github.com/Omega-Numworks/Omega-Design)
-* [Omega CLI Installer `BETA`](https://github.com/Omega-Numworks/Omega-CLI-Installer)
* [Omega App Template `BETA`](https://github.com/Omega-Numworks/Omega-App-Template)
## About Epsilon
@@ -137,15 +165,11 @@ Omega is a fork of Epsilon, a high-performance graphing calculator operating sys
You can try Epsilon straight from your browser in the [online simulator](https://www.numworks.com/simulator/).
-## Contributors ✨
-
-Thanks goes to these wonderful people!
-
-
-
## License
-NumWorks is a registered trademark. Omega is not affiliated with NumWorks.
+NumWorks is a registered trademark of NumWorks SAS, 24 Rue Godot de Mauroy, 75009 Paris, France.
+Nintendo and Nintendo 3DS are registered trademarks of Nintendo of America Inc, 4600 150th Ave NE, Redmond, WA 98052, USA.
+NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form with this project.
* NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
* Omega is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode).
diff --git a/apps/Makefile b/apps/Makefile
index b6d6ae189..381c0d7d2 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -1,3 +1,4 @@
+include apps/helpers.mk
include apps/shared/Makefile
include apps/home/Makefile
include apps/on_boarding/Makefile
@@ -10,8 +11,15 @@ apps =
# (path to the apps header).
$(foreach i,${apps_list},${eval include apps/$(i)/Makefile})
-app_src += $(addprefix apps/,\
+apps_src += $(addprefix apps/,\
+ alternate_empty_nested_menu_controller.cpp \
apps_container.cpp \
+ apps_container_launch_default.cpp:-onboarding \
+ apps_container_launch_on_boarding.cpp:+onboarding \
+ apps_container_prompt_beta.cpp:+beta \
+ apps_container_prompt_none.cpp:-beta \
+ apps_container_prompt_none.cpp:-update \
+ apps_container_prompt_update.cpp:+update \
apps_container_storage.cpp \
apps_window.cpp \
backlight_dimming_timer.cpp \
@@ -19,27 +27,21 @@ app_src += $(addprefix apps/,\
battery_view.cpp \
empty_battery_window.cpp \
exam_pop_up_controller.cpp \
+ exam_mode_configuration_official.cpp:+official \
+ exam_mode_configuration_non_official.cpp:-official \
global_preferences.cpp \
i18n.py \
lock_view.cpp \
main.cpp \
math_toolbox.cpp \
+ math_variable_box_controller.cpp \
+ math_variable_box_empty_controller.cpp \
shift_alpha_lock_view.cpp \
suspend_timer.cpp \
title_bar_view.cpp \
- variable_box_controller.cpp \
- variable_box_empty_controller.cpp \
)
tests_src += apps/exam_mode_configuration_non_official.cpp
-apps_official += apps/exam_mode_configuration_non_official.cpp
-apps_non_official += apps/exam_mode_configuration_non_official.cpp
-
-apps_launch_on_boarding_src += apps/apps_container_launch_on_boarding.cpp
-apps_launch_default_src += apps/apps_container_launch_default.cpp
-apps_prompt_none_src += apps/apps_container_prompt_none.cpp
-apps_prompt_beta_src += apps/apps_container_prompt_beta.cpp
-apps_prompt_update_src += apps/apps_container_prompt_update.cpp
snapshots_declaration = $(foreach i,$(apps),$(i)::Snapshot m_snapshot$(subst :,,$(i))Snapshot;)
apps_declaration = $(foreach i,$(apps),$(i) m_$(subst :,,$(i));)
@@ -57,33 +59,20 @@ $(call object_for,apps/apps_container_storage.cpp apps/apps_container.cpp apps/m
SFLAGS += -I$(BUILD_DIR)
i18n_files += $(addprefix apps/language_,$(addsuffix .universal.i18n, $(EPSILON_I18N)))
-i18n_files += $(addprefix apps/,\
- shared.de.i18n\
- shared.en.i18n\
- shared.es.i18n\
- shared.fr.i18n\
- shared.pt.i18n\
- shared.hu.i18n\
- shared.universal.i18n\
- toolbox.de.i18n\
- toolbox.en.i18n\
- toolbox.es.i18n\
- toolbox.fr.i18n\
- toolbox.pt.i18n\
- toolbox.hu.i18n\
- variables.de.i18n\
- variables.en.i18n\
- variables.es.i18n\
- variables.fr.i18n\
- variables.pt.i18n\
- variables.hu.i18n\
-)
+ifeq ($(EPSILON_GETOPT),1)
+i18n_files += $(addprefix apps/language_,$(addsuffix _iso6391.universal.i18n, $(EPSILON_I18N)))
+endif
+
+i18n_files += $(call i18n_with_universal_for,shared)
+i18n_files += $(call i18n_without_universal_for,toolbox)
+i18n_files += $(call i18n_without_universal_for,variables)
$(eval $(call rule_for, \
I18N, \
apps/i18n.cpp, \
$(i18n_files), \
- $$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ \
+ $$(PYTHON) apps/i18n.py --codepoints $(code_points) --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ --generateISO6391locales $$(EPSILON_GETOPT), \
+ global \
))
@@ -94,36 +83,30 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp
$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
-all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src)
+$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/apps/i18n.h
+$(call object_for,$(apps_src) $(tests_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h
-$(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
-
-apps_tests_src = $(app_calculation_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
+apps_tests_src = $(app_calculation_test_src) $(app_code_test_src) $(app_probability_test_src) $(app_regression_test_src) $(app_sequence_test_src) $(app_shared_test_src) $(app_statistics_test_src) $(app_settings_test_src) $(app_solver_test_src)
apps_tests_src += $(addprefix apps/,\
+ alternate_empty_nested_menu_controller.cpp \
global_preferences.cpp \
)
-
+ifeq ($(THEME_REPO),local)
$(foreach img,$(image_list), $(eval $(call rule_for, \
ICON, \
$(img), \
- $(addprefix themes/themes/, $(addsuffix .json, $(OMEGA_THEME))), \
- $$(PYTHON) themes/themes_manager.py -i $(OMEGA_THEME) $$@ $(BUILD_DIR)/ \
+ $(addprefix themes/themes/local/, $(addsuffix .json, $(THEME_NAME))), \
+ $$(PYTHON) themes/themes_manager.py -i $(THEME_REPO) $(THEME_NAME) $$@ $(BUILD_DIR)/, \
+ global \
)))
-
-# Configure variants
-apps_all_src = $(app_src)
-apps_all_src += $(apps_official) $(apps_non_official)
-apps_all_src += $(apps_launch_default_src) $(apps_launch_on_boarding_src)
-apps_all_src += $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src)
-
-apps_default_src = $(app_src) $(apps_non_official) $(apps_launch_default_src) $(apps_prompt_none_src)
-apps_official_default_src = $(app_src) $(apps_official) $(apps_launch_default_src) $(apps_prompt_none_src)
-apps_onboarding_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
-apps_official_onboarding_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_none_src)
-apps_onboarding_update_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
-apps_official_onboarding_update_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_update_src)
-apps_onboarding_beta_src = $(app_src) $(apps_non_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
-apps_official_onboarding_beta_src = $(app_src) $(apps_official) $(apps_launch_on_boarding_src) $(apps_prompt_beta_src)
+else
+$(foreach img,$(image_list), $(eval $(call rule_for, \
+ ICON, \
+ $(img), \
+ $(addsuffix /escher/palette.h, $(BUILD_DIR)), \
+ $$(PYTHON) themes/themes_manager.py -i $(THEME_REPO) $(THEME_NAME) $$@ $(BUILD_DIR)/, \
+ global \
+)))
+endif
diff --git a/apps/alternate_empty_nested_menu_controller.cpp b/apps/alternate_empty_nested_menu_controller.cpp
new file mode 100644
index 000000000..d3a89f0f9
--- /dev/null
+++ b/apps/alternate_empty_nested_menu_controller.cpp
@@ -0,0 +1,18 @@
+#include "alternate_empty_nested_menu_controller.h"
+
+void AlternateEmptyNestedMenuController::viewDidDisappear() {
+ if (isDisplayingEmptyController()) {
+ pop();
+ }
+ NestedMenuController::viewDidDisappear();
+}
+
+bool AlternateEmptyNestedMenuController::displayEmptyControllerIfNeeded() {
+ assert(!isDisplayingEmptyController());
+ // If the content is empty, we push an empty controller.
+ if (numberOfRows() == 0) {
+ push(emptyViewController());
+ return true;
+ }
+ return false;
+}
diff --git a/apps/alternate_empty_nested_menu_controller.h b/apps/alternate_empty_nested_menu_controller.h
new file mode 100644
index 000000000..d310b15b6
--- /dev/null
+++ b/apps/alternate_empty_nested_menu_controller.h
@@ -0,0 +1,19 @@
+#ifndef APPS_ALTERNATE_EMPTY_NESTED_MENU_CONTROLLER_H
+#define APPS_ALTERNATE_EMPTY_NESTED_MENU_CONTROLLER_H
+
+#include
+
+class AlternateEmptyNestedMenuController : public NestedMenuController {
+public:
+ AlternateEmptyNestedMenuController(I18n::Message title) :
+ NestedMenuController(nullptr, title)
+ {}
+ // View Controller
+ void viewDidDisappear() override;
+protected:
+ virtual ViewController * emptyViewController() = 0;
+ bool isDisplayingEmptyController() { return StackViewController::depth() == 2; }
+ bool displayEmptyControllerIfNeeded();
+};
+
+#endif
diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp
index 455fa3cbe..dd0b90207 100644
--- a/apps/apps_container.cpp
+++ b/apps/apps_container.cpp
@@ -37,7 +37,7 @@ AppsContainer::AppsContainer() :
m_usbConnectedSnapshot()
{
m_emptyBatteryWindow.setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), false);
-#if __EMSCRIPTEN__
+// #if __EMSCRIPTEN__
/* AppsContainer::poincareCircuitBreaker uses Ion::Keyboard::scan(), which
* calls emscripten_sleep. If we set the poincare circuit breaker, we would
* need to whitelist all the methods that might be in the call stack when
@@ -47,9 +47,13 @@ AppsContainer::AppsContainer() :
* quite painy to maintain).
* We just remove the circuit breaker for now.
* TODO: Put the Poincare circuit breaker back on epsilon's web emulator */
-#else
+
+ /*
+ * This can be run in Omega, since it uses WebASM.
+ */
+// #else
Poincare::Expression::SetCircuitBreaker(AppsContainer::poincareCircuitBreaker);
-#endif
+// #endif
Ion::Storage::sharedStorage()->setDelegate(this);
}
@@ -88,7 +92,7 @@ MathToolbox * AppsContainer::mathToolbox() {
return &m_mathToolbox;
}
-VariableBoxController * AppsContainer::variableBoxController() {
+MathVariableBoxController * AppsContainer::variableBoxController() {
return &m_variableBoxController;
}
@@ -218,6 +222,10 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
switchTo(appSnapshotAtIndex(0));
return true;
}
+ if (event == Ion::Events::ShiftHome) {
+ switchTo(appSnapshotAtIndex(1));
+ return true;
+ }
if (event == Ion::Events::OnOff) {
suspend(true);
return true;
diff --git a/apps/apps_container.h b/apps/apps_container.h
index 814e642a1..8af5abe7b 100644
--- a/apps/apps_container.h
+++ b/apps/apps_container.h
@@ -8,7 +8,7 @@
#include "apps_window.h"
#include "empty_battery_window.h"
#include "math_toolbox.h"
-#include "variable_box_controller.h"
+#include "math_variable_box_controller.h"
#include "exam_pop_up_controller.h"
#include "exam_pop_up_controller_delegate.h"
#include "battery_timer.h"
@@ -34,9 +34,9 @@ public:
void reset();
Poincare::Context * globalContext();
MathToolbox * mathToolbox();
- VariableBoxController * variableBoxController();
+ MathVariableBoxController * variableBoxController();
void suspend(bool checkIfOnOffKeyReleased = false);
- virtual bool dispatchEvent(Ion::Events::Event event) override;
+ bool dispatchEvent(Ion::Events::Event event) override;
bool switchTo(App::Snapshot * snapshot) override;
void run() override;
bool updateBatteryState();
@@ -70,7 +70,7 @@ private:
EmptyBatteryWindow m_emptyBatteryWindow;
Shared::GlobalContext m_globalContext;
MathToolbox m_mathToolbox;
- VariableBoxController m_variableBoxController;
+ MathVariableBoxController m_variableBoxController;
ExamPopUpController m_examPopUpController;
OnBoarding::PopUpController m_promptController;
BatteryTimer m_batteryTimer;
diff --git a/apps/atom b/apps/atom
index 8fa51525b..8f710a9d3 160000
--- a/apps/atom
+++ b/apps/atom
@@ -1 +1 @@
-Subproject commit 8fa51525b7547a73c99a3f6703e8fe67055095b0
+Subproject commit 8f710a9d3fa4daedde14ba489e418dbac5d590c5
diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile
index 078fb317e..453e46cf0 100644
--- a/apps/calculation/Makefile
+++ b/apps/calculation/Makefile
@@ -21,6 +21,7 @@ app_calculation_src = $(addprefix apps/calculation/,\
additional_outputs/trigonometry_graph_cell.cpp \
additional_outputs/trigonometry_list_controller.cpp \
additional_outputs/trigonometry_model.cpp \
+ additional_outputs/unit_list_controller.cpp \
app.cpp \
edit_expression_controller.cpp \
expression_field.cpp \
@@ -30,16 +31,9 @@ app_calculation_src = $(addprefix apps/calculation/,\
)
app_calculation_src += $(app_calculation_test_src)
-app_src += $(app_calculation_src)
+apps_src += $(app_calculation_src)
-i18n_files += $(addprefix apps/calculation/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
-)
+i18n_files += $(call i18n_without_universal_for,calculation/base)
tests_src += $(addprefix apps/calculation/test/,\
calculation_store.cpp\
diff --git a/apps/calculation/additional_outputs/complex_graph_cell.cpp b/apps/calculation/additional_outputs/complex_graph_cell.cpp
index 0d0bcf6fe..fe38bf910 100644
--- a/apps/calculation/additional_outputs/complex_graph_cell.cpp
+++ b/apps/calculation/additional_outputs/complex_graph_cell.cpp
@@ -1,4 +1,5 @@
#include "complex_graph_cell.h"
+#include
using namespace Shared;
using namespace Poincare;
@@ -12,7 +13,7 @@ ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) :
}
void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
- ctx->fillRect(rect, KDColorWhite);
+ ctx->fillRect(rect, Palette::BackgroundApps);
// Draw grid, axes and graduations
drawGrid(ctx, rect);
@@ -25,7 +26,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
assert(!std::isnan(real) && !std::isnan(imag) && !std::isinf(real) && !std::isinf(imag));
// Draw the segment from the origin to the dot (real, imag)
- drawSegment(ctx, rect, 0.0f, 0.0f, m_complex->real(), m_complex->imag(), Palette::GreyDark, false);
+ drawSegment(ctx, rect, 0.0f, 0.0f, m_complex->real(), m_complex->imag(), Palette::SecondaryText, false);
/* Draw the partial ellipse indicating the angle θ
* - the ellipse parameters are a = |real|/5 and b = |imag|/5,
@@ -39,7 +40,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
* and the line of equation (real*t,imag*t).
* (a*cos(t), b*sin(t)) = (real*t,imag*t) --> tan(t) = sign(a)*sign(b) (± π)
* --> t = π/4 [π/2] according to sign(a) and sign(b). */
- float th = real < 0.0f ? 3.0f*M_PI/4.0f : M_PI/4.0f;
+ float th = real < 0.0f ? (float)(3.0*M_PI_4) : (float)M_PI_4;
th = imag < 0.0f ? -th : th;
// Compute ellipsis parameters a and b
float factor = 5.0f;
@@ -48,7 +49,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
// Avoid flat ellipsis for edge cases (for real = 0, the case imag = 0 is excluded)
if (real == 0.0f) {
a = 1.0f/factor;
- th = imag < 0.0f ? -M_PI/2.0f : M_PI/2.0f;
+ th = imag < 0.0f ? (float)-M_PI_2 : (float)M_PI_2;
}
std::complex parameters(a,b);
drawCurve(ctx, rect, 0.0f, 1.0f, 0.01f,
@@ -58,27 +59,27 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
float a = parameters.real();
float b = parameters.imag();
return Poincare::Coordinate2D(a*std::cos(t*th), b*std::sin(t*th));
- }, ¶meters, &th, false, Palette::GreyDark, false);
+ }, ¶meters, &th, false, Palette::SecondaryText, false);
// Draw dashed segment to indicate real and imaginary
- drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, real, 0.0f, imag, Palette::Red, 1, 3);
- drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, imag, 0.0f, real, Palette::Red, 1, 3);
+ drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, real, 0.0f, imag, Palette::CalculationTrigoAndComplexForeground, 1, 3);
+ drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, imag, 0.0f, real, Palette::CalculationTrigoAndComplexForeground, 1, 3);
// Draw complex position on the plan
- drawDot(ctx, rect, real, imag, Palette::Red, Size::Large);
+ drawDot(ctx, rect, real, imag, Palette::CalculationTrigoAndComplexForeground, Size::Large);
// Draw labels
// 're(z)' label
- drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::Red, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
+ drawLabel(ctx, rect, real, 0.0f, "re(z)", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, imag >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
// 'im(z)' label
- drawLabel(ctx, rect, 0.0f, imag, "im(θ)", Palette::Red, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
+ drawLabel(ctx, rect, 0.0f, imag, "im(z)", Palette::CalculationTrigoAndComplexForeground, real >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
// '|z|' label, the relative horizontal position of this label depends on the quadrant
CurveView::RelativePosition verticalPosition = real*imag < 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After;
if (real == 0.0f) {
// Edge case: pure imaginary
verticalPosition = CurveView::RelativePosition::None;
}
- drawLabel(ctx, rect, real/2.0f, imag/2.0f, "|z|", Palette::Red, CurveView::RelativePosition::None, verticalPosition);
+ drawLabel(ctx, rect, real/2.0f, imag/2.0f, "|z|", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, verticalPosition);
// 'arg(z)' label, the absolute and relative horizontal/vertical positions of this label depends on the quadrant
CurveView::RelativePosition horizontalPosition = real >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::None;
verticalPosition = imag >= 0.0f ? CurveView::RelativePosition::After : CurveView::RelativePosition::Before;
@@ -87,7 +88,7 @@ void ComplexGraphView::drawRect(KDContext * ctx, KDRect rect) const {
* and for the left half plan, we position the label at the half angle. The
* relative position is chosen accordingly. */
float anglePositionRatio = real >= 0.0f ? 0.0f : 0.5f;
- drawLabel(ctx, rect, a*std::cos(anglePositionRatio*th), b*std::sin(anglePositionRatio*th), "arg(z)", Palette::Red, horizontalPosition, verticalPosition);
+ drawLabel(ctx, rect, a*std::cos(anglePositionRatio*th), b*std::sin(anglePositionRatio*th), "arg(z)", Palette::CalculationTrigoAndComplexForeground, horizontalPosition, verticalPosition);
}
}
diff --git a/apps/calculation/additional_outputs/complex_list_controller.h b/apps/calculation/additional_outputs/complex_list_controller.h
index 1c7d2a654..addb7c342 100644
--- a/apps/calculation/additional_outputs/complex_list_controller.h
+++ b/apps/calculation/additional_outputs/complex_list_controller.h
@@ -10,7 +10,7 @@ namespace Calculation {
class ComplexListController : public IllustratedListController {
public:
ComplexListController(EditExpressionController * editExpressionController) :
- IllustratedListController(nullptr, editExpressionController),
+ IllustratedListController(editExpressionController),
m_complexGraphCell(&m_model) {}
// ViewController
diff --git a/apps/calculation/additional_outputs/complex_model.cpp b/apps/calculation/additional_outputs/complex_model.cpp
index 6d59f3810..5a2322415 100644
--- a/apps/calculation/additional_outputs/complex_model.cpp
+++ b/apps/calculation/additional_outputs/complex_model.cpp
@@ -17,10 +17,10 @@ float ComplexModel::rangeBound(float direction, bool horizontal) const {
maxFactor = k_maxHorizontalMarginFactor;
value = real();
}
- if (std::isnan(value) || std::isinf(value) || value == 0.0f) {
- return direction*maxFactor;
- }
float factor = direction*value >= 0.0f ? maxFactor : minFactor;
+ if (std::isnan(value) || std::isinf(value) || value == 0.0f) {
+ return direction*factor;
+ }
return factor*value;
}
diff --git a/apps/calculation/additional_outputs/complex_model.h b/apps/calculation/additional_outputs/complex_model.h
index 827418c1e..685d6bdaf 100644
--- a/apps/calculation/additional_outputs/complex_model.h
+++ b/apps/calculation/additional_outputs/complex_model.h
@@ -2,6 +2,7 @@
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_MODEL_H
#include "../../shared/curve_view_range.h"
+#include "illustrated_list_controller.h"
#include
namespace Calculation {
@@ -17,11 +18,53 @@ public:
void setComplex(std::complex c) { *this = ComplexModel(c); }
-
- static constexpr float k_minVerticalMarginFactor = -0.5f;
- static constexpr float k_maxVerticalMarginFactor = 1.2f;
+ /* The range is computed from these criteria:
+ * - The real part is centered horizontally
+ * - Both left and right margins are equal to the real length
+ * - The imaginary part is the same length as the real part
+ * - The remaining vertical margin are splitted as one third at the top, 2
+ * thirds at the bottom
+ *
+ * | | 1/3 * vertical_margin
+ * +----------+
+ * | / | |
+ * | / | | Imaginary
+ * | / | |
+ * | / | |
+ * ----------+----------+----------
+ * |
+ * | 2/3 * vertical_margin
+ * -----------
+ * Real
+ *
+ */
+ // Horizontal range
static constexpr float k_minHorizontalMarginFactor = -1.0f;
static constexpr float k_maxHorizontalMarginFactor = 2.0f;
+ // Vertical range
+ static constexpr KDCoordinate k_width = Ion::Display::Width - Metric::PopUpRightMargin - Metric::PopUpLeftMargin;
+ static constexpr KDCoordinate k_height = IllustratedListController::k_illustrationHeight;
+ static constexpr KDCoordinate k_unit = k_width/3;
+ /*
+ * VerticalMaring = k_height - k_unit
+ *
+ * Values | Coordinates
+ * --------+----------------------------------
+ * imag | k_unit
+ * Ymax | k_unit + (1/3)*VerticalMargin
+ * Ymin | -(2/3)*VerticalMargin
+ *
+ * Thus:
+ * Ymin = -(2/3)*k_verticalMargin*imag/k_unit
+ * = -(2/3)*(k_height/k_unit - 1)*imag
+ * = 2/3*(1 - k_height/k_unit)*imag
+ * Ymax = (k_unit + (1/3)*VerticalMargin)*imag/k_unit
+ * = (1 + (1/3)*(k_height/k_unit - 1))*imag
+ * = 1/3*(2 + k_height/k_unit)*imag
+ *
+ * */
+ static constexpr float k_minVerticalMarginFactor = 2.0f/3.0f*(1.0f - (float)k_height/(float)k_unit);
+ static constexpr float k_maxVerticalMarginFactor = 1.0f/3.0f*(2.0f + (float)k_height/(float)k_unit);
private:
float rangeBound(float direction, bool horizontal) const;
diff --git a/apps/calculation/additional_outputs/expression_with_equal_sign_view.h b/apps/calculation/additional_outputs/expression_with_equal_sign_view.h
index 865a7393f..359fbe46b 100644
--- a/apps/calculation/additional_outputs/expression_with_equal_sign_view.h
+++ b/apps/calculation/additional_outputs/expression_with_equal_sign_view.h
@@ -4,13 +4,14 @@
#include
#include
#include
+#include
namespace Calculation {
class ExpressionWithEqualSignView : public ExpressionView {
public:
ExpressionWithEqualSignView() :
- m_equalSign(KDFont::LargeFont, I18n::Message::Equal, 0.5f, 0.5f, KDColorBlack)
+ m_equalSign(KDFont::LargeFont, I18n::Message::Equal, 0.5f, 0.5f, Palette::PrimaryText)
{}
KDSize minimalSizeForOptimalDisplay() const override;
void drawRect(KDContext * ctx, KDRect rect) const override;
diff --git a/apps/calculation/additional_outputs/expressions_list_controller.cpp b/apps/calculation/additional_outputs/expressions_list_controller.cpp
index 555de4433..2ea1b3ef3 100644
--- a/apps/calculation/additional_outputs/expressions_list_controller.cpp
+++ b/apps/calculation/additional_outputs/expressions_list_controller.cpp
@@ -7,8 +7,8 @@ namespace Calculation {
/* Expressions list controller */
-ExpressionsListController::ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
- ListController(parentResponder, editExpressionController),
+ExpressionsListController::ExpressionsListController(EditExpressionController * editExpressionController) :
+ ListController(editExpressionController),
m_cells{}
{
for (int i = 0; i < k_maxNumberOfCells; i++) {
@@ -38,9 +38,7 @@ HighlightCell * ExpressionsListController::reusableCell(int index, int type) {
KDCoordinate ExpressionsListController::rowHeight(int j) {
Layout l = layoutAtIndex(j);
- if (l.isUninitialized()) {
- return 0;
- }
+ assert(!l.isUninitialized());
return l.layoutSize().height() + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
}
diff --git a/apps/calculation/additional_outputs/expressions_list_controller.h b/apps/calculation/additional_outputs/expressions_list_controller.h
index afc59e9c1..4d6ade149 100644
--- a/apps/calculation/additional_outputs/expressions_list_controller.h
+++ b/apps/calculation/additional_outputs/expressions_list_controller.h
@@ -10,7 +10,7 @@ namespace Calculation {
class ExpressionsListController : public ListController {
public:
- ExpressionsListController(Responder * parentResponder, EditExpressionController * editExpressionController);
+ ExpressionsListController(EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
@@ -28,7 +28,7 @@ public:
protected:
constexpr static int k_maxNumberOfCells = 4;
- virtual int textAtIndex(char * buffer, size_t bufferSize, int index) override;
+ int textAtIndex(char * buffer, size_t bufferSize, int index) override;
Poincare::Expression m_expression;
// Memoization of layouts
mutable Poincare::Layout m_layouts[k_maxNumberOfCells];
diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp
index 80ea4f52a..1b78bef20 100644
--- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp
+++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp
@@ -1,4 +1,5 @@
#include "illustrated_list_controller.h"
+#include
#include
#include "../app.h"
@@ -8,8 +9,8 @@ namespace Calculation {
/* Illustrated list controller */
-IllustratedListController::IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController) :
- ListController(parentResponder, editExpressionController, this),
+IllustratedListController::IllustratedListController(EditExpressionController * editExpressionController) :
+ ListController(editExpressionController, this),
m_additionalCalculationCells{}
{
for (int i = 0; i < k_maxNumberOfAdditionalCalculations; i++) {
@@ -78,7 +79,17 @@ KDCoordinate IllustratedListController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer calculation = m_calculationStore.calculationAtIndex(calculationIndex);
- return calculation->height(App::app()->localContext(), true, true) + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness;
+ constexpr bool expanded = true;
+ KDCoordinate result = calculation->memoizedHeight(expanded);
+ if (result < 0) {
+ result = ScrollableThreeExpressionsCell::Height(calculation.pointer());
+ if (result < 0) {
+ // Raise, because Height modified the calculation and failed.
+ Poincare::ExceptionCheckpoint::Raise();
+ }
+ calculation->setMemoizedHeight(expanded, result);
+ }
+ return result + Metric::CellSeparatorThickness;
}
int IllustratedListController::typeAtLocation(int i, int j) {
diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.h b/apps/calculation/additional_outputs/illustrated_list_controller.h
index fdac26ddd..970341821 100644
--- a/apps/calculation/additional_outputs/illustrated_list_controller.h
+++ b/apps/calculation/additional_outputs/illustrated_list_controller.h
@@ -10,8 +10,10 @@
namespace Calculation {
class IllustratedListController : public ListController, public SelectableTableViewDelegate {
+/* TODO There is factorizable code between this and
+ * Calculation::HistoryController (at least rowHeight). */
public:
- IllustratedListController(Responder * parentResponder, EditExpressionController * editExpressionController);
+ IllustratedListController(EditExpressionController * editExpressionController);
// Responder
void viewDidDisappear() override;
@@ -31,7 +33,7 @@ public:
// IllustratedListController
void setExpression(Poincare::Expression e) override;
- constexpr static KDCoordinate k_illustrationHeight = 100;
+ constexpr static KDCoordinate k_illustrationHeight = 120;
protected:
Poincare::Expression m_savedExpression;
CalculationStore m_calculationStore;
diff --git a/apps/calculation/additional_outputs/illustration_cell.cpp b/apps/calculation/additional_outputs/illustration_cell.cpp
index bd2471710..20249c7d8 100644
--- a/apps/calculation/additional_outputs/illustration_cell.cpp
+++ b/apps/calculation/additional_outputs/illustration_cell.cpp
@@ -10,7 +10,7 @@ void IllustrationCell::layoutSubviews(bool force) {
}
void IllustrationCell::drawRect(KDContext * ctx, KDRect rect) const {
- drawBorderOfRect(ctx, bounds(), Palette::GreyBright);
+ drawBorderOfRect(ctx, bounds(), Palette::ListCellBorder);
}
}
diff --git a/apps/calculation/additional_outputs/integer_list_controller.cpp b/apps/calculation/additional_outputs/integer_list_controller.cpp
index 1e5bb0cb8..98deb516d 100644
--- a/apps/calculation/additional_outputs/integer_list_controller.cpp
+++ b/apps/calculation/additional_outputs/integer_list_controller.cpp
@@ -28,9 +28,6 @@ Integer::Base baseAtIndex(int index) {
}
void IntegerListController::computeLayoutAtIndex(int index) {
- if (!m_layouts[index].isUninitialized()) {
- return;
- }
assert(m_expression.type() == ExpressionNode::Type::BasedInteger);
// For index = k_indexOfFactorExpression, the layout is assumed to be alreday memoized because it is needed to compute the numberOfRows
assert(index < k_indexOfFactorExpression);
@@ -65,6 +62,6 @@ bool IntegerListController::factorExpressionIsComputable() const {
}
m_layouts[k_indexOfFactorExpression] = EmptyLayout::Builder();
return false;
+}
}
-}
diff --git a/apps/calculation/additional_outputs/integer_list_controller.h b/apps/calculation/additional_outputs/integer_list_controller.h
index a0208762d..b76f46d4d 100644
--- a/apps/calculation/additional_outputs/integer_list_controller.h
+++ b/apps/calculation/additional_outputs/integer_list_controller.h
@@ -8,7 +8,7 @@ namespace Calculation {
class IntegerListController : public ExpressionsListController {
public:
IntegerListController(EditExpressionController * editExpressionController) :
- ExpressionsListController(nullptr, editExpressionController) {}
+ ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
diff --git a/apps/calculation/additional_outputs/list_controller.cpp b/apps/calculation/additional_outputs/list_controller.cpp
index fe2836fed..ea2b17d3c 100644
--- a/apps/calculation/additional_outputs/list_controller.cpp
+++ b/apps/calculation/additional_outputs/list_controller.cpp
@@ -21,8 +21,8 @@ void ListController::InnerListController::didBecomeFirstResponder() {
/* List Controller */
-ListController::ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate) :
- StackViewController(parentResponder, &m_listController, Palette::ToolboxHeaderText, Palette::ToolboxHeaderBackground, Palette::ToolboxHeaderBorder),
+ListController::ListController(EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate) :
+ StackViewController(nullptr, &m_listController, Palette::ToolboxHeaderText, Palette::ToolboxHeaderBackground, Palette::ToolboxHeaderBorder),
m_listController(this, delegate),
m_editExpressionController(editExpressionController)
{
@@ -38,7 +38,6 @@ bool ListController::handleEvent(Ion::Events::Event event) {
* insertTextBody. */
Container::activeApp()->dismissModalViewController();
m_editExpressionController->insertTextBody(buffer);
- Container::activeApp()->setFirstResponder(m_editExpressionController);
return true;
}
return false;
diff --git a/apps/calculation/additional_outputs/list_controller.h b/apps/calculation/additional_outputs/list_controller.h
index 86f24b227..0378c90b6 100644
--- a/apps/calculation/additional_outputs/list_controller.h
+++ b/apps/calculation/additional_outputs/list_controller.h
@@ -10,7 +10,7 @@ class EditExpressionController;
class ListController : public StackViewController, public ListViewDataSource, public SelectableTableViewDataSource {
public:
- ListController(Responder * parentResponder, EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate = nullptr);
+ ListController(EditExpressionController * editExpressionController, SelectableTableViewDelegate * delegate = nullptr);
// Responder
bool handleEvent(Ion::Events::Event event) override;
diff --git a/apps/calculation/additional_outputs/rational_list_controller.h b/apps/calculation/additional_outputs/rational_list_controller.h
index fac0f06eb..62d0de5a9 100644
--- a/apps/calculation/additional_outputs/rational_list_controller.h
+++ b/apps/calculation/additional_outputs/rational_list_controller.h
@@ -8,7 +8,7 @@ namespace Calculation {
class RationalListController : public ExpressionsListController {
public:
RationalListController(EditExpressionController * editExpressionController) :
- ExpressionsListController(nullptr, editExpressionController) {}
+ ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp
index d90fc628d..63cf7b17e 100644
--- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp
+++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp
@@ -8,7 +8,8 @@ void ScrollableThreeExpressionsView::resetMemoization() {
setLayouts(Poincare::Layout(), Poincare::Layout(), Poincare::Layout());
}
-void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
+void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation, bool * didForceOutput) {
+ assert(!didForceOutput || *didForceOutput == false);
Poincare::Context * context = App::app()->localContext();
// Clean the layouts to make room in the pool
@@ -27,6 +28,9 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
Poincare::ExceptionCheckpoint::Raise();
} else {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
+ if (didForceOutput) {
+ *didForceOutput = true;
+ }
}
}
}
@@ -46,6 +50,9 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
+ if (didForceOutput) {
+ *didForceOutput = true;
+ }
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
@@ -65,6 +72,27 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) {
layoutSubviews();
}
+KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) {
+ ScrollableThreeExpressionsCell cell;
+ bool didForceOutput = false;
+ cell.setCalculation(calculation, &didForceOutput);
+ if (didForceOutput) {
+ /* We could not compute the height of the calculation as it is (the display
+ * output was forced to another value during the height computation).
+ * Warning: the display output of calculation was actually changed, so it
+ * will cause problems if we already did some computations with another
+ * display value. */
+ return -1;
+ }
+ KDRect leftFrame = KDRectZero;
+ KDRect centerFrame = KDRectZero;
+ KDRect approximateSignFrame = KDRectZero;
+ KDRect rightFrame = KDRectZero;
+ cell.subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame);
+ KDRect unionedFrame = leftFrame.unionedWith(centerFrame).unionedWith(rightFrame);
+ return unionedFrame.height() + 2 * ScrollableThreeExpressionsView::k_margin;
+}
+
void ScrollableThreeExpressionsCell::didBecomeFirstResponder() {
reinitSelection();
Container::activeApp()->setFirstResponder(&m_view);
@@ -75,8 +103,8 @@ void ScrollableThreeExpressionsCell::reinitSelection() {
m_view.reloadScroll();
}
-void ScrollableThreeExpressionsCell::setCalculation(Calculation * calculation) {
- m_view.setCalculation(calculation);
+void ScrollableThreeExpressionsCell::setCalculation(Calculation * calculation, bool * didForceOutput) {
+ m_view.setCalculation(calculation, didForceOutput);
layoutSubviews();
}
diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h
index 42c18becb..4a8a015b4 100644
--- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h
+++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h
@@ -5,22 +5,30 @@
#include "../../shared/scrollable_multiple_expressions_view.h"
#include "../calculation.h"
#include "expression_with_equal_sign_view.h"
+#include
namespace Calculation {
+/* TODO There is factorizable code between this and Calculation::HistoryViewCell
+ * (at least setCalculation). */
+
class ScrollableThreeExpressionsView : public Shared::AbstractScrollableMultipleExpressionsView {
public:
+ static constexpr KDCoordinate k_margin = Metric::CommonSmallMargin;
ScrollableThreeExpressionsView(Responder * parentResponder) : Shared::AbstractScrollableMultipleExpressionsView(parentResponder, &m_contentCell), m_contentCell() {
- setMargins(Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin); // Left Right margins are already added by TableCell
- setBackgroundColor(KDColorWhite);
+ setMargins(k_margin, k_margin, k_margin, k_margin); // Left Right margins are already added by TableCell
+ setBackgroundColor(Palette::BackgroundApps);
}
void resetMemoization();
- void setCalculation(Calculation * calculation);
+ void setCalculation(Calculation * calculation, bool * didForceOutput = nullptr);
+ void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
+ return m_contentCell.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
+ }
private:
class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell {
public:
ContentCell() : m_leftExpressionView() {}
- KDColor backgroundColor() const override { return KDColorWhite; }
+ KDColor backgroundColor() const override { return Palette::BackgroundApps; }
void setEven(bool even) override { return; }
ExpressionView * leftExpressionView() const override { return const_cast(&m_leftExpressionView); }
private:
@@ -28,12 +36,13 @@ private:
};
ContentCell * contentCell() override { return &m_contentCell; };
- const ContentCell * constContentCell() const override { return &m_contentCell; };
+ const ContentCell * constContentCell() const override { return &m_contentCell; };
ContentCell m_contentCell;
};
class ScrollableThreeExpressionsCell : public TableCell, public Responder {
public:
+ static KDCoordinate Height(Calculation * calculation);
ScrollableThreeExpressionsCell() :
Responder(nullptr),
m_view(this) {}
@@ -52,12 +61,15 @@ public:
void setHighlighted(bool highlight) override { m_view.evenOddCell()->setHighlighted(highlight); }
void resetMemoization() { m_view.resetMemoization(); }
- void setCalculation(Calculation * calculation);
+ void setCalculation(Calculation * calculation, bool * didForceOutput = nullptr);
void setDisplayCenter(bool display);
ScrollableThreeExpressionsView::SubviewPosition selectedSubviewPosition() { return m_view.selectedSubviewPosition(); }
void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); }
void reinitSelection();
+ void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) {
+ return m_view.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame);
+ }
private:
// Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView
KDCoordinate labelMargin() const override { return 0; }
diff --git a/apps/calculation/additional_outputs/trigonometry_graph_cell.cpp b/apps/calculation/additional_outputs/trigonometry_graph_cell.cpp
index f28c107f4..636ce588a 100644
--- a/apps/calculation/additional_outputs/trigonometry_graph_cell.cpp
+++ b/apps/calculation/additional_outputs/trigonometry_graph_cell.cpp
@@ -1,4 +1,5 @@
#include "trigonometry_graph_cell.h"
+#include
using namespace Shared;
using namespace Poincare;
@@ -14,24 +15,24 @@ TrigonometryGraphView::TrigonometryGraphView(TrigonometryModel * model) :
void TrigonometryGraphView::drawRect(KDContext * ctx, KDRect rect) const {
float s = std::sin(m_model->angle());
float c = std::cos(m_model->angle());
- ctx->fillRect(rect, KDColorWhite);
+ ctx->fillRect(rect, Palette::BackgroundApps);
drawGrid(ctx, rect);
drawAxes(ctx, rect);
// Draw the circle
drawCurve(ctx, rect, 0.0f, 2.0f*M_PI, M_PI/180.0f, [](float t, void * model, void * context) {
return Poincare::Coordinate2D(std::cos(t), std::sin(t));
- }, nullptr, nullptr, true, Palette::GreyDark, false);
+ }, nullptr, nullptr, true, Palette::SecondaryText, false);
// Draw dashed segment to indicate sine and cosine
- drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, c, 0.0f, s, Palette::Red, 1, 3);
- drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, s, 0.0f, c, Palette::Red, 1, 3);
+ drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, c, 0.0f, s, Palette::CalculationTrigoAndComplexForeground, 1, 3);
+ drawHorizontalOrVerticalSegment(ctx, rect, Axis::Horizontal, s, 0.0f, c, Palette::CalculationTrigoAndComplexForeground, 1, 3);
// Draw angle position on the circle
- drawDot(ctx, rect, c, s, Palette::Red, Size::Large);
+ drawDot(ctx, rect, c, s, Palette::CalculationTrigoAndComplexForeground, Size::Large);
// Draw graduations
drawLabelsAndGraduations(ctx, rect, Axis::Vertical, false, true);
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, true);
// Draw labels
- drawLabel(ctx, rect, 0.0f, s, "sin(θ)", Palette::Red, c >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
- drawLabel(ctx, rect, c, 0.0f, "cos(θ)", Palette::Red, CurveView::RelativePosition::None, s >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
+ drawLabel(ctx, rect, 0.0f, s, "sin(θ)", Palette::CalculationTrigoAndComplexForeground, c >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After, CurveView::RelativePosition::None);
+ drawLabel(ctx, rect, c, 0.0f, "cos(θ)", Palette::CalculationTrigoAndComplexForeground, CurveView::RelativePosition::None, s >= 0.0f ? CurveView::RelativePosition::Before : CurveView::RelativePosition::After);
}
}
diff --git a/apps/calculation/additional_outputs/trigonometry_list_controller.h b/apps/calculation/additional_outputs/trigonometry_list_controller.h
index dfec810d9..12880e1a0 100644
--- a/apps/calculation/additional_outputs/trigonometry_list_controller.h
+++ b/apps/calculation/additional_outputs/trigonometry_list_controller.h
@@ -10,7 +10,7 @@ namespace Calculation {
class TrigonometryListController : public IllustratedListController {
public:
TrigonometryListController(EditExpressionController * editExpressionController) :
- IllustratedListController(nullptr, editExpressionController),
+ IllustratedListController(editExpressionController),
m_graphCell(&m_model) {}
void setExpression(Poincare::Expression e) override;
private:
diff --git a/apps/calculation/additional_outputs/trigonometry_model.h b/apps/calculation/additional_outputs/trigonometry_model.h
index cf31fb1f8..c299565a8 100644
--- a/apps/calculation/additional_outputs/trigonometry_model.h
+++ b/apps/calculation/additional_outputs/trigonometry_model.h
@@ -18,7 +18,7 @@ public:
float yMax() const override { return yCenter() + yHalfRange(); }
void setAngle(float f) { m_angle = f; }
- float angle() const { return m_angle*M_PI/Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
+ float angle() const { return m_angle*(float)M_PI/(float)Poincare::Trigonometry::PiInAngleUnit(Poincare::Preferences::sharedPreferences()->angleUnit()); }
private:
constexpr static float k_xHalfRange = 2.1f;
// We center the yRange around the semi-circle where the angle is
diff --git a/apps/calculation/additional_outputs/unit_list_controller.cpp b/apps/calculation/additional_outputs/unit_list_controller.cpp
new file mode 100644
index 000000000..45eb58c4b
--- /dev/null
+++ b/apps/calculation/additional_outputs/unit_list_controller.cpp
@@ -0,0 +1,155 @@
+#include "unit_list_controller.h"
+#include "../app.h"
+#include "../../shared/poincare_helpers.h"
+#include
+#include
+#include
+#include
+#include
+
+using namespace Poincare;
+using namespace Shared;
+
+namespace Calculation {
+
+void UnitListController::setExpression(Poincare::Expression e) {
+ ExpressionsListController::setExpression(e);
+ assert(!m_expression.isUninitialized());
+ // Reinitialize m_memoizedExpressions
+ for (size_t i = 0; i < k_maxNumberOfCells; i++) {
+ m_memoizedExpressions[i] = Expression();
+ }
+
+ size_t numberOfMemoizedExpressions = 0;
+ // 1. First rows: miscellaneous classic units for some dimensions
+ Expression copy = m_expression.clone();
+ Expression units;
+ // Reduce to be able to recognize units
+ PoincareHelpers::Reduce(©, App::app()->localContext(), ExpressionNode::ReductionTarget::User);
+ copy = copy.removeUnit(&units);
+ bool requireSimplification = false;
+ bool canChangeUnitPrefix = false;
+
+ if (Unit::IsSISpeed(units)) {
+ // 1.a. Turn speed into km/h
+ m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
+ m_expression.clone(),
+ Multiplication::Builder(
+ Unit::Kilometer(),
+ Power::Builder(
+ Unit::Hour(),
+ Rational::Builder(-1)
+ )
+ )
+ );
+ requireSimplification = true; // Simplify the conversion
+ } else if (Unit::IsSIVolume(units)) {
+ // 1.b. Turn volume into L
+ m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
+ m_expression.clone(),
+ Unit::Liter()
+ );
+ requireSimplification = true; // Simplify the conversion
+ canChangeUnitPrefix = true; // Pick best prefix (mL)
+ } else if (Unit::IsSIEnergy(units)) {
+ // 1.c. Turn energy into Wh
+ m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
+ m_expression.clone(),
+ Multiplication::Builder(
+ Unit::Watt(),
+ Unit::Hour()
+ )
+ );
+ m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
+ m_expression.clone(),
+ Unit::ElectronVolt()
+ );
+ requireSimplification = true; // Simplify the conversion
+ canChangeUnitPrefix = true; // Pick best prefix (kWh)
+ } else if (Unit::IsSITime(units)) {
+ // Turn time into ? year + ? month + ? day + ? h + ? min + ? s
+ double value = Shared::PoincareHelpers::ApproximateToScalar(copy, App::app()->localContext());
+ m_memoizedExpressions[numberOfMemoizedExpressions++] = Unit::BuildTimeSplit(value, App::app()->localContext(), Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit());
+ }
+ // 1.d. Simplify and tune prefix of all computed expressions
+ size_t currentExpressionIndex = 0;
+ while (currentExpressionIndex < numberOfMemoizedExpressions) {
+ assert(!m_memoizedExpressions[currentExpressionIndex].isUninitialized());
+ if (requireSimplification) {
+ Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
+ }
+ if (canChangeUnitPrefix) {
+ Expression newUnits;
+ // Reduce to be able to removeUnit
+ PoincareHelpers::Reduce(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
+ m_memoizedExpressions[currentExpressionIndex] = m_memoizedExpressions[currentExpressionIndex].removeUnit(&newUnits);
+ double value = Shared::PoincareHelpers::ApproximateToScalar(m_memoizedExpressions[currentExpressionIndex], App::app()->localContext());
+ ExpressionNode::ReductionContext reductionContext(
+ App::app()->localContext(),
+ Preferences::sharedPreferences()->complexFormat(),
+ Preferences::sharedPreferences()->angleUnit(),
+ ExpressionNode::ReductionTarget::User,
+ ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
+ Unit::ChooseBestPrefixForValue(&newUnits, &value, reductionContext);
+ m_memoizedExpressions[currentExpressionIndex] = Multiplication::Builder(Number::FloatNumber(value), newUnits);
+ }
+ currentExpressionIndex++;
+ }
+
+ // 2. IS units only
+ assert(numberOfMemoizedExpressions < k_maxNumberOfCells - 1);
+ m_memoizedExpressions[numberOfMemoizedExpressions] = m_expression.clone();
+ Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[numberOfMemoizedExpressions], App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::InternationalSystem);
+ numberOfMemoizedExpressions++;
+
+ // 3. Get rid of duplicates
+ Expression reduceExpression = m_expression.clone();
+ // Make m_expression compareable to m_memoizedExpressions (turn BasedInteger into Rational for instance)
+ Shared::PoincareHelpers::Simplify(&reduceExpression, App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::None);
+ currentExpressionIndex = 1;
+ while (currentExpressionIndex < numberOfMemoizedExpressions) {
+ bool duplicateFound = false;
+ for (size_t i = 0; i < currentExpressionIndex + 1; i++) {
+ // Compare the currentExpression to all previous memoized expressions and to m_expression
+ Expression comparedExpression = i == currentExpressionIndex ? reduceExpression : m_memoizedExpressions[i];
+ assert(!comparedExpression.isUninitialized());
+ if (comparedExpression.isIdenticalTo(m_memoizedExpressions[currentExpressionIndex])) {
+ numberOfMemoizedExpressions--;
+ // Shift next expressions
+ for (size_t j = currentExpressionIndex; j < numberOfMemoizedExpressions; j++) {
+ m_memoizedExpressions[j] = m_memoizedExpressions[j+1];
+ }
+ // Remove last expression
+ m_memoizedExpressions[numberOfMemoizedExpressions] = Expression();
+ // The current expression has been discarded, no need to increment the current index
+ duplicateFound = true;
+ break;
+ }
+ }
+ if (!duplicateFound) {
+ // The current expression is not a duplicate, check next expression
+ currentExpressionIndex++;
+ }
+ }
+}
+
+int UnitListController::numberOfRows() const {
+ int nbOfRows = 0;
+ for (size_t i = 0; i < k_maxNumberOfCells; i++) {
+ if (!m_memoizedExpressions[i].isUninitialized()) {
+ nbOfRows++;
+ }
+ }
+ return nbOfRows;
+}
+
+void UnitListController::computeLayoutAtIndex(int index) {
+ assert(!m_memoizedExpressions[index].isUninitialized());
+ m_layouts[index] = Shared::PoincareHelpers::CreateLayout(m_memoizedExpressions[index]);
+}
+
+I18n::Message UnitListController::messageAtIndex(int index) {
+ return (I18n::Message)0;
+}
+
+}
diff --git a/apps/calculation/additional_outputs/unit_list_controller.h b/apps/calculation/additional_outputs/unit_list_controller.h
new file mode 100644
index 000000000..e3fdee036
--- /dev/null
+++ b/apps/calculation/additional_outputs/unit_list_controller.h
@@ -0,0 +1,26 @@
+#ifndef CALCULATION_ADDITIONAL_OUTPUTS_UNIT_LIST_CONTROLLER_H
+#define CALCULATION_ADDITIONAL_OUTPUTS_UNIT_LIST_CONTROLLER_H
+
+#include "expressions_list_controller.h"
+
+namespace Calculation {
+
+class UnitListController : public ExpressionsListController {
+public:
+ UnitListController(EditExpressionController * editExpressionController) :
+ ExpressionsListController(editExpressionController) {}
+
+ void setExpression(Poincare::Expression e) override;
+
+ //ListViewDataSource
+ int numberOfRows() const override;
+private:
+ void computeLayoutAtIndex(int index) override;
+ I18n::Message messageAtIndex(int index) override;
+ // Memoization of expressions
+ mutable Poincare::Expression m_memoizedExpressions[k_maxNumberOfCells];
+};
+
+}
+
+#endif
diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp
index 8947c679e..8f4fd1459 100644
--- a/apps/calculation/app.cpp
+++ b/apps/calculation/app.cpp
@@ -17,8 +17,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::CalculAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
diff --git a/apps/calculation/app.h b/apps/calculation/app.h
index 28293bf3b..ee12934c7 100644
--- a/apps/calculation/app.h
+++ b/apps/calculation/app.h
@@ -15,7 +15,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
diff --git a/apps/calculation/base.de.i18n b/apps/calculation/base.de.i18n
index ebb1bd928..18aab0374 100644
--- a/apps/calculation/base.de.i18n
+++ b/apps/calculation/base.de.i18n
@@ -4,6 +4,6 @@ AdditionalResults = "Weitere Ergebnisse"
DecimalBase = "Dezimal"
HexadecimalBase = "Hexadezimal"
BinaryBase = "Binär"
-PrimeFactors = "Primfaktor"
-MixedFraction = "Gemischte Fraktion"
-EuclideanDivision = "Euklidische Division"
+PrimeFactors = "Primfaktoren"
+MixedFraction = "Gemischte Zahl"
+EuclideanDivision = "Division mit Rest"
diff --git a/apps/calculation/base.es.i18n b/apps/calculation/base.es.i18n
index 36d7a8bdb..25c3c5035 100644
--- a/apps/calculation/base.es.i18n
+++ b/apps/calculation/base.es.i18n
@@ -1,9 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
-AdditionalResults = "????"
-DecimalBase = "????"
-HexadecimalBase = "????"
-BinaryBase = "????"
-PrimeFactors = "????"
-MixedFraction = "????"
-EuclideanDivision = "????"
+AdditionalResults = "Resultados adicionales"
+DecimalBase = "Decimal"
+HexadecimalBase = "Hexadecimal"
+BinaryBase = "Binario"
+PrimeFactors = "Factores primos"
+MixedFraction = "Fracción mixta"
+EuclideanDivision = "División euclidiana"
diff --git a/apps/calculation/base.it.i18n b/apps/calculation/base.it.i18n
new file mode 100644
index 000000000..6544c6622
--- /dev/null
+++ b/apps/calculation/base.it.i18n
@@ -0,0 +1,9 @@
+CalculApp = "Calcolo"
+CalculAppCapital = "CALCOLO"
+AdditionalResults = "Risultati complementari"
+DecimalBase = "Decimale"
+HexadecimalBase = "Esadecimale"
+BinaryBase = "Binario"
+PrimeFactors = "Fattori primi"
+MixedFraction = "Frazione mista"
+EuclideanDivision = "Divisione euclidea"
diff --git a/apps/calculation/base.nl.i18n b/apps/calculation/base.nl.i18n
new file mode 100644
index 000000000..0ae59c24a
--- /dev/null
+++ b/apps/calculation/base.nl.i18n
@@ -0,0 +1,9 @@
+CalculApp = "Calculatie"
+CalculAppCapital = "CALCULATIE"
+AdditionalResults = "Bijkomende resultaten"
+DecimalBase = "Decimaal"
+HexadecimalBase = "Hexadecimaal"
+BinaryBase = "Binaire"
+PrimeFactors = "Priemfactoren"
+MixedFraction = "Gemengde breuk"
+EuclideanDivision = "Geheeltallige deling"
diff --git a/apps/calculation/base.pt.i18n b/apps/calculation/base.pt.i18n
index 36d7a8bdb..b8e8717aa 100644
--- a/apps/calculation/base.pt.i18n
+++ b/apps/calculation/base.pt.i18n
@@ -1,9 +1,9 @@
CalculApp = "Cálculo"
CalculAppCapital = "CÁLCULO"
-AdditionalResults = "????"
-DecimalBase = "????"
-HexadecimalBase = "????"
-BinaryBase = "????"
-PrimeFactors = "????"
-MixedFraction = "????"
-EuclideanDivision = "????"
+AdditionalResults = "Resultados adicionais"
+DecimalBase = "Decimal"
+HexadecimalBase = "Hexadecimal"
+BinaryBase = "Binário"
+PrimeFactors = "Fatores primos"
+MixedFraction = "Fração mista"
+EuclideanDivision = "Divisão euclidiana"
diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp
index 90f2de0f0..ebe6e0f9c 100644
--- a/apps/calculation/calculation.cpp
+++ b/apps/calculation/calculation.cpp
@@ -1,20 +1,22 @@
#include "calculation.h"
#include "../shared/poincare_helpers.h"
+#include "../shared/scrollable_multiple_expressions_view.h"
#include "../global_preferences.h"
#include "../exam_mode_configuration.h"
+#include "app.h"
#include
#include
+#include
#include
#include
#include
+#include
using namespace Poincare;
using namespace Shared;
namespace Calculation {
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-
bool Calculation::operator==(const Calculation& c) {
return strcmp(inputText(), c.inputText()) == 0
&& strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), c.approximateOutputText(NumberOfSignificantDigits::Maximal)) == 0
@@ -39,8 +41,7 @@ Calculation * Calculation::next() const {
void Calculation::tidy() {
/* Reset height memoization (the complex format could have changed when
* re-entering Calculation app which would impact the heights). */
- m_height = -1;
- m_expandedHeight = -1;
+ resetHeightMemoization();
}
const char * Calculation::approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const {
@@ -125,123 +126,12 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul
}
}
-KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpressionsInline) {
- KDCoordinate result = expanded ? m_expandedHeight : m_height;
- if (result >= 0) {
- // Height already computed
- return result;
- }
-
- // Get input height
- Layout inputLayout = createInputLayout();
- KDCoordinate inputHeight = inputLayout.layoutSize().height();
- KDCoordinate inputWidth = inputLayout.layoutSize().width();
- float singleMargin = 2 * Metric::CommonSmallMargin;
- float doubleMargin = 4 * Metric::CommonSmallMargin;
- bool singleLine = false;
- KDCoordinate inputBaseline = inputLayout.baseline();
-
- // Get exact output height if needed
- Poincare::Layout exactLayout;
- bool couldNotCreateExactLayout = false;
- if (DisplaysExact(displayOutput(context))) {
- // Create the exact output layout
- exactLayout = createExactOutputLayout(&couldNotCreateExactLayout);
- if (couldNotCreateExactLayout) {
- if (displayOutput(context) != DisplayOutput::ExactOnly) {
- forceDisplayOutput(DisplayOutput::ApproximateOnly);
- } else {
- /* We should only display the exact result, but we cannot create it
- * -> raise an exception. */
- ExceptionCheckpoint::Raise();
- }
- }
- }
-
- if (displayOutput(context) == DisplayOutput::ExactOnly) {
- KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
- KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
- singleLine = exactOutputWidth + inputWidth < maxWidth - 40;
- if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
- KDCoordinate exactOutputBaseline = exactLayout.baseline();
- result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin;
- } else {
- if (allExpressionsInline) {
- KDCoordinate exactOutputBaseline = exactLayout.baseline();
- result = maxCoordinate(inputBaseline, exactOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline);
- } else {
- result = inputHeight + exactOutputHeight + doubleMargin;
- }
- }
+void Calculation::setMemoizedHeight(bool expanded, KDCoordinate height) {
+ if (expanded) {
+ m_expandedHeight = height;
} else {
- bool couldNotCreateApproximateLayout = false;
- Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
- if (couldNotCreateApproximateLayout) {
- if (displayOutput(context) == DisplayOutput::ApproximateOnly) {
- Poincare::ExceptionCheckpoint::Raise();
- } else {
- /* Set the display output to ApproximateOnly, make room in the pool by
- * erasing the exact layout, and retry to create the approximate layout */
- forceDisplayOutput(DisplayOutput::ApproximateOnly);
- exactLayout = Poincare::Layout();
- couldNotCreateApproximateLayout = false;
- approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
- if (couldNotCreateApproximateLayout) {
- Poincare::ExceptionCheckpoint::Raise();
- }
- }
- }
-
- KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height();
- KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
- singleLine = approximateOutputWidth + inputWidth < maxWidth - 40;
- if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) {
- if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact && !allExpressionsInline) {
- KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
- result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin;
- } else {
- if (allExpressionsInline) {
- KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
- result = maxCoordinate(inputBaseline, approximateOutputBaseline) + maxCoordinate(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline);
- } else {
- result = inputHeight + approximateOutputHeight + doubleMargin;
- }
- }
- } else {
- assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded));
- KDCoordinate exactOutputHeight = exactLayout.layoutSize().height();
- KDCoordinate exactOutputBaseline = exactLayout.baseline();
- KDCoordinate exactOutputWidth = exactLayout.layoutSize().width();
- KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width();
- singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 70;
- KDCoordinate approximateOutputBaseline = approximateLayout.baseline();
- if (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) {
- result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin;
- } else {
- if (allExpressionsInline) {
- result = maxCoordinate(inputBaseline, maxCoordinate(exactOutputBaseline, approximateOutputBaseline)) + maxCoordinate(inputHeight - inputBaseline, maxCoordinate(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline));
- } else {
- KDCoordinate outputHeight = maxCoordinate(exactOutputBaseline, approximateOutputBaseline) + maxCoordinate(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline);
- result = inputHeight + outputHeight + doubleMargin;
- }
- }
- }
+ m_height = height;
}
-
- /* For all display outputs except ExactAndApproximateToggle, the selected
- * height and the usual height are identical. We update both heights in
- * theses cases. */
- if (displayOutput(context) != DisplayOutput::ExactAndApproximateToggle) {
- m_height = result;
- m_expandedHeight = result;
- } else {
- if (expanded) {
- m_expandedHeight = result;
- } else {
- m_height = result;
- }
- }
- return result;
}
Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
@@ -285,7 +175,7 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
ExpressionNode::Type::PredictionInterval
};
return e.isOfType(approximateOnlyTypes, sizeof(approximateOnlyTypes)/sizeof(ExpressionNode::Type));
- }, context, true)
+ }, context)
)
{
m_displayOutput = DisplayOutput::ApproximateOnly;
@@ -302,15 +192,15 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
void Calculation::forceDisplayOutput(DisplayOutput d) {
m_displayOutput = d;
// Reset heights memoization as it might have changed when we modify the display output
- m_height = -1;
- m_expandedHeight = -1;
+ resetHeightMemoization();
}
+
bool Calculation::shouldOnlyDisplayExactOutput() {
/* If the input is a "store in a function", do not display the approximate
* result. This prevents x->f(x) from displaying x = undef. */
Expression i = input();
- return i.type() == ExpressionNode::Type::Store
- && i.childAtIndex(1).type() == ExpressionNode::Type::Function;
+ return (i.type() == ExpressionNode::Type::Store && i.childAtIndex(1).type() == ExpressionNode::Type::Function)
+ || strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Undefined::Name()) == 0;
}
Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(Poincare::Context * context) {
@@ -340,6 +230,9 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
}
Calculation::AdditionalInformationType Calculation::additionalInformationType(Context * context) {
+ if (ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode())) {
+ return AdditionalInformationType::None;
+ }
Preferences * preferences = Preferences::sharedPreferences();
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
Expression i = input();
@@ -361,8 +254,28 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co
if (input().isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit()) || o.isDefinedCosineOrSine(context, complexFormat, preferences->angleUnit())) {
return AdditionalInformationType::Trigonometry;
}
-
- // TODO: return AdditionalInformationType::Unit
+ if (o.hasUnit()) {
+ Expression unit;
+ PoincareHelpers::Reduce(&o, App::app()->localContext(), ExpressionNode::ReductionTarget::User,ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined, ExpressionNode::UnitConversion::None);
+ o = o.removeUnit(&unit);
+ if (Unit::IsSI(unit)) {
+ if (Unit::IsSISpeed(unit) || Unit::IsSIVolume(unit) || Unit::IsSIEnergy(unit)) {
+ /* All these units will provide misc. classic representatives in
+ * addition to the SI unit in additional information. */
+ return AdditionalInformationType::Unit;
+ }
+ if (Unit::IsSITime(unit)) {
+ /* If the number of seconds is above 60s, we can write it in the form
+ * of an addition: 23_min + 12_s for instance. */
+ double value = Shared::PoincareHelpers::ApproximateToScalar(o, App::app()->localContext());
+ if (value > Unit::SecondsPerMinute) {
+ return AdditionalInformationType::Unit;
+ }
+ }
+ return AdditionalInformationType::None;
+ }
+ return AdditionalInformationType::Unit;
+ }
if (o.isBasedIntegerCappedBy(k_maximalIntegerWithAdditionalInformation)) {
return AdditionalInformationType::Integer;
}
@@ -376,4 +289,9 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co
return AdditionalInformationType::None;
}
+void Calculation::resetHeightMemoization() {
+ m_height = -1;
+ m_expandedHeight = -1;
+}
+
}
diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h
index b084226df..f93c99aca 100644
--- a/apps/calculation/calculation.h
+++ b/apps/calculation/calculation.h
@@ -84,7 +84,8 @@ public:
Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout);
// Memoization of height
- KDCoordinate height(Poincare::Context * context, bool expanded = false, bool allExpressionsInline = false);
+ KDCoordinate memoizedHeight(bool expanded) { return expanded ? m_expandedHeight : m_height; }
+ void setMemoizedHeight(bool expanded, KDCoordinate height);
// Displayed output
DisplayOutput displayOutput(Poincare::Context * context);
@@ -99,6 +100,7 @@ private:
static constexpr int k_numberOfExpressions = 4;
static constexpr KDCoordinate k_heightComputationFailureHeight = 50;
static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000";
+ void resetHeightMemoization();
/* Buffers holding text expressions have to be longer than the text written
* by user (of maximum length TextField::maxBufferSize()) because when we
* print an expression we add omitted signs (multiplications, parenthesis...) */
diff --git a/apps/calculation/calculation_store.cpp b/apps/calculation/calculation_store.cpp
index cf9487c7b..e51ae3043 100644
--- a/apps/calculation/calculation_store.cpp
+++ b/apps/calculation/calculation_store.cpp
@@ -4,6 +4,7 @@
#include
#include
#include
+#include "../exam_mode_configuration.h"
#include
using namespace Poincare;
@@ -98,8 +99,11 @@ ExpiringPointer CalculationStore::push(const char * text, Context *
// Outputs hold exact output, approximate output and its duplicate
constexpr static int numberOfOutputs = Calculation::k_numberOfExpressions - 1;
Expression outputs[numberOfOutputs] = {Expression(), Expression(), Expression()};
- // SYMBOLIC COMPUTATION <= E12: PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->isInExamModeSymbolic()); // Symbolic computation
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->isInExamModeSymbolic() ? Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition : Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
+ if (ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode()) && outputs[1].hasUnit()) {
+ // Hide results with units on units if required by the exam mode configuration
+ outputs[1] = Undefined::Builder();
+ }
outputs[2] = outputs[1];
int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits;
for (int i = 0; i < numberOfOutputs; i++) {
diff --git a/apps/calculation/edit_expression_controller.cpp b/apps/calculation/edit_expression_controller.cpp
index 5e69bfee8..cd7702e93 100644
--- a/apps/calculation/edit_expression_controller.cpp
+++ b/apps/calculation/edit_expression_controller.cpp
@@ -9,7 +9,7 @@ using namespace Poincare;
namespace Calculation {
-EditExpressionController::ContentView::ContentView(Responder * parentResponder, TableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
+EditExpressionController::ContentView::ContentView(Responder * parentResponder, CalculationSelectableTableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
View(),
m_mainView(subview),
m_expressionField(parentResponder, inputEventHandlerDelegate, textFieldDelegate, layoutFieldDelegate)
@@ -42,18 +42,18 @@ EditExpressionController::EditExpressionController(Responder * parentResponder,
ViewController(parentResponder),
m_historyController(historyController),
m_calculationStore(calculationStore),
- m_contentView(this, (TableView *)m_historyController->view(), inputEventHandlerDelegate, this, this)
+ m_contentView(this, static_cast(m_historyController->view()), inputEventHandlerDelegate, this, this)
{
m_cacheBuffer[0] = 0;
}
void EditExpressionController::insertTextBody(const char * text) {
+ Container::activeApp()->setFirstResponder(this);
m_contentView.expressionField()->handleEventWithText(text, false, true);
}
void EditExpressionController::didBecomeFirstResponder() {
- int lastRow = m_calculationStore->numberOfCalculations() > 0 ? m_calculationStore->numberOfCalculations()-1 : 0;
- m_historyController->scrollToCell(0, lastRow);
+ m_contentView.mainView()->scrollToBottom();
m_contentView.expressionField()->setEditing(true, false);
Container::activeApp()->setFirstResponder(m_contentView.expressionField());
}
diff --git a/apps/calculation/edit_expression_controller.h b/apps/calculation/edit_expression_controller.h
index fe2d4c44c..eee1c4033 100644
--- a/apps/calculation/edit_expression_controller.h
+++ b/apps/calculation/edit_expression_controller.h
@@ -8,6 +8,7 @@
#include "../shared/layout_field_delegate.h"
#include "history_controller.h"
#include "calculation_store.h"
+#include "selectable_table_view.h"
namespace Calculation {
@@ -34,15 +35,15 @@ public:
private:
class ContentView : public View {
public:
- ContentView(Responder * parentResponder, TableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate);
+ ContentView(Responder * parentResponder, CalculationSelectableTableView * subview, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate);
void reload();
- TableView * mainView() { return m_mainView; }
+ CalculationSelectableTableView * mainView() { return m_mainView; }
ExpressionField * expressionField() { return &m_expressionField; }
private:
int numberOfSubviews() const override { return 2; }
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
- TableView * m_mainView;
+ CalculationSelectableTableView * m_mainView;
ExpressionField m_expressionField;
};
void reloadView();
diff --git a/apps/calculation/expression_field.h b/apps/calculation/expression_field.h
index a7fe0e20e..284404628 100644
--- a/apps/calculation/expression_field.h
+++ b/apps/calculation/expression_field.h
@@ -7,7 +7,10 @@ namespace Calculation {
class ExpressionField : public ::ExpressionField {
public:
- using ::ExpressionField::ExpressionField;
+ ExpressionField(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandler, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) :
+ ::ExpressionField(parentResponder, inputEventHandler, textFieldDelegate, layoutFieldDelegate) {
+ setLayoutInsertionCursorEvent(Ion::Events::Up);
+ }
protected:
bool handleEvent(Ion::Events::Event event) override;
};
diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp
index 292a6199e..c8c153b6a 100644
--- a/apps/calculation/history_controller.cpp
+++ b/apps/calculation/history_controller.cpp
@@ -1,5 +1,6 @@
#include "history_controller.h"
#include "app.h"
+#include
#include
using namespace Shared;
@@ -15,7 +16,8 @@ HistoryController::HistoryController(EditExpressionController * editExpressionCo
m_complexController(editExpressionController),
m_integerController(editExpressionController),
m_rationalController(editExpressionController),
- m_trigonometryController(editExpressionController)
+ m_trigonometryController(editExpressionController),
+ m_unitController(editExpressionController)
{
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].setParentResponder(&m_selectableTableView);
@@ -37,9 +39,9 @@ void HistoryController::reload() {
* the table view twice.
*/
if (numberOfRows() > 0) {
- m_selectableTableView.scrollToCell(0, numberOfRows()-1);
+ m_selectableTableView.scrollToBottom();
// Force to reload last added cell (hide the burger and exact output if necessary)
- tableViewDidChangeSelection(&m_selectableTableView, 0, numberOfRows()-1);
+ tableViewDidChangeSelectionAndDidScroll(&m_selectableTableView, 0, numberOfRows()-1);
}
}
@@ -78,11 +80,9 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
EditExpressionController * editController = (EditExpressionController *)parentResponder();
if (subviewType == SubviewType::Input) {
m_selectableTableView.deselectTable();
- Container::activeApp()->setFirstResponder(editController);
editController->insertTextBody(calculationAtIndex(focusRow)->inputText());
} else if (subviewType == SubviewType::Output) {
m_selectableTableView.deselectTable();
- Container::activeApp()->setFirstResponder(editController);
Shared::ExpiringPointer calculation = calculationAtIndex(focusRow);
ScrollableTwoExpressionsView::SubviewPosition outputSubviewPosition = selectedCell->outputView()->selectedSubviewPosition();
if (outputSubviewPosition == ScrollableTwoExpressionsView::SubviewPosition::Right
@@ -108,6 +108,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
vc = &m_integerController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Rational) {
vc = &m_rationalController;
+ } else if (additionalInfoType == Calculation::AdditionalInformationType::Unit) {
+ vc = &m_unitController;
}
if (vc) {
vc->setExpression(e);
@@ -120,24 +122,14 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
int focusRow = selectedRow();
SubviewType subviewType = selectedSubviewType();
m_selectableTableView.deselectTable();
- EditExpressionController * editController = (EditExpressionController *)parentResponder();
m_calculationStore->deleteCalculationAtIndex(storeIndex(focusRow));
reload();
if (numberOfRows()== 0) {
- Container::activeApp()->setFirstResponder(editController);
+ Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
- if (focusRow > 0) {
- m_selectableTableView.selectCellAtLocation(0, focusRow-1);
- } else {
- m_selectableTableView.selectCellAtLocation(0, 0);
- }
- if (subviewType == SubviewType::Input) {
- tableViewDidChangeSelection(&m_selectableTableView, 0, selectedRow());
- } else {
- tableViewDidChangeSelection(&m_selectableTableView, 0, -1);
- }
- m_selectableTableView.scrollToCell(0, selectedRow());
+ m_selectableTableView.selectCellAtLocation(0, focusRow > 0 ? focusRow - 1 : 0);
+ setSelectedSubviewType(subviewType, true, 0, selectedRow());
return true;
}
if (event == Ion::Events::Clear) {
@@ -148,9 +140,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
return true;
}
if (event == Ion::Events::Back) {
- EditExpressionController * editController = (EditExpressionController *)parentResponder();
m_selectableTableView.deselectTable();
- Container::activeApp()->setFirstResponder(editController);
+ Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
return false;
@@ -160,19 +151,23 @@ Shared::ExpiringPointer HistoryController::calculationAtIndex(int i
return m_calculationStore->calculationAtIndex(storeIndex(i));
}
-void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
+void HistoryController::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection || previousSelectedCellY == selectedRow()) {
return;
}
if (previousSelectedCellY == -1) {
setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
- } else if (selectedRow() < previousSelectedCellY) {
- setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY);
- } else if (selectedRow() > previousSelectedCellY) {
- setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
} else if (selectedRow() == -1) {
setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY);
+ } else {
+ HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
+ SubviewType nextSelectedSubviewType = selectedSubviewType();
+ if (selectedCell && !selectedCell->displaysSingleLine()) {
+ nextSelectedSubviewType = previousSelectedCellY < selectedRow() ? SubviewType::Input : SubviewType::Output;
+ }
+ setSelectedSubviewType(nextSelectedSubviewType, false, previousSelectedCellX, previousSelectedCellY);
}
+ // The selectedCell may change during setSelectedSubviewType
HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell());
if (selectedCell == nullptr) {
return;
@@ -208,22 +203,42 @@ KDCoordinate HistoryController::rowHeight(int j) {
return 0;
}
Shared::ExpiringPointer calculation = calculationAtIndex(j);
- return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output);
+ bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output;
+ KDCoordinate result = calculation->memoizedHeight(expanded);
+ if (result < 0) {
+ result = HistoryViewCell::Height(calculation.pointer(), expanded);
+ if (result < 0) {
+ // Raise, because Height modified the calculation and failed.
+ Poincare::ExceptionCheckpoint::Raise();
+ }
+ calculation->setMemoizedHeight(expanded, result);
+ }
+ /* We might want to put an assertion here to check the memoization:
+ * assert(result == HistoryViewCell::Height(calculation.pointer(), expanded));
+ * However, Height might fail due to pool memory exhaustion, in which case the
+ * assertion fails even if "result" had the right value. */
+ return result;
}
int HistoryController::typeAtLocation(int i, int j) {
return 0;
}
-void HistoryController::scrollToCell(int i, int j) {
- m_selectableTableView.scrollToCell(i, j);
-}
-
bool HistoryController::calculationAtIndexToggles(int index) {
Context * context = App::app()->localContext();
return index >= 0 && index < m_calculationStore->numberOfCalculations() && calculationAtIndex(index)->displayOutput(context) == Calculation::DisplayOutput::ExactAndApproximateToggle;
}
+
+void HistoryController::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX, int previousSelectedY) {
+ // Avoid selecting non-displayed ellipsis
+ HistoryViewCell * selectedCell = static_cast(m_selectableTableView.selectedCell());
+ if (subviewType == SubviewType::Ellipsis && selectedCell && selectedCell->additionalInformationType() == Calculation::AdditionalInformationType::None) {
+ subviewType = SubviewType::Output;
+ }
+ HistoryViewCellDataSource::setSelectedSubviewType(subviewType, sameCell, previousSelectedX, previousSelectedY);
+}
+
void HistoryController::historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) {
/* If the selection change triggers the toggling of the outputs, we update
* the whole table as the height of the selected cell row might have changed. */
diff --git a/apps/calculation/history_controller.h b/apps/calculation/history_controller.h
index 407e52805..021a0e876 100644
--- a/apps/calculation/history_controller.h
+++ b/apps/calculation/history_controller.h
@@ -9,6 +9,7 @@
#include "additional_outputs/integer_list_controller.h"
#include "additional_outputs/rational_list_controller.h"
#include "additional_outputs/trigonometry_list_controller.h"
+#include "additional_outputs/unit_list_controller.h"
namespace Calculation {
@@ -30,8 +31,8 @@ public:
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
KDCoordinate rowHeight(int j) override;
int typeAtLocation(int i, int j) override;
- void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection = false) override;
- void scrollToCell(int i, int j);
+ void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1) override;
+ void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection = false) override;
private:
int storeIndex(int i) { return numberOfRows() - i - 1; }
Shared::ExpiringPointer calculationAtIndex(int i);
@@ -46,6 +47,7 @@ private:
IntegerListController m_integerController;
RationalListController m_rationalController;
TrigonometryListController m_trigonometryController;
+ UnitListController m_unitController;
};
}
diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp
index ff41c7b3e..1f47f7e0f 100644
--- a/apps/calculation/history_view_cell.cpp
+++ b/apps/calculation/history_view_cell.cpp
@@ -5,17 +5,12 @@
#include
#include
#include
+#include
namespace Calculation {
-static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-
/* HistoryViewCellDataSource */
-HistoryViewCellDataSource::HistoryViewCellDataSource() :
- m_selectedSubviewType(SubviewType::Output) {}
-
void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedCellX, int previousSelectedCellY) {
HistoryViewCell * selectedCell = nullptr;
HistoryViewCell * previouslySelectedCell = nullptr;
@@ -30,6 +25,7 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
if (selectedCell) {
selectedCell->reloadSubviewHighlight();
selectedCell->cellDidSelectSubview(subviewType, previousSubviewType);
+ Container::activeApp()->setFirstResponder(selectedCell);
}
if (previouslySelectedCell) {
previouslySelectedCell->cellDidSelectSubview(SubviewType::Input);
@@ -38,19 +34,35 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType,
/* HistoryViewCell */
-HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
- Responder(parentResponder),
- m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
- m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
- m_calculationExpanded(false),
- m_inputView(this, Metric::CommonLargeMargin, Metric::CommonSmallMargin),
- m_scrollableOutputView(this)
-{
- m_calculationCRC32 = 0;
+KDCoordinate HistoryViewCell::Height(Calculation * calculation, bool expanded) {
+ HistoryViewCell cell(nullptr);
+ bool didForceOutput = false;
+ cell.setCalculation(calculation, expanded, &didForceOutput);
+ if (didForceOutput) {
+ /* We could not compute the height of the calculation as it is (the display
+ * output was forced to another value during the height computation).
+ * Warning: the display output of calculation was actually changed, so it
+ * will cause problems if we already did some computations with another
+ * display value. */
+ return -1;
+ }
+ KDRect ellipsisFrame = KDRectZero;
+ KDRect inputFrame = KDRectZero;
+ KDRect outputFrame = KDRectZero;
+ cell.computeSubviewFrames(Ion::Display::Width, KDCOORDINATE_MAX, &ellipsisFrame, &inputFrame, &outputFrame);
+ return k_margin + inputFrame.unionedWith(outputFrame).height() + k_margin;
}
-Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() {
- return &m_scrollableOutputView;
+HistoryViewCell::HistoryViewCell(Responder * parentResponder) :
+ Responder(parentResponder),
+ m_calculationCRC32(0),
+ m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown),
+ m_calculationAdditionInformation(Calculation::AdditionalInformationType::None),
+ m_inputView(this, k_inputViewHorizontalMargin, k_inputOutputViewsVerticalMargin),
+ m_scrollableOutputView(this),
+ m_calculationExpanded(false),
+ m_calculationSingleLine(false)
+{
}
void HistoryViewCell::setEven(bool even) {
@@ -138,15 +150,6 @@ void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewTyp
reloadScroll();
}
-KDColor HistoryViewCell::backgroundColor() const {
- KDColor background = m_even ? Palette::CalculationBackgroundEven : Palette::CalculationBackgroundOdd;
- return background;
-}
-
-int HistoryViewCell::numberOfSubviews() const {
- return 2 + displayedEllipsis();
-}
-
View * HistoryViewCell::subviewAtIndex(int index) {
/* The order of the subviews should not matter here as they don't overlap.
* However, the order determines the order of redrawing as well. For several
@@ -170,29 +173,69 @@ View * HistoryViewCell::subviewAtIndex(int index) {
return views[index];
}
+bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) {
+ // k_margin is the separation between the input and output.
+ return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth;
+}
+
void HistoryViewCell::layoutSubviews(bool force) {
- KDCoordinate maxFrameWidth = bounds().width();
- if (displayedEllipsis()) {
- m_ellipsis.setFrame(KDRect(maxFrameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, bounds().height()), force);
- maxFrameWidth -= Metric::EllipsisCellWidth;
- } else {
- m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty
+ KDRect frameBounds = bounds();
+ if (bounds().width() <= 0 || bounds().height() <= 0) {
+ // TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews
+ return;
}
+ KDRect ellipsisFrame = KDRectZero;
+ KDRect inputFrame = KDRectZero;
+ KDRect outputFrame = KDRectZero;
+ computeSubviewFrames(frameBounds.width(), frameBounds.height(), &ellipsisFrame, &inputFrame, &outputFrame);
+
+ m_ellipsis.setFrame(ellipsisFrame, force); // Required even if ellipsisFrame is KDRectZero, to mark previous rect as dirty
+ m_inputView.setFrame(inputFrame,force);
+ m_scrollableOutputView.setFrame(outputFrame, force);
+}
+
+void HistoryViewCell::computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame) {
+ assert(ellipsisFrame != nullptr && inputFrame != nullptr && outputFrame != nullptr);
+
+ if (displayedEllipsis()) {
+ *ellipsisFrame = KDRect(frameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, frameHeight);
+ frameWidth -= Metric::EllipsisCellWidth;
+ } else {
+ *ellipsisFrame = KDRectZero;
+ }
+
KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay();
- m_inputView.setFrame(KDRect(
- 0, 0,
- minCoordinate(maxFrameWidth, inputSize.width()),
- inputSize.height()),
- force);
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
- int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6;
- int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
- m_scrollableOutputView.setFrame(KDRect(
- maxCoordinate(0, maxFrameWidth - outputSize.width()),
- outputHeight,
- minCoordinate(maxFrameWidth, outputSize.width()),
- outputSize.height()),
- force);
+
+ /* To compute if the calculation is on a single line, use the expanded width
+ * if there is both an exact and an approximate layout. */
+ m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), m_scrollableOutputView.minimalSizeForOptimalDisplayFullSize().width());
+
+ KDCoordinate inputY = k_margin;
+ KDCoordinate outputY = k_margin;
+ if (m_calculationSingleLine && !m_inputView.layout().isUninitialized()) {
+ KDCoordinate inputBaseline = m_inputView.layout().baseline();
+ KDCoordinate outputBaseline = m_scrollableOutputView.baseline();
+ KDCoordinate baselineDifference = outputBaseline - inputBaseline;
+ if (baselineDifference > 0) {
+ inputY += baselineDifference;
+ } else {
+ outputY += -baselineDifference;
+ }
+ } else {
+ outputY += inputSize.height();
+ }
+
+ *inputFrame = KDRect(
+ 0,
+ inputY,
+ std::min(frameWidth, inputSize.width()),
+ inputSize.height());
+ *outputFrame = KDRect(
+ std::max(0, frameWidth - outputSize.width()),
+ outputY,
+ std::min(frameWidth, outputSize.width()),
+ outputSize.height());
}
void HistoryViewCell::resetMemoization() {
@@ -203,7 +246,8 @@ void HistoryViewCell::resetMemoization() {
m_calculationCRC32 = 0;
}
-void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
+void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded, bool * didForceOutput) {
+ assert(!didForceOutput || *didForceOutput == false);
uint32_t newCalculationCRC = Ion::crc32Byte((const uint8_t *)calculation, ((char *)calculation->next()) - ((char *) calculation));
if (newCalculationCRC == m_calculationCRC32 && m_calculationExpanded == expanded) {
return;
@@ -231,6 +275,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
if (couldNotCreateExactLayout) {
if (calculation->displayOutput(context) != ::Calculation::Calculation::DisplayOutput::ExactOnly) {
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
+ if (didForceOutput) {
+ *didForceOutput = true;
+ }
} else {
/* We should only display the exact result, but we cannot create it
* -> raise an exception. */
@@ -253,6 +300,9 @@ void HistoryViewCell::setCalculation(Calculation * calculation, bool expanded) {
/* Set the display output to ApproximateOnly, make room in the pool by
* erasing the exact layout, and retry to create the approximate layout */
calculation->forceDisplayOutput(::Calculation::Calculation::DisplayOutput::ApproximateOnly);
+ if (didForceOutput) {
+ *didForceOutput = true;
+ }
exactOutputLayout = Poincare::Layout();
couldNotCreateApproximateLayout = false;
approximateOutputLayout = calculation->createApproximateOutputLayout(context, &couldNotCreateApproximateLayout);
@@ -286,31 +336,41 @@ void HistoryViewCell::didBecomeFirstResponder() {
}
bool HistoryViewCell::handleEvent(Ion::Events::Event event) {
- assert(m_dataSource);
+ assert(m_dataSource != nullptr);
HistoryViewCellDataSource::SubviewType type = m_dataSource->selectedSubviewType();
- if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input) ||
- (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) ||
- (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) ||
- (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis)) {
- HistoryViewCellDataSource::SubviewType otherSubviewType;
- if (event == Ion::Events::Down) {
- otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
- } else if (event == Ion::Events::Up) {
- otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
- } else if (event == Ion::Events::Right) {
- otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
- } else {
- assert(event == Ion::Events::Left);
- otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
+ assert(type != HistoryViewCellDataSource::SubviewType::None);
+ HistoryViewCellDataSource::SubviewType otherSubviewType = HistoryViewCellDataSource::SubviewType::None;
+ if (m_calculationSingleLine) {
+ static_assert(
+ static_cast(HistoryViewCellDataSource::SubviewType::None) == 0
+ && static_cast(HistoryViewCellDataSource::SubviewType::Input) == 1
+ && static_cast(HistoryViewCellDataSource::SubviewType::Output) == 2
+ && static_cast(HistoryViewCellDataSource::SubviewType::Ellipsis) == 3,
+ "The array types is not well-formed anymore");
+ HistoryViewCellDataSource::SubviewType types[] = {
+ HistoryViewCellDataSource::SubviewType::None,
+ HistoryViewCellDataSource::SubviewType::Input,
+ HistoryViewCellDataSource::SubviewType::Output,
+ displayedEllipsis() ? HistoryViewCellDataSource::SubviewType::Ellipsis : HistoryViewCellDataSource::SubviewType::None,
+ HistoryViewCellDataSource::SubviewType::None,
+ };
+ if (event == Ion::Events::Right || event == Ion::Events::Left) {
+ otherSubviewType = types[static_cast(type) + (event == Ion::Events::Right ? 1 : -1)];
}
- m_dataSource->setSelectedSubviewType(otherSubviewType, true);
- return true;
+ } else if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input)
+ || (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis))
+ {
+ otherSubviewType = HistoryViewCellDataSource::SubviewType::Output;
+ } else if (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) {
+ otherSubviewType = HistoryViewCellDataSource::SubviewType::Input;
+ } else if (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) {
+ otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis;
}
- return false;
-}
-
-bool HistoryViewCell::displayedEllipsis() const {
- return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
+ if (otherSubviewType == HistoryViewCellDataSource::SubviewType::None) {
+ return false;
+ }
+ m_dataSource->setSelectedSubviewType(otherSubviewType, true);
+ return true;
}
}
diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h
index 7d6c12b45..2932ee1a5 100644
--- a/apps/calculation/history_view_cell.h
+++ b/apps/calculation/history_view_cell.h
@@ -12,14 +12,14 @@ class HistoryViewCell;
class HistoryViewCellDataSource {
public:
enum class SubviewType {
- None,
- Input,
- Output,
- Ellipsis
+ None = 0,
+ Input = 1,
+ Output = 2,
+ Ellipsis = 3
};
- HistoryViewCellDataSource();
- void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
- SubviewType selectedSubviewType() { return m_selectedSubviewType; }
+ HistoryViewCellDataSource() : m_selectedSubviewType(SubviewType::Output) {}
+ virtual void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1);
+ SubviewType selectedSubviewType() const { return m_selectedSubviewType; }
private:
/* This method should belong to a delegate instead of a data source but as
* both the data source and the delegate will be the same controller, we
@@ -31,39 +31,52 @@ private:
class HistoryViewCell : public ::EvenOddCell, public Responder {
public:
+ constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin;
+ constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin;
+ constexpr static KDCoordinate k_inputViewHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin;
+ static KDCoordinate Height(Calculation * calculation, bool expanded);
HistoryViewCell(Responder * parentResponder = nullptr);
+ static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth);
void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None);
void setEven(bool even) override;
void setHighlighted(bool highlight) override;
void reloadSubviewHighlight();
void setDataSource(HistoryViewCellDataSource * dataSource) { m_dataSource = dataSource; }
+ bool displaysSingleLine() const {
+ return m_calculationSingleLine;
+ }
Responder * responder() override {
return this;
}
Poincare::Layout layout() const override;
- KDColor backgroundColor() const override;
+ KDColor backgroundColor() const override { return m_even ? Palette::CalculationBackgroundEven : Palette::CalculationBackgroundOdd; }
void resetMemoization();
- void setCalculation(Calculation * calculation, bool expanded);
- int numberOfSubviews() const override;
+ void setCalculation(Calculation * calculation, bool expanded, bool * didForceOutput = nullptr);
+ int numberOfSubviews() const override { return 2 + displayedEllipsis(); }
View * subviewAtIndex(int index) override;
void layoutSubviews(bool force = false) override;
void didBecomeFirstResponder() override;
bool handleEvent(Ion::Events::Event event) override;
- Shared::ScrollableTwoExpressionsView * outputView();
+ Shared::ScrollableTwoExpressionsView * outputView() { return &m_scrollableOutputView; }
+ ScrollableExpressionView * inputView() { return &m_inputView; }
Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; }
private:
constexpr static KDCoordinate k_resultWidth = 80;
+ void computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame);
void reloadScroll();
void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType);
- bool displayedEllipsis() const;
+ bool displayedEllipsis() const {
+ return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None;
+ }
uint32_t m_calculationCRC32;
Calculation::DisplayOutput m_calculationDisplayOutput;
Calculation::AdditionalInformationType m_calculationAdditionInformation;
- bool m_calculationExpanded;
ScrollableExpressionView m_inputView;
Shared::ScrollableTwoExpressionsView m_scrollableOutputView;
EvenOddCellWithEllipsis m_ellipsis;
HistoryViewCellDataSource * m_dataSource;
+ bool m_calculationExpanded;
+ bool m_calculationSingleLine;
};
}
diff --git a/apps/calculation/selectable_table_view.cpp b/apps/calculation/selectable_table_view.cpp
index e2d81e53c..8a2884ce1 100644
--- a/apps/calculation/selectable_table_view.cpp
+++ b/apps/calculation/selectable_table_view.cpp
@@ -1,4 +1,5 @@
#include "selectable_table_view.h"
+#include
namespace Calculation {
@@ -11,18 +12,23 @@ CalculationSelectableTableView::CalculationSelectableTableView(Responder * paren
setDecoratorType(ScrollView::Decorator::Type::None);
}
+void CalculationSelectableTableView::scrollToBottom() {
+ KDCoordinate contentOffsetX = contentOffset().x();
+ KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(dataSource()->numberOfRows()) - maxContentHeightDisplayableWithoutScrolling();
+ setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
+}
+
void CalculationSelectableTableView::scrollToCell(int i, int j) {
- ::SelectableTableView::scrollToCell(i, j);
if (m_contentView.bounds().height() < bounds().height()) {
setTopMargin(bounds().height() - m_contentView.bounds().height());
} else {
setTopMargin(0);
}
+ ::SelectableTableView::scrollToCell(i, j);
ScrollView::layoutSubviews();
if (m_contentView.bounds().height() - contentOffset().y() < bounds().height()) {
- KDCoordinate contentOffsetX = contentOffset().x();
- KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(dataSource()->numberOfRows()) - maxContentHeightDisplayableWithoutScrolling();
- setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
+ // Avoid empty space at the end of the table
+ scrollToBottom();
}
}
@@ -31,23 +37,63 @@ void CalculationSelectableTableView::scrollToSubviewOfTypeOfCellAtLocation(Histo
return;
}
/* As we scroll, the selected calculation does not use the same history view
- * cell, thus, we want to deselect the previous used history view cell. */
+ * cell, thus, we want to deselect the previous used history view cell. (*) */
unhighlightSelectedCell();
/* Main part of the scroll */
+ HistoryViewCell * cell = static_cast(selectedCell());
+ assert(cell);
KDCoordinate contentOffsetX = contentOffset().x();
- KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(j+1) - maxContentHeightDisplayableWithoutScrolling();
- if (subviewType == HistoryViewCellDataSource::SubviewType::Input) {
- if (j == 0) {
- contentOffsetY = 0;
- } else {
- contentOffsetY = dataSource()->cumulatedHeightFromIndex(j);
- }
+
+ KDCoordinate contentOffsetY = dataSource()->cumulatedHeightFromIndex(j);
+ if (cell->displaysSingleLine() && dataSource()->rowHeight(j) > maxContentHeightDisplayableWithoutScrolling()) {
+ /* If we cannot display the full calculation, we display the selected
+ * layout as close as possible to the top of the screen without drawing
+ * empty space between the history and the input field.
+ *
+ * Below are some values we can assign to contentOffsetY, and the kinds of
+ * display they entail :
+ * (the selected cell is at index j)
+ *
+ * 1 - cumulatedHeightFromIndex(j)
+ * Aligns the top of the cell with the top of the zone in which the
+ * history can be drawn.
+ *
+ * 2 - (cumulatedHeightFromIndex(j+1)
+ * - maxContentHeightDisplayableWithoutScrolling())
+ * Aligns the bottom of the cell with the top of the input field.
+ *
+ * 3 - cumulatedHeightFromIndex(j) + baseline1 - baseline2
+ * Aligns the top of the selected layout with the top of the screen (only
+ * used when the selected layout is the smallest).
+ *
+ * The following drawing shows where the calculation would be aligned with
+ * each value of contentOffsetY, for the calculation (1/3)/(4/2) = 1/6.
+ *
+ * (1) (2) (3)
+ * +--------------+ +--------------+ +--------------+
+ * | 1 | | --- - | | 3 1 |
+ * | - | | 4 6 | | --- - |
+ * | 3 1 | | - | | 4 6 |
+ * | --- - | | 2 | | - |
+ * +--------------+ +--------------+ +--------------+
+ * | (1/3)/(4/2) | | (1/3)/(4/2) | | (1/3)/(4/2) |
+ * +--------------+ +--------------+ +--------------+
+ *
+ * */
+ contentOffsetY += std::min(
+ dataSource()->rowHeight(j) - maxContentHeightDisplayableWithoutScrolling(),
+ std::max(0, (cell->inputView()->layout().baseline() - cell->outputView()->baseline()) * (subviewType == HistoryViewCellDataSource::SubviewType::Input ? -1 : 1)));
+ } else if (subviewType != HistoryViewCellDataSource::SubviewType::Input) {
+ contentOffsetY += dataSource()->rowHeight(j) - maxContentHeightDisplayableWithoutScrolling();
}
+
setContentOffset(KDPoint(contentOffsetX, contentOffsetY));
- /* For the same reason, we have to rehighlight the new history view cell and
- * reselect the first responder. */
- HistoryViewCell * cell = (HistoryViewCell *)(selectedCell());
+ /* For the same reason as (*), we have to rehighlight the new history view
+ * cell and reselect the first responder.
+ * We have to recall "selectedCell" because when the table might have been
+ * relayouted in "setContentOffset".*/
+ cell = static_cast(selectedCell());
assert(cell);
cell->setHighlighted(true);
Container::activeApp()->setFirstResponder(cell);
diff --git a/apps/calculation/selectable_table_view.h b/apps/calculation/selectable_table_view.h
index 07823d9f1..d1740ab78 100644
--- a/apps/calculation/selectable_table_view.h
+++ b/apps/calculation/selectable_table_view.h
@@ -9,6 +9,7 @@ class CalculationSelectableTableView : public ::SelectableTableView {
public:
CalculationSelectableTableView(Responder * parentResponder, TableViewDataSource * dataSource,
SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate = nullptr);
+ void scrollToBottom();
void scrollToCell(int i, int j) override;
void scrollToSubviewOfTypeOfCellAtLocation(HistoryViewCellDataSource::SubviewType subviewType, int i, int j);
};
diff --git a/apps/code/Makefile b/apps/code/Makefile
index 8e7dd87cb..de11dcb4c 100644
--- a/apps/code/Makefile
+++ b/apps/code/Makefile
@@ -11,42 +11,31 @@ app_code_src = $(addprefix apps/code/,\
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 \
+)
+
+app_code_test_src = $(addprefix apps/code/,\
+ python_toolbox.cpp \
+ script.cpp \
+ script_node_cell.cpp \
script_store.cpp \
script_template.cpp \
+ variable_box_empty_controller.cpp \
variable_box_controller.cpp \
)
-app_src += $(app_code_src)
-
-i18n_files += $(addprefix apps/code/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
- base.universal.i18n\
- catalog.de.i18n\
- catalog.en.i18n\
- catalog.es.i18n\
- catalog.fr.i18n\
- catalog.pt.i18n\
- catalog.hu.i18n\
- catalog.universal.i18n\
- toolbox.de.i18n\
- toolbox.en.i18n\
- toolbox.es.i18n\
- toolbox.fr.i18n\
- toolbox.pt.i18n\
- toolbox.hu.i18n\
- toolbox.universal.i18n\
+tests_src += $(addprefix apps/code/test/,\
+ variable_box_controller.cpp\
)
+app_code_src += $(app_code_test_src)
+apps_src += $(app_code_src)
+
+i18n_files += $(call i18n_with_universal_for,code/base)
+i18n_files += $(call i18n_with_universal_for,code/catalog)
+i18n_files += $(call i18n_with_universal_for,code/toolbox)
+
$(eval $(call depends_on_image,apps/code/app.cpp,apps/code/code_icon.png))
diff --git a/apps/code/app.cpp b/apps/code/app.cpp
index 10d372183..9de25b735 100644
--- a/apps/code/app.cpp
+++ b/apps/code/app.cpp
@@ -14,8 +14,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::CodeAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::BasicExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Basic;
}
const Image * App::Descriptor::icon() {
@@ -67,7 +67,7 @@ void App::Snapshot::setOpt(const char * name, const char * value) {
const char * scriptContent = separator;
Code::ScriptTemplate script(scriptName, scriptContent);
m_scriptStore.addScriptFromTemplate(&script);
- m_scriptStore.scriptNamed(scriptName).toggleImportationStatus(); // set Importation Status to 1
+ ScriptStore::ScriptNamed(scriptName).toggleAutoimportationStatus(); // set Importation Status to 1
return;
}
if (strcmp(name, "lock-on-console") == 0) {
diff --git a/apps/code/app.h b/apps/code/app.h
index bdc3b9f05..2deeb7f03 100644
--- a/apps/code/app.h
+++ b/apps/code/app.h
@@ -18,7 +18,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
@@ -51,6 +51,7 @@ public:
}
StackViewController * stackViewController() { return &m_codeStackViewController; }
ConsoleController * consoleController() { return &m_consoleController; }
+ MenuController * menuController() { return &m_menuController; }
/* Responder */
bool handleEvent(Ion::Events::Event event) override;
diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n
index 5c4014a14..40cf856fa 100644
--- a/apps/code/base.de.i18n
+++ b/apps/code/base.de.i18n
@@ -1,9 +1,15 @@
-Console = "Interaktive Konsole"
AddScript = "Skript hinzufügen"
-ScriptOptions = "Skriptoptionen"
-ExecuteScript = "Skript ausführen"
+AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _"
+Autocomplete = "Autovervollständigung"
AutoImportScript = "Automatischer Import in Konsole"
+BuiltinsAndKeywords = "Native Funktionen und Schlüsselwörter"
+Console = "Interaktive Konsole"
DeleteScript = "Skript löschen"
DuplicateScript = "Skript duplizieren"
+ExecuteScript = "Skript ausführen"
FunctionsAndVariables = "Funktionen und Variablen"
-AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _"
+ImportedModulesAndScripts = "Importierte Module und Skripte"
+NoWordAvailableHere = "Kein Wort ist hier verfübar."
+ScriptInProgress = "Aktuelle Skript"
+ScriptOptions = "Skriptoptionen"
+ScriptSize = "Script size"
diff --git a/apps/code/base.en.i18n b/apps/code/base.en.i18n
index d9886d3fd..082bcfd79 100644
--- a/apps/code/base.en.i18n
+++ b/apps/code/base.en.i18n
@@ -1,9 +1,15 @@
-Console = "Python shell"
AddScript = "Add a script"
-ScriptOptions = "Script options"
-ExecuteScript = "Execute script"
+AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _"
+Autocomplete = "Autocomplete"
AutoImportScript = "Auto import in shell"
+BuiltinsAndKeywords = "Builtins and keywords"
+Console = "Python shell"
DeleteScript = "Delete script"
DuplicateScript = "Duplicate script"
+ExecuteScript = "Execute script"
FunctionsAndVariables = "Functions and variables"
-AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _"
+ImportedModulesAndScripts = "Imported modules and scripts"
+NoWordAvailableHere = "No word available here."
+ScriptInProgress = "Script in progress"
+ScriptOptions = "Script options"
+ScriptSize = "Script size"
diff --git a/apps/code/base.es.i18n b/apps/code/base.es.i18n
index 3038a29f5..5d9087b54 100644
--- a/apps/code/base.es.i18n
+++ b/apps/code/base.es.i18n
@@ -1,9 +1,15 @@
-Console = "Interprete de comandos"
AddScript = "Agregar un archivo"
-ScriptOptions = "Opciones del archivo"
-ExecuteScript = "Ejecutar el archivo"
+AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
+Autocomplete = "Autocompleción"
AutoImportScript = "Importación auto en intérprete"
+BuiltinsAndKeywords = "Funciones nativas y palabras clave"
+Console = "Interprete de comandos"
DeleteScript = "Eliminar el archivo"
DuplicateScript = "Duplicar el guión"
+ExecuteScript = "Ejecutar el archivo"
FunctionsAndVariables = "Funciones y variables"
-AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
+ImportedModulesAndScripts = "Módulos y archivos importados"
+NoWordAvailableHere = "No hay ninguna palabra disponible aquí."
+ScriptInProgress = "Archivo en curso"
+ScriptOptions = "Opciones del archivo"
+ScriptSize = "Script size"
diff --git a/apps/code/base.fr.i18n b/apps/code/base.fr.i18n
index 48b12d6d7..b9006a2d0 100644
--- a/apps/code/base.fr.i18n
+++ b/apps/code/base.fr.i18n
@@ -1,9 +1,15 @@
-Console = "Console d'exécution"
AddScript = "Ajouter un script"
-ScriptOptions = "Options de script"
-ExecuteScript = "Exécuter le script"
+AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _"
+Autocomplete = "Auto-complétion"
AutoImportScript = "Importation auto dans la console"
+BuiltinsAndKeywords = "Fonctions natives et mots-clés"
+Console = "Console d'exécution"
DeleteScript = "Supprimer le script"
DuplicateScript = "Dupliquer le script"
+ExecuteScript = "Exécuter le script"
FunctionsAndVariables = "Fonctions et variables"
-AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _"
+ImportedModulesAndScripts = "Modules et scripts importés"
+NoWordAvailableHere = "Aucun mot disponible à cet endroit."
+ScriptInProgress = "Script en cours"
+ScriptOptions = "Options de script"
+ScriptSize = "Script size"
diff --git a/apps/code/base.hu.i18n b/apps/code/base.hu.i18n
index 91437ca29..f5d1ab6fe 100644
--- a/apps/code/base.hu.i18n
+++ b/apps/code/base.hu.i18n
@@ -1,9 +1,15 @@
-Console = "Konzol"
AddScript = "Script hozzadáadása"
-ScriptOptions = "Script beállítások"
-ExecuteScript = "Script indítása"
+AllowedCharactersaz09 = "Engedélyezett karakterek: a-z, 0-9, _"
+Autocomplete = "Autocomplete"
AutoImportScript = "Script automata importálása"
+BuiltinsAndKeywords = "Builtins and keywords"
+Console = "Konzol"
DeleteScript = "Script törlése"
DuplicateScript = "Script másolása"
+ExecuteScript = "Script indítása"
FunctionsAndVariables = "Függvények és változók"
-AllowedCharactersaz09 = "Engedélyezett karakterek: a-z, 0-9, _"
+ImportedModulesAndScripts = "Imported modules and scripts"
+NoWordAvailableHere = "No word available here."
+ScriptInProgress = "Script in progress"
+ScriptOptions = "Script beállítások"
+ScriptSize = "Script size"
diff --git a/apps/code/base.it.i18n b/apps/code/base.it.i18n
new file mode 100644
index 000000000..d3778db0a
--- /dev/null
+++ b/apps/code/base.it.i18n
@@ -0,0 +1,15 @@
+AddScript = "Aggiungere script"
+AllowedCharactersaz09 = "Caratteri consentiti : a-z, 0-9, _"
+Autocomplete = "Autocompletamento"
+AutoImportScript = "Importazione automatica dello script"
+BuiltinsAndKeywords = "Funzioni native e parole chiave"
+Console = "Console d'esecuzione"
+DeleteScript = "Eliminare lo script"
+DuplicateScript = "Duplicate script"
+ExecuteScript = "Eseguire lo script"
+FunctionsAndVariables = "Funzioni e variabili"
+ImportedModulesAndScripts = "Moduli e scripts importati"
+NoWordAvailableHere = "Nessuna parola disponibile qui."
+ScriptInProgress = "Script in corso"
+ScriptOptions = "Opzioni dello script"
+ScriptSize = "Script Size"
diff --git a/apps/code/base.nl.i18n b/apps/code/base.nl.i18n
new file mode 100644
index 000000000..769bdbb72
--- /dev/null
+++ b/apps/code/base.nl.i18n
@@ -0,0 +1,15 @@
+AddScript = "Script toevoegen"
+AllowedCharactersaz09 = "Toegestane tekens: a-z, 0-9, _"
+Autocomplete = "Autocomplete"
+AutoImportScript = "Automatisch importeren in shell"
+BuiltinsAndKeywords = "Builtins and keywords"
+Console = "Python shell"
+DeleteScript = "Script verwijderen"
+DuplicateScript = "Duplicate script"
+ExecuteScript = "Script uitvoeren"
+FunctionsAndVariables = "Functies en variabelen"
+ImportedModulesAndScripts = "Imported modules and scripts"
+NoWordAvailableHere = "No word available here."
+ScriptInProgress = "Script in progress"
+ScriptOptions = "Script opties"
+ScriptSize = "Script Size"
diff --git a/apps/code/base.pt.i18n b/apps/code/base.pt.i18n
index 0fdbd1490..889c0fa35 100644
--- a/apps/code/base.pt.i18n
+++ b/apps/code/base.pt.i18n
@@ -1,9 +1,15 @@
-Console = "Interpretador interativo"
AddScript = "Adicionar um script"
-ScriptOptions = "Opções de script"
-ExecuteScript = "Executar o script"
+AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
+Autocomplete = "Preenchimento automático"
AutoImportScript = "Importação auto no interpretador"
+BuiltinsAndKeywords = "Funções nativas e palavras-chave"
+Console = "Interpretador interativo"
DeleteScript = "Eliminar o script"
DuplicateScript = "Duplicar o script"
+ExecuteScript = "Executar o script"
FunctionsAndVariables = "Funções e variáveis"
-AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _"
+ImportedModulesAndScripts = "Módulos e scripts importados"
+NoWordAvailableHere = "Nenhuma palavra disponível aqui."
+ScriptInProgress = "Script em curso"
+ScriptOptions = "Opções de script"
+ScriptSize = "Script Size"
diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n
index f53e3d884..bbfd013aa 100644
--- a/apps/code/catalog.de.i18n
+++ b/apps/code/catalog.de.i18n
@@ -26,7 +26,18 @@ PythonCeil = "Aufrundung"
PythonChoice = "Zufallszahl aus der Liste"
PythonClear = "Leere die Liste"
PythonCmathFunction = "cmath-Modul-Funktionspräfix"
-PythonColor = "Definiert eine RGB-Farbe"
+PythonColor = "Definiere eine RGB-Farbe"
+PythonColorBlack = "Black color"
+PythonColorBlue = "Blue color"
+PythonColorBrown = "Brown color"
+PythonColorGreen = "Green color"
+PythonColorGrey = "Grey color"
+PythonColorOrange = "Orange color"
+PythonColorPink = "Pink color"
+PythonColorPurple = "Purple color"
+PythonColorRed = "Red color"
+PythonColorWhite = "White color"
+PythonColorYellow = "Yellow color"
PythonComplex = "a+ib zurückgeben"
PythonCopySign = "x mit dem Vorzeichen von y"
PythonCos = "Kosinus"
@@ -45,10 +56,10 @@ PythonFillRect = "Malt ein Rechteck bei Pixel (x,y)"
PythonFloat = "Wandelt x zu float um"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
-PythonFrExp = "Rest und Exponent von x"
-PythonGamma = "Gammafunktion"
-PythonGetPixel = "Farbe von Pixel (x,y)"
-PythonGetrandbits = "Ganzzahl mit k zufälligen Bits"
+PythonFrExp = "Mantissa and exponent of x: (m,e)"
+PythonGamma = "Gamma function"
+PythonGetPixel = "Return pixel (x,y) color"
+PythonGetrandbits = "Integer with k random bits"
PythonGrid = "Toggle the visibility of the grid"
PythonHex = "Ganzzahl zu Hexadecimal"
PythonHist = "Draw the histogram of x"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
-PythonRandrange = "Random number in range(start, stop)"
+PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
-PythonRect = "z in cartesian coordinates"
+PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
-PythonTurtleBlack = "Schwarze Farbe"
-PythonTurtleBlue = "Blaue Farbe"
-PythonTurtleBrown = "Braune Farbe"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Stiftfarbe setzen"
+PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
-PythonTurtleGreen = "Grüne Farbe"
-PythonTurtleGrey = "Graue Farbe"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
-PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
-PythonTurtlePink = "Pinke Farbe"
PythonTurtlePosition = "Return the current (x,y) location"
-PythonTurtlePurple = "Purple color"
-PythonTurtleRed = "Rote Farbe"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
-PythonTurtleSetposition = "Position des turtles"
-PythonTurtleShowturtle = "Die turtle anzeigen"
-PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10"
-PythonTurtleWhite = "Weiße Farbe"
-PythonTurtleYellow = "Gelbe Farbe"
-PythonUniform = "Fließkommazahl in [a,b]"
-PythonTimeFromImport = "Import time module"
-PythonTimeImport = "Import time module"
+PythonTurtleSetposition = "Positionne la tortue"
+PythonTurtleShowturtle = "Show the turtle"
+PythonTurtleSpeed = "Drawing speed between 0 and 10"
+PythonTurtleWrite = "Display a text"
+PythonUniform = "Floating point number in [a,b]"
+PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
-PythonTimeSleep = "Warten Sie n Sekunden lang"
-PythonTimeMonotonic = "Monotone Zeit zurückgeben"
+PythonTimeSleep = "Wait for n second"
+PythonMonotonic = "Return monotonic time"
+PythonFileOpen = "Öffnet eine Datei"
+PythonFileSeekable = "Ist eine Datei durchsuchbar?"
+PythonFileSeek = "Dateicursor verschieben"
+PythonFileTell = "Cursorposition der Datei abrufen"
+PythonFileClose = "Schließt eine Datei"
+PythonFileClosed = "Wenn Datei geschlossen wurde"
+PythonFileRead = "Bis zu size Bytes lesen"
+PythonFileWrite = "Schreibe b in die Datei"
+PythonFileReadline = "Lies eine Zeile"
+PythonFileReadlines = "Liest eine Liste von Zeilen"
+PythonFileTruncate = "Größe der Datei ändern"
+PythonFileWritelines = "Schreibt eine Liste von Zeilen"
+PythonFileName = "Dateiname"
+PythonFileMode = "Dateiöffnungsmodus"
+PythonFileReadable = "Ist die Datei lesbar?"
+PythonFileWritable = "Ist die Datei beschreibbar?"
diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n
index 31755c5d1..5f8657118 100644
--- a/apps/code/catalog.en.i18n
+++ b/apps/code/catalog.en.i18n
@@ -1,7 +1,7 @@
PythonPound = "Comment"
PythonPercent = "Modulo"
Python1J = "Imaginary i"
-PythonLF = "Line feed"
+PythonLF = "line feed"
PythonTab = "Tabulation"
PythonAmpersand = "Bitwise and"
PythonSymbolExp = "Bitwise exclusive or"
@@ -27,6 +27,17 @@ PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
+PythonColorBlack = "Black color"
+PythonColorBlue = "Blue color"
+PythonColorBrown = "Brown color"
+PythonColorGreen = "Green color"
+PythonColorGrey = "Grey color"
+PythonColorOrange = "Orange color"
+PythonColorPink = "Pink color"
+PythonColorPurple = "Purple color"
+PythonColorRed = "Red color"
+PythonColorWhite = "White color"
+PythonColorYellow = "Yellow color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
@@ -45,7 +56,7 @@ PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
-PythonFrExp = "Mantissa and exponent of x"
+PythonFrExp = "Mantissa and exponent of x: (m,e)"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
-PythonRandrange = "Random number in range(start, stop)"
+PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
-PythonRect = "z in cartesian coordinates"
+PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
-PythonTurtleBlack = "Black color"
-PythonTurtleBlue = "Blue color"
-PythonTurtleBrown = "Brown color"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
+PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
-PythonTurtleGreen = "Green color"
-PythonTurtleGrey = "Grey color"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
-PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
-PythonTurtlePink = "Pink color"
PythonTurtlePosition = "Return the current (x,y) location"
-PythonTurtlePurple = "Purple color"
-PythonTurtleRed = "Red color"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
-PythonTurtleWhite = "White color"
-PythonTurtleYellow = "Yellow color"
+PythonTurtleWrite = "Display a text"
PythonUniform = "Floating point number in [a,b]"
-PythonTimeFromImport = "Import time module"
-PythonTimeImport = "Import time module"
+PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Wait for n second"
-PythonTimeMonotonic = "Return monotonic time"
+PythonMonotonic = "Return monotonic time"
+PythonFileOpen = "Opens a file"
+PythonFileSeekable = "Tells if seek can be used on a file"
+PythonFileSeek = "Move file's cursor"
+PythonFileTell = "Get file's cursor location"
+PythonFileClose = "Closes a file"
+PythonFileClosed = "True if file was closed"
+PythonFileRead = "Read up to size bytes"
+PythonFileWrite = "Write b into file"
+PythonFileReadline = "Reads a line or up to size bytes"
+PythonFileReadlines = "Reads a list of lines"
+PythonFileTruncate = "Resize the file to size"
+PythonFileWritelines = "Writes a list of lines"
+PythonFileName = "Contains file's name"
+PythonFileMode = "Contains file's open mode"
+PythonFileReadable = "Tells if read can be used on a file"
+PythonFileWritable = "Tells if write can be used on a file"
diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n
index e5a7d3d47..35fa14d4e 100644
--- a/apps/code/catalog.es.i18n
+++ b/apps/code/catalog.es.i18n
@@ -27,6 +27,17 @@ PythonChoice = "Random number in the list"
PythonClear = "Empty the list"
PythonCmathFunction = "cmath module function prefix"
PythonColor = "Define a rgb color"
+PythonColorBlack = "Black color"
+PythonColorBlue = "Blue color"
+PythonColorBrown = "Brown color"
+PythonColorGreen = "Green color"
+PythonColorGrey = "Grey color"
+PythonColorOrange = "Orange color"
+PythonColorPink = "Pink color"
+PythonColorPurple = "Purple color"
+PythonColorRed = "Red color"
+PythonColorWhite = "White color"
+PythonColorYellow = "Yellow color"
PythonComplex = "Return a+ib"
PythonCopySign = "Return x with the sign of y"
PythonCos = "Cosine"
@@ -45,7 +56,7 @@ PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
-PythonFrExp = "Mantissa and exponent of x"
+PythonFrExp = "Mantissa and exponent of x: (m,e)"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
@@ -139,10 +150,10 @@ PythonRadians = "Convert x from degrees to radians"
PythonRandint = "Random integer in [a,b]"
PythonRandom = "Floating point number in [0,1["
PythonRandomFunction = "random module function prefix"
-PythonRandrange = "Random number in range(start, stop)"
+PythonRandrange = "Random number in range(start,stop)"
PythonRangeStartStop = "List from start to stop-1"
PythonRangeStop = "List from 0 to stop-1"
-PythonRect = "z in cartesian coordinates"
+PythonRect = "Convert to cartesian coordinates"
PythonRemove = "Remove the first occurrence of x"
PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
-PythonTurtleBlack = "Black color"
-PythonTurtleBlue = "Blue color"
-PythonTurtleBrown = "Brown color"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
+PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
-PythonTurtleGreen = "Green color"
-PythonTurtleGrey = "Grey color"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
PythonTurtleLeft = "Turn left by a degrees"
-PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
-PythonTurtlePink = "Pink color"
PythonTurtlePosition = "Return the current (x,y) location"
-PythonTurtlePurple = "Purple color"
-PythonTurtleRed = "Red color"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
-PythonTurtleWhite = "White color"
-PythonTurtleYellow = "Yellow color"
+PythonTurtleWrite = "Display a text"
PythonUniform = "Floating point number in [a,b]"
-PythonTimeFromImport = "Import time module"
-PythonTimeImport = "Import time module"
+PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
PythonTimeSleep = "Esperar n segundos"
-PythonTimeMonotonic = "Tiempo monótono de retorno"
+PythonMonotonic = "Tiempo monótono de retorno"
+PythonFileOpen = "Opens a file"
+PythonFileSeekable = "Tells if seek can be used on a file"
+PythonFileSeek = "Move file's internal cursor"
+PythonFileTell = "Get file's internal cursor location"
+PythonFileClose = "Closes a file"
+PythonFileClosed = "True if file was closed"
+PythonFileRead = "Read up to size bytes"
+PythonFileWrite = "Write b into file"
+PythonFileReadline = "Reads a line or up to size bytes"
+PythonFileReadlines = "Reads a list of lines"
+PythonFileTruncate = "Resize the file to size"
+PythonFileWritelines = "Writes a list of lines"
+PythonFileName = "Contains file's name"
+PythonFileMode = "Contains file's open mode"
+PythonFileReadable = "Tells if read can be used on a file"
+PythonFileWritable = "Tells if write can be used on a file"
diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n
index 47ca4ab0a..9b436400c 100644
--- a/apps/code/catalog.fr.i18n
+++ b/apps/code/catalog.fr.i18n
@@ -27,6 +27,17 @@ PythonChoice = "Nombre aléatoire dans la liste"
PythonClear = "Vide la liste"
PythonCmathFunction = "Préfixe fonction du module cmath"
PythonColor = "Définit une couleur rvb"
+PythonColorBlack = "Couleur noire"
+PythonColorBlue = "Couleur bleue"
+PythonColorBrown = "Couleur marron"
+PythonColorGreen = "Couleur verte"
+PythonColorGrey = "Couleur grise"
+PythonColorOrange = "Couleur orange"
+PythonColorPink = "Couleur rose"
+PythonColorPurple = "Couleur violette"
+PythonColorRed = "Couleur rouge"
+PythonColorWhite = "Couleur blanche"
+PythonColorYellow = "Couleur jaune"
PythonComplex = "Renvoie a+ib"
PythonCopySign = "Renvoie x avec le signe de y"
PythonCos = "Cosinus"
@@ -139,7 +150,7 @@ PythonRadians = "Conversion de degrés en radians"
PythonRandint = "Entier aléatoire dans [a,b]"
PythonRandom = "Nombre décimal dans [0,1["
PythonRandomFunction = "Préfixe fonction du module random"
-PythonRandrange = "Nombre dans range(start, stop)"
+PythonRandrange = "Nombre dans range(start,stop)"
PythonRangeStartStop = "Liste de start à stop-1"
PythonRangeStop = "Liste de 0 à stop-1"
PythonRect = "Conversion en algébrique"
@@ -162,39 +173,45 @@ PythonText = "Affiche un texte en (x,y)"
PythonTimeFunction = "Préfixe fonction module time"
PythonTrunc = "Troncature entière"
PythonTurtleBackward = "Recule de x pixels"
-PythonTurtleBlack = "Couleur noire"
-PythonTurtleBlue = "Couleur bleue"
-PythonTurtleBrown = "Couleur marron"
PythonTurtleCircle = "Cercle de rayon r pixels"
PythonTurtleColor = "Modifie la couleur du tracé"
+PythonTurtleColorMode = "Met le mode de couleur à 1.0 ou 255"
PythonTurtleForward = "Avance de x pixels"
PythonTurtleFunction = "Préfixe fonction du module turtle"
PythonTurtleGoto = "Va au point de coordonnées (x,y)"
-PythonTurtleGreen = "Couleur verte"
-PythonTurtleGrey = "Couleur grise"
PythonTurtleHeading = "Renvoie l'orientation actuelle"
PythonTurtleHideturtle = "Masque la tortue"
PythonTurtleIsdown = "True si le crayon est abaissé"
PythonTurtleLeft = "Pivote de a degrés vers la gauche"
-PythonTurtleOrange = "Couleur orange"
PythonTurtlePendown = "Abaisse le crayon"
PythonTurtlePensize = "Taille du tracé en pixels"
PythonTurtlePenup = "Relève le crayon"
-PythonTurtlePink = "Couleur rose"
PythonTurtlePosition = "Renvoie la position (x,y)"
-PythonTurtlePurple = "Couleur violette"
-PythonTurtleRed = "Couleur rouge"
PythonTurtleReset = "Réinitialise le dessin"
PythonTurtleRight = "Pivote de a degrés vers la droite"
PythonTurtleSetheading = "Met un cap de a degrés"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Affiche la tortue"
PythonTurtleSpeed = "Vitesse du tracé entre 0 et 10"
-PythonTurtleWhite = "Couleur blanche"
-PythonTurtleYellow = "Couleur jaune"
+PythonTurtleWrite = "Affiche un texte"
PythonUniform = "Nombre décimal dans [a,b]"
-PythonTimeFromImport = "Importation du module temps"
-PythonTimeImport = "Importation du module temps"
+PythonImportTime = "Importation du module temps"
PythonTimePrefix = "Préfixe fonction du module temps"
PythonTimeSleep = "Attendre n secondes"
-PythonTimeMonotonic = "Retourne le temps monotonic"
+PythonMonotonic = "Retourne le temps monotonic"
+PythonFileOpen = "Ouvre un fichier"
+PythonFileSeekable = "Indique si seek peut être utilisé"
+PythonFileSeek = "Déplace le curseur interne"
+PythonFileTell = "Donne la posititon du curseur"
+PythonFileClose = "Ferme un fichier"
+PythonFileClosed = "True si le fichier a été fermé"
+PythonFileRead = "Lis jusqu'à size bytes"
+PythonFileWrite = "Écris b dans le fichier"
+PythonFileReadline = "Lis une ligne ou jusqu'à size bytes"
+PythonFileReadlines = "Lis une liste de lignes"
+PythonFileTruncate = "Redimensionne le fichier"
+PythonFileWritelines = "Écris une liste de lignes"
+PythonFileName = "Nom du fichier"
+PythonFileMode = "Mode d'ouverture du fichier"
+PythonFileReadable = "Indique si read peut être utilisé"
+PythonFileWritable = "Indique si write peut être utilisé"
diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n
index e81f1a746..c344ab2bf 100644
--- a/apps/code/catalog.hu.i18n
+++ b/apps/code/catalog.hu.i18n
@@ -27,6 +27,17 @@ PythonChoice = "Véletlenszerü szám a listában"
PythonClear = "A lista ürítése"
PythonCmathFunction = "cmath modul funkció elötag"
PythonColor = "Rgb szín meghatározása"
+PythonColorBlack = "Fekete szín"
+PythonColorBlue = "Kék szín"
+PythonColorBrown = "Barna szín"
+PythonColorGreen = "Zöld szín"
+PythonColorGrey = "Szürke szín"
+PythonColorOrange = "Narancssárga szín"
+PythonColorPink = "Rózsaszín szín"
+PythonColorPurple = "Lila szín"
+PythonColorRed = "Piros szín"
+PythonColorWhite = "White color"
+PythonColorYellow = "Sárga szín"
PythonComplex = "A + ib visszaadása"
PythonCopySign = "Visszatérés x-val y jelével"
PythonCos = "Koszinusz"
@@ -162,39 +173,45 @@ PythonText = "Display a text at (x,y) coordinates"
PythonTimeFunction = "idömodul funkció elötag"
PythonTrunc = "x egészre csonkítva"
PythonTurtleBackward = "Visszalépés x pixelrel"
-PythonTurtleBlack = "Fekete szín"
-PythonTurtleBlue = "Kék szín"
-PythonTurtleBrown = "Barna szín"
PythonTurtleCircle = "r pixel sugarú kör"
PythonTurtleColor = "Állítsa be az toll színét"
+PythonTurtleColorMode = "Set the color mode to 1.0 or 255"
PythonTurtleForward = "Ugrás x pixelrel"
PythonTurtleFunction = "teknös modul funkció elötag"
PythonTurtleGoto = "Mozgatás (x, y) koordinátákra"
-PythonTurtleGreen = "Zöld szín"
-PythonTurtleGrey = "Szürke szín"
PythonTurtleHeading = "Visszaadja az aktuális címsort"
PythonTurtleHideturtle = "A teknös elrejtése"
PythonTurtleIsdown = "Visszatérés igazhoz, ha az toll lefelé"
PythonTurtleLeft = "Forduljon fokkal balra"
-PythonTurtleOrange = "Narancssárga szín"
PythonTurtlePendown = "Húzza le a tollat"
PythonTurtlePensize = "Állítsa a vonalvastagságot x pixelre"
PythonTurtlePenup = "Húzza fel a tollat"
-PythonTurtlePink = "Rózsaszín szín"
PythonTurtlePosition = "Az aktuális (x, y) hely visszaadása"
-PythonTurtlePurple = "Lila szín"
-PythonTurtleRed = "Piros szín"
PythonTurtleReset = "A rajz visszaállítása"
PythonTurtleRight = "Forduljon fokkal jobbra"
PythonTurtleSetheading = "Állítsa be a tájolást fokokra"
PythonTurtleSetposition = "A helymeghatározás"
PythonTurtleShowturtle = "Mutasd a teknösöt"
PythonTurtleSpeed = "Rajzolási sebesség 0 és 10 között"
-PythonTurtleWhite = "Fehér szín"
-PythonTurtleYellow = "Sárga szín"
+PythonTurtleWrite = "Display a text"
PythonUniform = "Lebegöpontos szám [a, b] -ben"
-PythonTimeFromImport = "Idömodul importálása"
-PythonTimeImport = "Idömodul importálása"
+PythonImportTime = "Idömodul importálása"
PythonTimePrefix = "idömodul funkció elötag"
PythonTimeSleep = "Várj n másodpercet"
-PythonTimeMonotonic = "Vissza a monoton idö"
+PythonMonotonic = "Vissza a monoton idö"
+PythonFileOpen = "Fájl megnyitása"
+PythonFileSeekable = "A fájl kereshető?"
+PythonFileSeek = "A fájl kurzorának áthelyezése"
+PythonFileTell = "A fájl kurzorának helye"
+PythonFileClose = "Bezár egy fájlt"
+PythonFileClosed = "Igaz, ha a fájl bezárt"
+PythonFileRead = "Olvasson méretbájtig"
+PythonFileWrite = "B beírása fájlba"
+PythonFileReadline = "Olvas egy sort"
+PythonFileReadlines = "Olvassa a sorok listáját"
+PythonFileTruncate = "A fájl átméretezése méretre"
+PythonFileWritelines = "Sorok listáját írja"
+PythonFileName = "a fájl neve"
+PythonFileMode = "a fájl nyitott módja"
+PythonFileReadable = "A fájl olvasható?"
+PythonFileWritable = "A fájl írható?"
diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n
new file mode 100644
index 000000000..eaecb080c
--- /dev/null
+++ b/apps/code/catalog.it.i18n
@@ -0,0 +1,217 @@
+PythonPound = "Commento"
+PythonPercent = "Modulo"
+Python1J = "Unità immaginaria"
+PythonLF = "Nuova riga"
+PythonTab = "Tabulazione"
+PythonAmpersand = "Congiunzione"
+PythonSymbolExp = "Disgiunzione esclusiva"
+PythonVerticalBar = "Disgiunzione"
+PythonImag = "Parte immaginaria di z"
+PythonReal = "Parte reale di z"
+PythonSingleQuote = "Apostrofo"
+PythonAbs = "Valore assoluto/Modulo"
+PythonAcos = "Coseno d'arco"
+PythonAcosh = "Coseno iperbolico inverso"
+PythonAppend = "Inserisce x alla fine della lista"
+PythonArrow = "Freccia da (x,y) a (x+dx,y+dy)"
+PythonAsin = "Arco sinusoidale"
+PythonAsinh = "Arco sinusoidale iperbolico"
+PythonAtan = "Arco tangente"
+PythonAtan2 = "Calcolo di atan(y/x)"
+PythonAtanh = "Arco tangente iperbolico"
+PythonAxis = "Imposta assi (xmin,xmax,ymin,ymax)"
+PythonBar = "Grafico a barre con x valori"
+PythonBin = "Converte un intero in binario"
+PythonCeil = "Parte intera superiore"
+PythonChoice = "Numero aleatorio nella lista"
+PythonClear = "Svuota la lista"
+PythonCmathFunction = "Funz. prefissata modulo cmath"
+PythonColor = "Definisci un colore rvb"
+PythonColorBlack = "Colore nero"
+PythonColorBlue = "Colore blu"
+PythonColorBrown = "Colore marrone"
+PythonColorGreen = "Colore verde"
+PythonColorGrey = "Colore grigio"
+PythonColorOrange = "Colore arancione"
+PythonColorPink = "Colore rosa"
+PythonColorPurple = "Colore viola"
+PythonColorRed = "Colore rosso"
+PythonColorWhite = "Colore bianco"
+PythonColorYellow = "Colore giallo"
+PythonComplex = "Restituisce a+ib"
+PythonCopySign = "Restituisce x con segno di y"
+PythonCos = "Coseno"
+PythonCosh = "Coseno iperbolico"
+PythonCount = "Conta le ricorrenze di x"
+PythonDegrees = "Conversione di radianti in gradi"
+PythonDivMod = "Quoziente e resto"
+PythonDrawString = "Visualizza il testo dal pixel x,y"
+PythonErf = "Funzione d'errore"
+PythonErfc = "Funzione d'errore complementare"
+PythonEval = "Valuta l'espressione nell'argomento "
+PythonExp = "Funzione esponenziale"
+PythonExpm1 = "Calcola exp(x)-1"
+PythonFabs = "Valore assoluto"
+PythonFillRect = "Riempie un rettangolo"
+PythonFloat = "Conversione in flottanti"
+PythonFloor = "Parte intera"
+PythonFmod = "a modulo b"
+PythonFrExp = "Mantissa ed esponente di x : (m,e)"
+PythonGamma = "Funzione gamma"
+PythonGetPixel = "Restituisce colore del pixel(x,y)"
+PythonGetrandbits = "Numero aleatorio con k bit"
+PythonGrid = "Attiva la visibilità della griglia"
+PythonHex = "Conversione intero in esadecimale"
+PythonHist = "Disegna l'istogramma di x"
+PythonImportCmath = "Importa modulo cmath"
+PythonImportIon = "Importa modulo ion"
+PythonImportKandinsky = "Importa modulo kandinsky"
+PythonImportRandom = "Importa modulo random"
+PythonImportMath = "Importa modulo math"
+PythonImportMatplotlibPyplot = "Importa modulo matplotlib.pyplot"
+PythonImportTurtle = "Importa del modulo turtle"
+PythonImportTime = "Importa del modulo time"
+PythonIndex = "Indice prima occorrenza di x"
+PythonInput = "Inserire un valore"
+PythonInsert = "Inserire x in posizione i-esima"
+PythonInt = "Conversione in intero"
+PythonIonFunction = "Prefisso di funzione modulo ion"
+PythonIsFinite = "Testa se x è finito"
+PythonIsInfinite = "Testa se x est infinito"
+PythonIsKeyDown = "Restituisce True premendo tasto k"
+PythonIsNaN = "Testa se x è NaN"
+PythonKandinskyFunction = "Prefisso funzione modulo kandinsky"
+PythonKeyLeft = "Tasto FRECCIA SINISTRA"
+PythonKeyUp = "Tasto FRECCIA ALTO"
+PythonKeyDown = "Tasto FRECCIA BASSO"
+PythonKeyRight = "Tasto FRECCIA DESTRA"
+PythonKeyOk = "Tasto OK"
+PythonKeyBack = "Tasto INDIETRO"
+PythonKeyHome = "Tasto CASA"
+PythonKeyOnOff = "Tasto ON/OFF"
+PythonKeyShift = "Tasto SHIFT"
+PythonKeyAlpha = "Tasto ALPHA"
+PythonKeyXnt = "Tasto X,N,T"
+PythonKeyVar = "Tasto VAR"
+PythonKeyToolbox = "Tasto TOOLBOX"
+PythonKeyBackspace = "Tasto CANCELLA"
+PythonKeyExp = "Tasto ESPONENZIALE"
+PythonKeyLn = "Tasto LOGARITMO NEPERIANO"
+PythonKeyLog = "Tasto LOGARITMO DECIMALE"
+PythonKeyImaginary = "Tasto I IMMAGINE"
+PythonKeyComma = "Tasto VIRGOLA"
+PythonKeyPower = "Tasto POTENZA"
+PythonKeySine = "Tasto SENO"
+PythonKeyCosine = "Tasto COSENO"
+PythonKeyTangent = "Tasto TANGENTE"
+PythonKeyPi = "Tasto PI"
+PythonKeySqrt = "Tasto RADICE QUADRATA"
+PythonKeySquare = "Tasto QUADRATO"
+PythonKeySeven = "Tasto 7"
+PythonKeyEight = "Tasto 8"
+PythonKeyNine = "Tasto 9"
+PythonKeyLeftParenthesis = "Tasto PARENTESI SINISTRA"
+PythonKeyRightParenthesis = "Tasto PARENTESI DESTRA"
+PythonKeyFour = "Tasto 4"
+PythonKeyFive = "Tasto 5"
+PythonKeySix = "Tasto 6"
+PythonKeyMultiplication = "Tasto MOLTIPLICAZIONE"
+PythonKeyDivision = "Tasto DIVISIONE"
+PythonKeyOne = "Tasto 1"
+PythonKeyTwo = "Tasto 2"
+PythonKeyThree = "Tasto 3"
+PythonKeyPlus = "Tasto PIÙ"
+PythonKeyMinus = "Tasto MENO"
+PythonKeyZero = "Tasto 0"
+PythonKeyDot = "Tasto PUNTO"
+PythonKeyEe = "Tasto 10 POTENZA X"
+PythonKeyAns = "Tasto ANS"
+PythonKeyExe = "Tasto EXE"
+PythonLdexp = "Inversa di frexp : x*(2**i)"
+PythonLength = "Longhezza di un oggetto"
+PythonLgamma = "Logaritmo della funzione gamma"
+PythonLog = "Logaritmo di base a"
+PythonLog10 = "Logaritmo decimale"
+PythonLog2 = "Logaritmo di base 2"
+PythonMathFunction = "Prefisso funzione del modulo math"
+PythonMatplotlibPyplotFunction = "Prefisso modulo matplotlib.pyplot"
+PythonMax = "Massimo"
+PythonMin = "Minimo"
+PythonModf = "Parti frazionarie e intere"
+PythonMonotonic = "Restituisce il valore dell'orologio"
+PythonOct = "Conversione in ottale"
+PythonPhase = "Argomento di z"
+PythonPlot = "Disegna y in f. di x come linee"
+PythonPolar = "Conversione in polare"
+PythonPop = "Cancella l'ultimo elemento"
+PythonPower = "x alla potenza y"
+PythonPrint = "Visualizza l'oggetto"
+PythonRadians = "Conversione da gradi a radianti"
+PythonRandint = "Intero aleatorio in [a,b]"
+PythonRandom = "Numero aleatorio in [0,1["
+PythonRandomFunction = "Prefisso funzione modulo casuale"
+PythonRandrange = "Numero dentro il range(start, stop)"
+PythonRangeStartStop = "Lista da start a stop-1"
+PythonRangeStop = "Lista da 0 a stop-1"
+PythonRect = "Converte in coordinate algebriche"
+PythonRemove = "Cancella la prima x dalla lista"
+PythonReverse = "Inverte gli elementi della lista"
+PythonRound = "Arrotondato a n cifre decimali"
+PythonScatter = "Diagramma dispersione y in f. di x"
+PythonSeed = "Inizializza il generatore random"
+PythonSetPixel = "Colora il pixel (x,y)"
+PythonShow = "Mostra la figura"
+PythonSin = "Seno"
+PythonSinh = "Seno iperbolico"
+PythonSleep = "Sospende l'esecuzione t secondi"
+PythonSort = "Ordina l'elenco"
+PythonSqrt = "Radice quadrata"
+PythonSum = "Somma degli elementi della lista"
+PythonTan = "Tangente"
+PythonTanh = "Tangente iperbolica"
+PythonText = "Mostra un testo in (x,y)"
+PythonTimeFunction = "Prefisso funzione modulo time"
+PythonTrunc = "Troncamento intero"
+PythonTurtleBackward = "Indietreggia di x pixels"
+PythonTurtleCircle = "Cerchio di raggio r pixel"
+PythonTurtleColor = "Modifica il colore del tratto"
+PythonTurtleColorMode = "Imposta modalità colore a 1.0 o 255"
+PythonTurtleForward = "Avanza di x pixel"
+PythonTurtleFunction = "Prefisso funzione modello turtle"
+PythonTurtleGoto = "Spostati alle coordinate (x,y)"
+PythonTurtleHeading = "Restituisce l'orientamento attuale"
+PythonTurtleHideturtle = "Nascondi la tartaruga"
+PythonTurtleIsdown = "True se la penna è abbassata"
+PythonTurtleLeft = "Ruota di a gradi a sinistra"
+PythonTurtlePendown = "Abbassa la penna"
+PythonTurtlePensize = "Dimensione del tratto in pixel"
+PythonTurtlePenup = "Solleva la penna"
+PythonTurtlePosition = "Fornisce posizione corrente (x,y)"
+PythonTurtleReset = "Azzera il disegno"
+PythonTurtleRight = "Ruota di a gradi a destra"
+PythonTurtleSetheading = "Imposta l'orientamento per a gradi"
+PythonTurtleSetposition = "Posiziona la tartaruga"
+PythonTurtleShowturtle = "Mostra la tartaruga"
+PythonTurtleSpeed = "Velocità di disegno (x tra 0 e 10)"
+PythonTurtleWrite = "Mostra un testo"
+PythonUniform = "Numero decimale tra [a,b]"
+PythonImportTime = "Import time module"
+PythonTimePrefix = "time module function prefix"
+PythonTimeSleep = "Wait for n second"
+PythonMonotonic = "Return monotonic time"
+PythonFileOpen = "Opens a file"
+PythonFileSeekable = "Tells if seek can be used on a file"
+PythonFileSeek = "Move file's cursor"
+PythonFileTell = "Get file's cursor location"
+PythonFileClose = "Closes a file"
+PythonFileClosed = "True if file was closed"
+PythonFileRead = "Read up to size bytes"
+PythonFileWrite = "Write b into file"
+PythonFileReadline = "Reads a line or up to size bytes"
+PythonFileReadlines = "Reads a list of lines"
+PythonFileTruncate = "Resize the file to size"
+PythonFileWritelines = "Writes a list of lines"
+PythonFileName = "Contains file's name"
+PythonFileMode = "Contains file's open mode"
+PythonFileReadable = "Tells if read can be used on a file"
+PythonFileWritable = "Tells if write can be used on a file"
diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n
new file mode 100644
index 000000000..bbe2b04c5
--- /dev/null
+++ b/apps/code/catalog.nl.i18n
@@ -0,0 +1,217 @@
+PythonPound = "Opmerkingen"
+PythonPercent = "Modulo"
+Python1J = "Imaginaire i"
+PythonLF = "Nieuwe regel"
+PythonTab = "Tabulatie"
+PythonAmpersand = "Bitsgewijze en"
+PythonSymbolExp = "Bitsgewijze exclusieve of"
+PythonVerticalBar = "Bitsgewijze of"
+PythonImag = "Imaginair deel van z"
+PythonReal = "Reëel deel van z"
+PythonSingleQuote = "Enkele aanhalingstekens"
+PythonAbs = "Absolute waarde"
+PythonAcos = "Arccosinus"
+PythonAcosh = "Arccosinus hyperbolicus"
+PythonAppend = "Voeg x toe aan het eind van je lijst"
+PythonArrow = "Arrow from (x,y) to (x+dx,y+dy)"
+PythonAsin = "Arcsinus"
+PythonAsinh = "Arcsinus hyperbolicus"
+PythonAtan = "Arctangens"
+PythonAtan2 = "Geeft atan(y/x)"
+PythonAtanh = "Arctangens hyperbolicus"
+PythonAxis = "Set the axes to (xmin,xmax,ymin,ymax)"
+PythonBar = "Draw a bar plot with x values"
+PythonBin = "Zet integer om in een binair getal"
+PythonCeil = "Plafond"
+PythonChoice = "Geeft willek. getal van de lijst"
+PythonClear = "Lijst leegmaken"
+PythonCmathFunction = "cmath module voorvoegsel"
+PythonColor = "Definieer een rgb kleur"
+PythonColorBlack = "Zwarte kleur"
+PythonColorBlue = "Blauwe kleur"
+PythonColorBrown = "Bruine kleur"
+PythonColorGreen = "Groene kleur"
+PythonColorGrey = "Grijze kleur"
+PythonColorOrange = "Oranje kleur"
+PythonColorPink = "Roze kleur"
+PythonColorPurple = "Paarse kleur"
+PythonColorRed = "Rode kleur"
+PythonColorWhite = "Witte kleur"
+PythonColorYellow = "Gele kleur"
+PythonComplex = "Geeft a+ib"
+PythonCopySign = "Geeft x met het teken van y"
+PythonCos = "Cosinus"
+PythonCosh = "Cosinus hyperbolicus"
+PythonCount = "Tel voorkomen van x"
+PythonDegrees = "Zet x om van radialen naar graden"
+PythonDivMod = "Quotiënt en rest"
+PythonDrawString = "Geef een tekst weer van pixel (x,y)"
+PythonErf = "Error functie"
+PythonErfc = "Complementaire error functie"
+PythonEval = "Geef de geëvalueerde uitdrukking"
+PythonExp = "Exponentiële functie"
+PythonExpm1 = "Bereken exp(x)-1"
+PythonFabs = "Absolute waarde"
+PythonFillRect = "Vul een rechthoek bij pixel (x,y)"
+PythonFloat = "Zet x om in een float"
+PythonFloor = "Vloer"
+PythonFmod = "a modulo b"
+PythonFrExp = "Mantisse en exponent van x: (m,e)"
+PythonGamma = "Gammafunctie"
+PythonGetPixel = "Geef pixel (x,y) kleur (rgb)"
+PythonGetrandbits = "Integer met k willekeurige bits"
+PythonGrid = "Toggle the visibility of the grid"
+PythonHex = "Zet integer om in hexadecimaal"
+PythonHist = "Draw the histogram of x"
+PythonImportCmath = "Importeer cmath module"
+PythonImportIon = "Importeer ion module"
+PythonImportKandinsky = "Importeer kandinsky module"
+PythonImportRandom = "Importeer random module"
+PythonImportMath = "Importeer math module"
+PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module"
+PythonImportTime = "Importeer time module"
+PythonImportTurtle = "Importeer turtle module"
+PythonIndex = "Index van de eerste x aanwezigheden"
+PythonInput = "Wijs een waarde toe"
+PythonInsert = "Voeg x toe aan index i in de lijst"
+PythonInt = "Zet x om in een integer"
+PythonIonFunction = "ion module voorvoegsel"
+PythonIsFinite = "Controleer of x eindig is"
+PythonIsInfinite = "Controleer of x oneindig is"
+PythonIsKeyDown = "Geef True als k toets omlaag is"
+PythonIsNaN = "Controleer of x geen nummer is"
+PythonKandinskyFunction = "kandinsky module voorvoegsel"
+PythonKeyLeft = "PIJL NAAR LINKS toets"
+PythonKeyUp = "PIJL OMHOOG toets"
+PythonKeyDown = "PIJL OMLAAG toets"
+PythonKeyRight = "PIJL NAAR RECHTS toets"
+PythonKeyOk = "OK toets"
+PythonKeyBack = "TERUG toets"
+PythonKeyHome = "HOME toets"
+PythonKeyOnOff = "AAN/UIT toets"
+PythonKeyShift = "SHIFT toets"
+PythonKeyAlpha = "ALPHA toets"
+PythonKeyXnt = "X,N,T toets"
+PythonKeyVar = "VAR toets"
+PythonKeyToolbox = "TOOLBOX toets"
+PythonKeyBackspace = "BACKSPACE toets"
+PythonKeyExp = "EXPONENTIEEL toets"
+PythonKeyLn = "NATUURLIJKE LOGARITME toets"
+PythonKeyLog = "BRIGGSE LOGARITME toets"
+PythonKeyImaginary = "IMAGINAIRE I toets"
+PythonKeyComma = "KOMMA toets"
+PythonKeyPower = "MACHT toets"
+PythonKeySine = "SINUS toets"
+PythonKeyCosine = "COSINUS toets"
+PythonKeyTangent = "TANGENS toets"
+PythonKeyPi = "PI toets"
+PythonKeySqrt = "VIERKANTSWORTEL toets"
+PythonKeySquare = "KWADRAAT toets"
+PythonKeySeven = "7 toets"
+PythonKeyEight = "8 toets"
+PythonKeyNine = "9 toets"
+PythonKeyLeftParenthesis = "HAAKJE OPENEN toets"
+PythonKeyRightParenthesis = "HAAKJE SLUITEN toets"
+PythonKeyFour = "4 toets"
+PythonKeyFive = "5 toets"
+PythonKeySix = "6 toets"
+PythonKeyMultiplication = "VERMENIGVULDIGEN toets"
+PythonKeyDivision = "DELEN toets"
+PythonKeyOne = "1 toets"
+PythonKeyTwo = "2 toets"
+PythonKeyThree = "3 toets"
+PythonKeyPlus = "PLUS toets"
+PythonKeyMinus = "MIN toets"
+PythonKeyZero = "0 toets"
+PythonKeyDot = "PUNT toets"
+PythonKeyEe = "10 TOT DE MACHT X toets"
+PythonKeyAns = "ANS toets"
+PythonKeyExe = "EXE toets"
+PythonLdexp = "Geeft x*(2**i), inversie van frexp"
+PythonLength = "Lengte van een object"
+PythonLgamma = "Log-gammafunctie"
+PythonLog = "Logaritme met grondgetal a"
+PythonLog10 = "Logaritme met grondgetal 10"
+PythonLog2 = "Logaritme met grondgetal 2"
+PythonMathFunction = "math module voorvoegsel"
+PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix"
+PythonMax = "Maximum"
+PythonMin = "Minimum"
+PythonModf = "Fractionele en gehele delen van x"
+PythonMonotonic = "Waarde van een monotone klok"
+PythonOct = "Integer omzetten naar octaal"
+PythonPhase = "Fase van z in radialen"
+PythonPlot = "Plot y versus x as lines"
+PythonPolar = "z in poolcoördinaten"
+PythonPop = "Verwijder en breng het laatste item terug"
+PythonPower = "x tot de macht y"
+PythonPrint = "Print object"
+PythonRadians = "Zet x om van graden naar radialen"
+PythonRandint = "Geeft willek. integer in [a,b]"
+PythonRandom = "Een willekeurig getal in [0,1["
+PythonRandomFunction = "random module voorvoegsel"
+PythonRandrange = "Willek. getal in range(start, stop)"
+PythonRangeStartStop = "Lijst van start tot stop-1"
+PythonRangeStop = "Lijst van 0 tot stop-1"
+PythonRect = "z in cartesiaanse coördinaten"
+PythonRemove = "Verwijder het eerste voorkomen van x"
+PythonReverse = "Keer de elementen van de lijst om"
+PythonRound = "Rond af op n cijfers"
+PythonScatter = "Draw a scatter plot of y versus x"
+PythonSeed = "Start willek. getallengenerator"
+PythonSetPixel = "Kleur pixel (x,y)"
+PythonShow = "Display the figure"
+PythonSin= "Sinus"
+PythonSinh = "Sinus hyperbolicus"
+PythonSleep = "Stel executie voor t seconden uit"
+PythonSort = "Sorteer de lijst"
+PythonSqrt = "Vierkantswortel"
+PythonSum = "Sommeer de items van een lijst"
+PythonTan = "Tangens"
+PythonTanh = "Tangens hyperbolicus"
+PythonText = "Display a text at (x,y) coordinates"
+PythonTimeFunction = "time module voorvoegsel"
+PythonTrunc = "x afgeknot tot een integer"
+PythonTurtleBackward = "Ga achterwaarts met x pixels"
+PythonTurtleCircle = "Cirkel van straal r pixels"
+PythonTurtleColor = "Stel de kleur van de pen in"
+PythonTurtleColorMode = "Stel de kleurmodus in op 1.0 of 255"
+PythonTurtleForward = "Ga voorwaarts met x pixels"
+PythonTurtleFunction = "turtle module voorvoegsel"
+PythonTurtleGoto = "Verplaats naar (x,y) coordinaten"
+PythonTurtleHeading = "Ga terug naar de huidige koers"
+PythonTurtleHideturtle = "Verberg de schildpad"
+PythonTurtleIsdown = "Geeft True als pen naar beneden is"
+PythonTurtleLeft = "Ga linksaf met a graden"
+PythonTurtlePendown = "Zet de pen naar beneden"
+PythonTurtlePensize = "Stel de lijndikte in op x pixels"
+PythonTurtlePenup = "Zet de pen omhoog"
+PythonTurtlePosition = "Zet huidige (x,y) locatie terug"
+PythonTurtleReset = "Reset de tekening"
+PythonTurtleRight = "Ga rechtsaf met a graden"
+PythonTurtleSetheading = "Zet de oriëntatie op a graden"
+PythonTurtleSetposition = "Plaats de schildpad"
+PythonTurtleShowturtle = "Laat de schildpad zien"
+PythonTurtleSpeed = "Tekensnelheid tussen 0 and 10"
+PythonTurtleWrite = "Display a text"
+PythonUniform = "Zwevendekommagetal in [a,b]"
+PythonImportTime = "Import time module"
+PythonTimePrefix = "time module function prefix"
+PythonTimeSleep = "Wait for n second"
+PythonMonotonic = "Return monotonic time"
+PythonFileOpen = "Opens a file"
+PythonFileSeekable = "Tells if seek can be used on a file"
+PythonFileSeek = "Move file's cursor"
+PythonFileTell = "Get file's cursor location"
+PythonFileClose = "Closes a file"
+PythonFileClosed = "True if file was closed"
+PythonFileRead = "Read up to size bytes"
+PythonFileWrite = "Write b into file"
+PythonFileReadline = "Reads a line or up to size bytes"
+PythonFileReadlines = "Reads a list of lines"
+PythonFileTruncate = "Resize the file to size"
+PythonFileWritelines = "Writes a list of lines"
+PythonFileName = "Contains file's name"
+PythonFileMode = "Contains file's open mode"
+PythonFileReadable = "Tells if read can be used on a file"
+PythonFileWritable = "Tells if write can be used on a file"
diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n
index ea8e665f9..f0b401b81 100644
--- a/apps/code/catalog.pt.i18n
+++ b/apps/code/catalog.pt.i18n
@@ -1,200 +1,217 @@
-PythonPound = "Comment"
-PythonPercent = "Modulo"
-Python1J = "Imaginary i"
-PythonLF = "Line feed"
-PythonTab = "Tabulation"
-PythonAmpersand = "Bitwise and"
-PythonSymbolExp = "Bitwise exclusive or"
-PythonVerticalBar = "Bitwise or"
-PythonSingleQuote = "Single quote"
-PythonImag = "Imaginary part of z"
-PythonReal = "Real part of z"
-PythonAbs = "Absolute value/Magnitude"
-PythonAcos = "Arc cosine"
-PythonAcosh = "Arc hyperbolic cosine"
-PythonAppend = "Add x to the end of the list"
-PythonArrow = "Arrow from (x,y) to (x+dx,y+dy)"
-PythonAsin = "Arc sine"
-PythonAsinh = "Arc hyperbolic sine"
-PythonAtan = "Arc tangent"
-PythonAtan2 = "Return atan(y/x)"
-PythonAtanh = "Arc hyperbolic tangent"
-PythonAxis = "Set axes to (xmin,xmax,ymin,ymax)"
-PythonBar = "Draw a bar plot with x values"
-PythonBin = "Convert integer to binary"
-PythonCeil = "Ceiling"
-PythonChoice = "Random number in the list"
-PythonClear = "Empty the list"
-PythonCmathFunction = "cmath module function prefix"
-PythonColor = "Define a rgb color"
-PythonComplex = "Return a+ib"
-PythonCopySign = "Return x with the sign of y"
-PythonCos = "Cosine"
-PythonCosh = "Hyperbolic cosine"
-PythonCount = "Count the occurrences of x"
-PythonDegrees = "Convert x from radians to degrees"
-PythonDivMod = "Quotient and remainder"
-PythonDrawString = "Display a text from pixel (x,y)"
-PythonErf = "Error function"
-PythonErfc = "Complementary error function"
-PythonEval = "Return the evaluated expression"
-PythonExp = "Exponential function"
-PythonExpm1 = "Compute exp(x)-1"
-PythonFabs = "Absolute value"
-PythonFillRect = "Fill a rectangle at pixel (x,y)"
-PythonFloat = "Convert x to a float"
-PythonFloor = "Floor"
-PythonFmod = "a modulo b"
-PythonFrExp = "Mantissa and exponent of x"
-PythonGamma = "Gamma function"
-PythonGetPixel = "Return pixel (x,y) color"
-PythonGetrandbits = "Integer with k random bits"
-PythonGrid = "Toggle the visibility of the grid"
-PythonHex = "Convert integer to hexadecimal"
-PythonHist = "Draw the histogram of x"
-PythonImportCmath = "Import cmath module"
-PythonImportIon = "Import ion module"
-PythonImportKandinsky = "Import kandinsky module"
-PythonImportRandom = "Import random module"
-PythonImportMath = "Import math module"
-PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module"
-PythonImportTime = "Import time module"
-PythonImportTurtle = "Import turtle module"
-PythonIndex = "Index of the first x occurrence"
-PythonInput = "Prompt a value"
-PythonInsert = "Insert x at index i in the list"
-PythonInt = "Convert x to an integer"
-PythonIonFunction = "ion module function prefix"
-PythonIsFinite = "Check if x is finite"
-PythonIsInfinite = "Check if x is infinity"
-PythonIsKeyDown = "Return True if the k key is down"
-PythonIsNaN = "Check if x is a NaN"
-PythonKandinskyFunction = "kandinsky module function prefix"
-PythonKeyLeft = "LEFT ARROW key"
-PythonKeyUp = "UP ARROW key"
-PythonKeyDown = "DOWN ARROW key"
-PythonKeyRight = "RIGHT ARROW key"
-PythonKeyOk = "OK key"
-PythonKeyBack = "BACK key"
-PythonKeyHome = "HOME key"
-PythonKeyOnOff = "ON/OFF key"
-PythonKeyShift = "SHIFT key"
-PythonKeyAlpha = "ALPHA key"
-PythonKeyXnt = "X,N,T key"
-PythonKeyVar = "VAR key"
-PythonKeyToolbox = "TOOLBOX key"
-PythonKeyBackspace = "BACKSPACE key"
-PythonKeyExp = "EXPONENTIAL key"
-PythonKeyLn = "NATURAL LOGARITHM key"
-PythonKeyLog = "DECIMAL LOGARITHM key"
-PythonKeyImaginary = "IMAGINARY I key"
-PythonKeyComma = "COMMA key"
-PythonKeyPower = "POWER key"
-PythonKeySine = "SINE key"
-PythonKeyCosine = "COSINE key"
-PythonKeyTangent = "TANGENT key"
-PythonKeyPi = "PI key"
-PythonKeySqrt = "SQUARE ROOT key"
-PythonKeySquare = "SQUARE key"
-PythonKeySeven = "7 key"
-PythonKeyEight = "8 key"
-PythonKeyNine = "9 key"
-PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
-PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
-PythonKeyFour = "4 key"
-PythonKeyFive = "5 key"
-PythonKeySix = "6 key"
-PythonKeyMultiplication = "MULTIPLICATION key"
-PythonKeyDivision = "DIVISION key"
-PythonKeyOne = "1 key"
-PythonKeyTwo = "2 key"
-PythonKeyThree = "3 key"
-PythonKeyPlus = "PLUS key"
-PythonKeyMinus = "MINUS key"
-PythonKeyZero = "0 key"
-PythonKeyDot = "DOT key"
-PythonKeyEe = "10 POWER X key"
-PythonKeyAns = "ANS key"
-PythonKeyExe = "EXE key"
-PythonLdexp = "Return x*(2**i), inverse of frexp"
-PythonLength = "Length of an object"
-PythonLgamma = "Log-gamma function"
-PythonLog = "Logarithm to base a"
-PythonLog10 = "Logarithm to base 10"
-PythonLog2 = "Logarithm to base 2"
-PythonMathFunction = "math module function prefix"
-PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix"
-PythonMax = "Maximum"
-PythonMin = "Minimum"
-PythonModf = "Fractional and integer parts of x"
-PythonMonotonic = "Value of a monotonic clock"
-PythonOct = "Convert integer to octal"
-PythonPhase = "Phase of z"
-PythonPlot = "Plot y versus x as lines"
-PythonPolar = "z in polar coordinates"
-PythonPop = "Remove and return the last item"
-PythonPower = "x raised to the power y"
-PythonPrint = "Print object"
-PythonRadians = "Convert x from degrees to radians"
-PythonRandint = "Random integer in [a,b]"
-PythonRandom = "Floating point number in [0,1["
-PythonRandomFunction = "random module function prefix"
-PythonRandrange = "Random number in range(start, stop)"
-PythonRangeStartStop = "List from start to stop-1"
-PythonRangeStop = "List from 0 to stop-1"
-PythonRect = "z in cartesian coordinates"
-PythonRemove = "Remove the first occurrence of x"
-PythonReverse = "Reverse the elements of the list"
-PythonRound = "Round to n digits"
-PythonScatter = "Draw a scatter plot of y versus x"
-PythonSeed = "Initialize random number generator"
-PythonSetPixel = "Color pixel (x,y)"
-PythonShow = "Display the figure"
-PythonSin = "Sine"
-PythonSinh = "Hyperbolic sine"
-PythonSleep = "Suspend the execution for t seconds"
-PythonSort = "Sort the list"
-PythonSqrt = "Square root"
-PythonSum = "Sum the items of a list"
-PythonTan = "Tangent"
-PythonTanh = "Hyperbolic tangent"
-PythonText = "Display a text at (x,y) coordinates"
-PythonTimeFunction = "time module function prefix"
-PythonTrunc = "x truncated to an integer"
-PythonTurtleBackward = "Move backward by x pixels"
-PythonTurtleBlack = "Black color"
-PythonTurtleBlue = "Blue color"
-PythonTurtleBrown = "Brown color"
-PythonTurtleCircle = "Circle of radius r pixels"
-PythonTurtleColor = "Set the pen color"
-PythonTurtleForward = "Move forward by x pixels"
-PythonTurtleFunction = "turtle module function prefix"
-PythonTurtleGoto = "Move to (x,y) coordinates"
-PythonTurtleGreen = "Green color"
-PythonTurtleGrey = "Grey color"
-PythonTurtleHeading = "Return the current heading"
-PythonTurtleHideturtle = "Hide the turtle"
-PythonTurtleIsdown = "Return True if the pen is down"
-PythonTurtleLeft = "Turn left by a degrees"
-PythonTurtleOrange = "Orange color"
-PythonTurtlePendown = "Pull the pen down"
-PythonTurtlePensize = "Set the line thickness to x pixels"
-PythonTurtlePenup = "Pull the pen up"
-PythonTurtlePink = "Pink color"
-PythonTurtlePosition = "Return the current (x,y) location"
-PythonTurtlePurple = "Purple color"
-PythonTurtleRed = "Red color"
-PythonTurtleReset = "Reset the drawing"
-PythonTurtleRight = "Turn right by a degrees"
-PythonTurtleSetheading = "Set the orientation to a degrees"
+PythonPound = "Comentário"
+PythonPercent = "Módulo"
+Python1J = "i Complexo"
+PythonLF = "Nova linha"
+PythonTab = "Tabulação"
+PythonAmpersand = "Operador binário and"
+PythonSymbolExp = "Operador binário exclusivo or"
+PythonVerticalBar = "Operador binário or"
+PythonSingleQuote = "Apóstrofo"
+PythonImag = "Parte imaginária de z"
+PythonReal = "Parte real de z"
+PythonAbs = "Valor absoluto/módulo"
+PythonAcos = "Arco cosseno"
+PythonAcosh = "Arco cosseno hiperbólico"
+PythonAppend = "Adicionar x no fim da lista"
+PythonArrow = "Seta de (x,y) para (x+dx,y+dy)"
+PythonAsin = "Arco seno"
+PythonAsinh = "Arco seno hiperbólico"
+PythonAtan = "Arco tangente"
+PythonAtan2 = "Cálculo de atan(y/x)"
+PythonAtanh = "Arco tangente hiperbólica"
+PythonAxis = "Definir eixos (xmin,xmax,ymin,ymax)"
+PythonBar = "Gráfico de barras com valores de x"
+PythonBin = "Converter número inteiro em binário"
+PythonCeil = "Teto"
+PythonChoice = "Número aleatório na lista"
+PythonClear = "Esvaziar a lista"
+PythonCmathFunction = "Prefixo da função do módulo cmath"
+PythonColor = "Define uma cor rgb"
+PythonColorBlack = "Cor preta"
+PythonColorBlue = "Cor azul"
+PythonColorBrown = "Cor castanha"
+PythonColorGreen = "Cor verde"
+PythonColorGrey = "Cor cinzenta"
+PythonColorOrange = "Cor laranja"
+PythonColorPink = "Cor rosa"
+PythonColorPurple = "Cor roxa"
+PythonColorRed = "Cor vermelha"
+PythonColorWhite = "Cor branca"
+PythonColorYellow = "Cor amarela"
+PythonComplex = "Devolve a+ib"
+PythonCopySign = "Devolve x com o sinal de y"
+PythonCos = "Cosseno"
+PythonCosh = "Cosseno hiperbólico"
+PythonCount = "Contar as ocorrências de x"
+PythonDegrees = "Converter x de radianos para graus"
+PythonDivMod = "Quociente e resto"
+PythonDrawString = "Mostrar o texto do pixel (x,y)"
+PythonErf = "Função erro"
+PythonErfc = "Função erro complementar"
+PythonEval = "Devolve a expressão avaliada"
+PythonExp = "Função exponencial"
+PythonExpm1 = "Calcular exp(x)-1"
+PythonFabs = "Valor absoluto"
+PythonFillRect = "Preencher um retângulo em (x,y)"
+PythonFloat = "Converter x num flutuante"
+PythonFloor = "Parte inteira"
+PythonFmod = "a módulo b"
+PythonFrExp = "Coeficiente e expoente de x: (m, e)"
+PythonGamma = "Função gama"
+PythonGetPixel = "Devolve a cor do pixel (x,y)"
+PythonGetrandbits = "Número inteiro aleatório com k bits"
+PythonGrid = "Alterar visibilidade da grelha"
+PythonHex = "Converter inteiro em hexadecimal"
+PythonHist = "Desenhar o histograma de x"
+PythonImportCmath = "Importar módulo cmath"
+PythonImportIon = "Importar módulo ion"
+PythonImportKandinsky = "Importar módulo kandinsky"
+PythonImportRandom = "Importar módulo random"
+PythonImportMath = "Importar módulo math"
+PythonImportMatplotlibPyplot = "Importar módulo matplotlib.pyplot"
+PythonImportTime = "Importar módulo time"
+PythonImportTurtle = "Importar módulo turtle"
+PythonIndex = "Índice da primeira ocorrência de x"
+PythonInput = "Adicionar um valor"
+PythonInsert = "Inserir x no índice i na lista"
+PythonInt = "Converter x num número inteiro"
+PythonIonFunction = "Prefixo da função do módulo ion"
+PythonIsFinite = "Verificar se x é finito"
+PythonIsInfinite = "Verificar se x é infinito"
+PythonIsKeyDown = "Devolve True se tecla k pressionada"
+PythonIsNaN = "Verificar se x é um NaN"
+PythonKandinskyFunction = "Prefixo da função do módulo kandinsky"
+PythonKeyLeft = "tecla SETA ESQUERDA"
+PythonKeyUp = "tecla SETA CIMA "
+PythonKeyDown = "tecla SETA BAIXO"
+PythonKeyRight = "tecla SETA DIREITA"
+PythonKeyOk = "tecla OK"
+PythonKeyBack = "tecla VOLTAR"
+PythonKeyHome = "tecla HOME"
+PythonKeyOnOff = "tecla ON/OFF"
+PythonKeyShift = "tecla SHIFT"
+PythonKeyAlpha = "tecla ALPHA"
+PythonKeyXnt = "tecla X,N,T"
+PythonKeyVar = "tecla VAR"
+PythonKeyToolbox = "tecla CAIXA DE FERRAMENTAS"
+PythonKeyBackspace = "tecla APAGAR"
+PythonKeyExp = "tecla EXPONENCIAL"
+PythonKeyLn = "tecla LOGARITMO NATURAL"
+PythonKeyLog = "tecla LOGARITMO DECIMAL"
+PythonKeyImaginary = "tecla I IMAGINÁRIO"
+PythonKeyComma = "tecla VÍRGULA"
+PythonKeyPower = "tecla EXPOENTE"
+PythonKeySine = "tecla SENO"
+PythonKeyCosine = "tecla COSSENO"
+PythonKeyTangent = "tecla TANGENTE"
+PythonKeyPi = "tecla PI"
+PythonKeySqrt = "tecla RAIZ QUADRADA"
+PythonKeySquare = "tecla AO QUADRADO"
+PythonKeySeven = "tecla 7"
+PythonKeyEight = "tecla 8"
+PythonKeyNine = "tecla 9"
+PythonKeyLeftParenthesis = "tecla PARÊNTESE ESQUERDO"
+PythonKeyRightParenthesis = "tecla PARÊNTESE DIREITO"
+PythonKeyFour = "tecla 4"
+PythonKeyFive = "tecla 5"
+PythonKeySix = "tecla 6"
+PythonKeyMultiplication = "tecla MULTIPLICAÇÃO"
+PythonKeyDivision = "tecla DIVISÃO"
+PythonKeyOne = "tecla 1"
+PythonKeyTwo = "tecla 2"
+PythonKeyThree = "tecla 3"
+PythonKeyPlus = "tecla MAIS"
+PythonKeyMinus = "tecla MENOS"
+PythonKeyZero = "tecla 0"
+PythonKeyDot = "tecla PONTO"
+PythonKeyEe = "tecla 10 expoente X"
+PythonKeyAns = "tecla ANS"
+PythonKeyExe = "tecla EXE"
+PythonLdexp = "Devolve x*(2**i), inverso de frexp"
+PythonLength = "Comprimento de um objeto"
+PythonLgamma = "Logaritmo da função gama"
+PythonLog = "Logaritmo de base a"
+PythonLog10 = "Logaritmo de base 10"
+PythonLog2 = "Logaritmo de base 2"
+PythonMathFunction = "Prefixo da função do módulo math"
+PythonMatplotlibPyplotFunction = "Prefixo do módulo matplotlib.pyplot"
+PythonMax = "Máximo"
+PythonMin = "Mínimo"
+PythonModf = "Partes inteira e frácionária de x"
+PythonMonotonic = "Devolve o valor do relógio"
+PythonOct = "Converter número inteiro em octal"
+PythonPhase = "Argumento de z"
+PythonPlot = "Desenhar y em função de x"
+PythonPolar = "z em coordenadas polares"
+PythonPop = "Remover o último item"
+PythonPower = "x levantado a y"
+PythonPrint = "Mostrar o objeto"
+PythonRadians = "Converter x de graus para radianos"
+PythonRandint = "Número inteiro aleatório em [a,b]"
+PythonRandom = "Número decimal em [0,1["
+PythonRandomFunction = "Prefixo da função do módulo random"
+PythonRandrange = "Número aleatório em [start,stop-1]"
+PythonRangeStartStop = "Lista de start a stop-1"
+PythonRangeStop = "Lista de 0 a stop-1"
+PythonRect = "Converter para coordenadas cartesianas"
+PythonRemove = "Remover a primeira ocorrência de x"
+PythonReverse = "Inverter os elementos da lista"
+PythonRound = "Arredondar para n dígitos"
+PythonScatter = "Gráfico de dispersão (x,y)"
+PythonSeed = "Iniciar gerador aleatório"
+PythonSetPixel = "Cor do pixel (x,y)"
+PythonShow = "Mostrar a figura"
+PythonSin = "Seno"
+PythonSinh = "Seno hiperbólico"
+PythonSleep = "Suspender a execução por t segundos"
+PythonSort = "Ordenar a lista"
+PythonSqrt = "Raiz quadrada"
+PythonSum = "Soma dos itens da lista"
+PythonTan = "Tangente"
+PythonTanh = "Tangente hiperbólica"
+PythonText = "Mostrar um texto em (x,y)"
+PythonTimeFunction = "Prefixo da função do módulo time"
+PythonTrunc = "x truncado a um número inteiro"
+PythonTurtleBackward = "Recuar x pixels"
+PythonTurtleCircle = "Circunferência de raio r pixels"
+PythonTurtleColor = "Definir a cor da caneta"
+PythonTurtleColorMode = "Define modo de cor para 1.0 ou 255"
+PythonTurtleForward = "Avançar x pixels"
+PythonTurtleFunction = "Prefixo da função do módulo turtle"
+PythonTurtleGoto = "Ir paras as coordenadas (x,y)"
+PythonTurtleHeading = "Voltar para a orientação atual"
+PythonTurtleHideturtle = "Esconder o turtle"
+PythonTurtleIsdown = "True se a caneta está pressionada"
+PythonTurtleLeft = "Vira à esquerda por a graus"
+PythonTurtlePendown = "Puxar a caneta para baixo"
+PythonTurtlePensize = "Definir a espessura para x pixels"
+PythonTurtlePenup = "Puxar a caneta para cima"
+PythonTurtlePosition = "Devolve a posição atual (x,y)"
+PythonTurtleReset = "Reiniciar o desenho"
+PythonTurtleRight = "Virar à esquerda por a graus"
+PythonTurtleSetheading = "Definir a orientação por a graus"
PythonTurtleSetposition = "Positionne la tortue"
-PythonTurtleShowturtle = "Show the turtle"
-PythonTurtleSpeed = "Drawing speed between 0 and 10"
-PythonTurtleWhite = "White color"
-PythonTurtleYellow = "Yellow color"
-PythonUniform = "Floating point number in [a,b]"
-PythonTimeFromImport = "Import time module"
-PythonTimeImport = "Import time module"
+PythonTurtleShowturtle = "Mostrar o turtle"
+PythonTurtleSpeed = "Velocidade do desenho entre 0 e 10"
+PythonTurtleWrite = "Mostrar um texto"
+PythonUniform = "Número decimal em [a,b]"
+PythonImportTime = "Import time module"
PythonTimePrefix = "time module function prefix"
-PythonTimeSleep = "Aguardar n segundos"
-PythonTimeMonotonic = "Retornar tempo monotônico"
+PythonTimeSleep = "Wait for n second"
+PythonMonotonic = "Return monotonic time"
+PythonFileOpen = "Opens a file"
+PythonFileSeekable = "Tells if seek can be used on a file"
+PythonFileSeek = "Move file's cursor"
+PythonFileTell = "Get file's cursor location"
+PythonFileClose = "Closes a file"
+PythonFileClosed = "True if file was closed"
+PythonFileRead = "Read up to size bytes"
+PythonFileWrite = "Write b into file"
+PythonFileReadline = "Reads a line or up to size bytes"
+PythonFileReadlines = "Reads a list of lines"
+PythonFileTruncate = "Resize the file to size"
+PythonFileWritelines = "Writes a list of lines"
+PythonFileName = "Contains file's name"
+PythonFileMode = "Contains file's open mode"
+PythonFileReadable = "Tells if read can be used on a file"
+PythonFileWritable = "Tells if write can be used on a file"
diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n
index d83b8cb1d..371a33f44 100644
--- a/apps/code/catalog.universal.i18n
+++ b/apps/code/catalog.universal.i18n
@@ -29,6 +29,17 @@ PythonCommandClearWithoutArg = ".clear()"
PythonCommandCmathFunction = "cmath.function"
PythonCommandCmathFunctionWithoutArg = "cmath.\x11"
PythonCommandColor = "color(r,g,b)"
+PythonCommandColorBlack = "'black'"
+PythonCommandColorBlue = "'blue'"
+PythonCommandColorBrown = "'brown'"
+PythonCommandColorGreen = "'green'"
+PythonCommandColorGrey = "'grey'"
+PythonCommandColorOrange = "'orange'"
+PythonCommandColorPink = "'pink'"
+PythonCommandColorPurple = "'purple'"
+PythonCommandColorRed = "'red'"
+PythonCommandColorWhite = "'white'"
+PythonCommandColorYellow = "'yellow'"
PythonCommandComplex = "complex(a,b)"
PythonCommandConstantPi = "pi"
PythonCommandCopySign = "copysign(x,y)"
@@ -154,7 +165,7 @@ PythonCommandModf = "modf(x)"
PythonCommandMonotonic = "monotonic()"
PythonCommandOct = "oct(x)"
PythonCommandPhase = "phase(z)"
-PythonCommandPlot = "plot(x,y)"
+PythonCommandPlot = "plot(x,y,color)"
PythonCommandPolar = "polar(z)"
PythonCommandPop = "list.pop()"
PythonCommandPopWithoutArg = ".pop()"
@@ -165,17 +176,17 @@ PythonCommandRandint = "randint(a,b)"
PythonCommandRandom = "random()"
PythonCommandRandomFunction = "random.function"
PythonCommandRandomFunctionWithoutArg = "random.\x11"
-PythonCommandRandrange = "randrange(start, stop)"
-PythonCommandRangeStartStop = "range(start, stop)"
+PythonCommandRandrange = "randrange(start,stop)"
+PythonCommandRangeStartStop = "range(start,stop)"
PythonCommandRangeStop = "range(stop)"
PythonCommandReal = "z.real"
PythonCommandRealWithoutArg = ".real"
-PythonCommandRect = "rect(r, arg)"
+PythonCommandRect = "rect(r,arg)"
PythonCommandRemove = "list.remove(x)"
PythonCommandRemoveWithoutArg = ".remove(\x11)"
PythonCommandReverse = "list.reverse()"
PythonCommandReverseWithoutArg = ".reverse()"
-PythonCommandRound = "round(x, n)"
+PythonCommandRound = "round(x,n)"
PythonCommandScatter = "scatter(x,y)"
PythonCommandSeed = "seed(x)"
PythonCommandSetPixel = "set_pixel(x,y,color)"
@@ -202,32 +213,23 @@ PythonCommandUniform = "uniform(a,b)"
PythonConstantE = "2.718281828459045"
PythonConstantPi = "3.141592653589793"
PythonTurtleCommandBackward = "backward(x)"
-PythonTurtleCommandBlack = "'black'"
-PythonTurtleCommandBlue = "'blue'"
-PythonTurtleCommandBrown = "'brown'"
PythonTurtleCommandCircle = "circle(r)"
-PythonTurtleCommandColor = "color('c')/color(r,g,b)"
-PythonTurtleCommandColorWithoutArg = "color(\x11)"
+PythonTurtleCommandColor = "color('c')"
+PythonTurtleCommandColorMode = "colormode(x)"
PythonTurtleCommandForward = "forward(x)"
PythonTurtleCommandGoto = "goto(x,y)"
-PythonTurtleCommandGreen = "'green'"
-PythonTurtleCommandGrey = "'grey'"
PythonTurtleCommandHeading = "heading()"
PythonTurtleCommandHideturtle = "hideturtle()"
PythonTurtleCommandIsdown= "isdown()"
PythonTurtleCommandLeft = "left(a)"
-PythonTurtleCommandOrange = "'orange'"
PythonTurtleCommandPendown = "pendown()"
PythonTurtleCommandPensize = "pensize(x)"
PythonTurtleCommandPenup = "penup()"
-PythonTurtleCommandPink = "'pink'"
PythonTurtleCommandPosition = "position()"
-PythonTurtleCommandPurple = "'purple'"
-PythonTurtleCommandRed = "'red'"
PythonTurtleCommandReset = "reset()"
PythonTurtleCommandRight = "right(a)"
PythonTurtleCommandSetheading = "setheading(a)"
-PythonTurtleCommandSetposition = "setposition(x, [y])"
+PythonTurtleCommandSetposition = "setposition(x,[y])"
PythonTurtleCommandShowturtle = "showturtle()"
PythonTurtleCommandSpeed = "speed(x)"
PythonTurtleCommandWhite = "'white'"
@@ -237,3 +239,36 @@ PythonTimeCommandImportFrom = "from time import *"
PythonTimeCommandSleep = "sleep()"
PythonTimeCommandSleepDemo = "sleep(n)"
PythonTimeCommandMonotonic = "monotonic()"
+PythonCommandFileOpen = "open(name, [mode])"
+PythonCommandFileOpenWithoutArg = "open(\x11)"
+PythonCommandFileSeek = "file.seek(offset, [whence])"
+PythonCommandFileSeekWithoutArg = ".seek(\x11)"
+PythonCommandFileTell = "file.tell()"
+PythonCommandFileTellWithoutArg = ".tell()"
+PythonCommandFileSeekable = "file.seekable()"
+PythonCommandFileSeekableWithoutArg = ".seekable()"
+PythonCommandFileClose = "file.close()"
+PythonCommandFileCloseWithoutArg = ".close()"
+PythonCommandFileClosed = "file.closed"
+PythonCommandFileClosedWithoutArg = ".closed"
+PythonCommandFileRead = "file.read([size])"
+PythonCommandFileReadWithoutArg = ".read(\x11)"
+PythonCommandFileWrite = "file.write(b)"
+PythonCommandFileWriteWithoutArg = ".write(\x11)"
+PythonCommandFileReadline = "file.readline([size])"
+PythonCommandFileReadlineWithoutArg = ".readline(\x11)"
+PythonCommandFileReadlines = "file.readlines([hint])"
+PythonCommandFileReadlinesWithoutArg = ".readlines(\x11)"
+PythonCommandFileTruncate = "file.truncate([size])"
+PythonCommandFileTruncateWithoutArg = ".truncate(\x11)"
+PythonCommandFileWritelines = "file.writelines(lines)"
+PythonCommandFileWritelinesWithoutArg = ".writelines(\x11)"
+PythonCommandFileName = "file.name"
+PythonCommandFileNameWithoutArg = ".name"
+PythonCommandFileMode = "file.mode"
+PythonCommandFileModeWithoutArg = ".mode"
+PythonCommandFileReadable = "file.readable()"
+PythonCommandFileReadableWithoutArg = ".readable()"
+PythonCommandFileWritable = "file.writable()"
+PythonCommandFileWritableWithoutArg = ".writable()"
+PythonTurtleCommandWrite = "write(\"text\")"
diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp
index 1cdb14113..2d01720da 100644
--- a/apps/code/console_controller.cpp
+++ b/apps/code/console_controller.cpp
@@ -3,6 +3,7 @@
#include "script.h"
#include "variable_box_controller.h"
#include
+#include
#include
#include
#include
@@ -16,8 +17,6 @@ extern "C" {
namespace Code {
-static inline int minInt(int x, int y) { return x < y ? x : y; }
-
static const char * sStandardPromptText = ">>> ";
ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
@@ -32,7 +31,7 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
m_pythonDelegate(pythonDelegate),
m_importScriptsWhenViewAppears(false),
m_selectableTableView(this, this, this, this),
- m_editCell(this, pythonDelegate, this),
+ m_editCell(this, this, this),
m_scriptStore(scriptStore),
m_sandboxController(this),
m_inputRunLoopActive(false)
@@ -49,17 +48,13 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
}
bool ConsoleController::loadPythonEnvironment() {
- if (m_pythonDelegate->isPythonUser(this)) {
- return true;
+ if (!m_pythonDelegate->isPythonUser(this)) {
+ m_scriptStore->clearConsoleFetchInformation();
+ emptyOutputAccumulationBuffer();
+ m_pythonDelegate->initPythonWithUser(this);
+ MicroPython::registerScriptProvider(m_scriptStore);
+ m_importScriptsWhenViewAppears = m_autoImportScripts;
}
- emptyOutputAccumulationBuffer();
- m_pythonDelegate->initPythonWithUser(this);
- MicroPython::registerScriptProvider(m_scriptStore);
- m_importScriptsWhenViewAppears = m_autoImportScripts;
- /* We load functions and variables names in the variable box before running
- * any other python code to avoid failling to load functions and variables
- * due to memory exhaustion. */
- App::app()->variableBoxController()->loadFunctionsAndVariables();
return true;
}
@@ -291,7 +286,7 @@ void ConsoleController::willDisplayCellAtLocation(HighlightCell * cell, int i, i
}
}
-void ConsoleController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
+void ConsoleController::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection) {
return;
}
@@ -376,6 +371,14 @@ bool ConsoleController::textFieldDidAbortEditing(TextField * textField) {
return true;
}
+VariableBoxController * ConsoleController::variableBoxForInputEventHandler(InputEventHandler * textInput) {
+ VariableBoxController * varBox = App::app()->variableBoxController();
+ varBox->loadVariablesImportedFromScripts();
+ varBox->setTitle(I18n::Message::FunctionsAndVariables);
+ varBox->setDisplaySubtitles(false);
+ return varBox;
+}
+
void ConsoleController::resetSandbox() {
if (stackViewController()->topViewController() != sandbox()) {
return;
@@ -448,7 +451,7 @@ void ConsoleController::printText(const char * text, size_t length) {
flushOutputAccumulationBufferToStore();
micropython_port_vm_hook_refresh_print();
}
-#if __EMSCRIPTEN__
+// #if __EMSCRIPTEN__
/* If we called micropython_port_interrupt_if_needed here, we would need to
* put in the WHITELIST all the methods that call
* ConsoleController::printText, which means all the MicroPython methods that
@@ -461,13 +464,17 @@ void ConsoleController::printText(const char * text, size_t length) {
* device.
*
* TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */
-#else
+
+ /*
+ * This can be run in Omega, since it uses WebASM.
+ */
+// #else
/* micropython_port_vm_hook_loop is not enough to detect user interruptions,
* because it calls micropython_port_interrupt_if_needed every 20000
* operations, and a print operation is quite long. We thus explicitely call
* micropython_port_interrupt_if_needed here. */
micropython_port_interrupt_if_needed();
-#endif
+// #endif
}
void ConsoleController::autoImportScript(Script script, bool force) {
@@ -476,7 +483,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
* the sandbox. */
hideAnyDisplayedViewController();
- if (script.importationStatus() || force) {
+ if (script.autoImportationStatus() || force) {
// Step 1 - Create the command "from scriptName import *".
assert(strlen(k_importCommand1) + strlen(script.fullName()) - strlen(ScriptStore::k_scriptExtension) - 1 + strlen(k_importCommand2) + 1 <= k_maxImportCommandSize);
@@ -488,7 +495,7 @@ void ConsoleController::autoImportScript(Script script, bool force) {
/* Copy the script name without the extension ".py". The '.' is overwritten
* by the null terminating char. */
- int copySizeWithNullTerminatingZero = minInt(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
+ int copySizeWithNullTerminatingZero = std::min(k_maxImportCommandSize - currentChar, strlen(scriptName) - strlen(ScriptStore::k_scriptExtension));
assert(copySizeWithNullTerminatingZero >= 0);
assert(copySizeWithNullTerminatingZero <= k_maxImportCommandSize - currentChar);
strlcpy(command+currentChar, scriptName, copySizeWithNullTerminatingZero);
diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h
index e50d31c34..d4fa256f6 100644
--- a/apps/code/console_controller.h
+++ b/apps/code/console_controller.h
@@ -10,12 +10,14 @@
#include "console_store.h"
#include "sandbox_controller.h"
#include "script_store.h"
+#include "variable_box_controller.h"
+#include "../shared/input_event_handler_delegate.h"
namespace Code {
class App;
-class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public MicroPython::ExecutionEnvironment {
+class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public Shared::InputEventHandlerDelegate, public MicroPython::ExecutionEnvironment {
public:
ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore
#if EPSILON_GETOPT
@@ -52,7 +54,7 @@ public:
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
// SelectableTableViewDelegate
- void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
+ void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
// TextFieldDelegate
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
@@ -60,6 +62,9 @@ public:
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
bool textFieldDidAbortEditing(TextField * textField) override;
+ // InputEventHandlerDelegate
+ VariableBoxController * variableBoxForInputEventHandler(InputEventHandler * textInput) override;
+
// MicroPython::ExecutionEnvironment
ViewController * sandbox() override { return &m_sandboxController; }
void resetSandbox() override;
diff --git a/apps/code/console_edit_cell.cpp b/apps/code/console_edit_cell.cpp
index f938f52dc..f82d5d596 100644
--- a/apps/code/console_edit_cell.cpp
+++ b/apps/code/console_edit_cell.cpp
@@ -4,11 +4,10 @@
#include
#include
#include
+#include
namespace Code {
-static inline int minInt(int x, int y) { return x < y ? x : y; }
-
ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) :
HighlightCell(),
Responder(parentResponder),
@@ -70,7 +69,7 @@ const char * ConsoleEditCell::shiftCurrentTextAndClear() {
char * textFieldBuffer = const_cast(m_textField.text());
char * newTextPosition = textFieldBuffer + 1;
assert(previousBufferSize > 0);
- size_t copyLength = minInt(previousBufferSize - 1, strlen(textFieldBuffer));
+ size_t copyLength = std::min(previousBufferSize - 1, strlen(textFieldBuffer));
memmove(newTextPosition, textFieldBuffer, copyLength);
newTextPosition[copyLength] = 0;
textFieldBuffer[0] = 0;
diff --git a/apps/code/console_line_cell.cpp b/apps/code/console_line_cell.cpp
index 458c8eb42..3cad12787 100644
--- a/apps/code/console_line_cell.cpp
+++ b/apps/code/console_line_cell.cpp
@@ -19,7 +19,7 @@ void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::setLine(Consol
void ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), Palette::CodeBackground);
- ctx->drawString(m_line->text(), KDPointZero, GlobalPreferences::sharedGlobalPreferences()->font(), textColor(m_line), isHighlighted()? Palette::Select : KDColorWhite);
+ ctx->drawString(m_line->text(), KDPointZero, GlobalPreferences::sharedGlobalPreferences()->font(), textColor(m_line), isHighlighted()? Palette::Select : Palette::BackgroundApps);
}
KDSize ConsoleLineCell::ScrollableConsoleLineView::ConsoleLineView::minimalSizeForOptimalDisplay() const {
diff --git a/apps/code/console_store.cpp b/apps/code/console_store.cpp
index 390ba860e..e4ff3fa35 100644
--- a/apps/code/console_store.cpp
+++ b/apps/code/console_store.cpp
@@ -1,10 +1,9 @@
#include "console_store.h"
#include
+#include
namespace Code {
-static inline int minInt(int x, int y) { return x < y ? x : y; }
-
void ConsoleStore::startNewSession() {
if (k_historySize < 1) {
return;
@@ -12,7 +11,7 @@ void ConsoleStore::startNewSession() {
m_history[0] = makePrevious(m_history[0]);
- for (int i = 0; i < k_historySize - 1; i++) {
+ for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
if (m_history[i+1] == 0) {
return ;
@@ -25,7 +24,7 @@ void ConsoleStore::startNewSession() {
ConsoleLine ConsoleStore::lineAtIndex(int i) const {
assert(i >= 0 && i < numberOfLines());
int currentLineIndex = 0;
- for (int j=0; j k_historySize - 1) {
textLength = k_historySize - 1 - 1 - 1; // Marker, null termination and null marker.
}
- int i = indexOfNullMarker();
+ size_t i = indexOfNullMarker();
// If needed, make room for the text we want to push.
while (i + ConsoleLine::sizeOfConsoleLine(textLength) > k_historySize - 1) {
deleteFirstLine();
i = indexOfNullMarker();
}
m_history[i] = marker;
- strlcpy(&m_history[i+1], text, minInt(k_historySize-(i+1),textLength+1));
+ strlcpy(&m_history[i+1], text, std::min(k_historySize-(i+1),textLength+1));
m_history[i+1+textLength+1] = 0;
return &m_history[i+1];
}
@@ -113,11 +112,11 @@ ConsoleLine::Type ConsoleStore::lineTypeForMarker(char marker) const {
return static_cast(marker-1);
}
-int ConsoleStore::indexOfNullMarker() const {
+size_t ConsoleStore::indexOfNullMarker() const {
if (m_history[0] == 0) {
return 0;
}
- for (int i=0; i=0 && index < numberOfLines());
int currentLineIndex = 0;
- for (int i = 0; i < k_historySize - 1; i++) {
+ for (size_t i = 0; i < k_historySize - 1; i++) {
if (m_history[i] == 0) {
currentLineIndex++;
continue;
}
if (currentLineIndex == index) {
- int nextLineStart = i;
+ size_t nextLineStart = i;
while (m_history[nextLineStart] != 0 && nextLineStart < k_historySize - 2) {
nextLineStart++;
}
@@ -158,7 +157,7 @@ void ConsoleStore::deleteFirstLine() {
secondLineMarkerIndex++;
}
secondLineMarkerIndex++;
- for (int i=0; iputAvailableSpaceAtEndOfRecord(m_script);
- m_editorView.setText(const_cast(m_script.scriptContent()), newScriptSize - Script::k_importationStatusSize);
+ m_editorView.setText(const_cast(m_script.content()), newScriptSize - Script::StatusSize());
}
void EditorController::willExitApp() {
@@ -78,14 +80,6 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
return true;
}
- if(event.hasText()){
- if(event.text() == "%" && Ion::Events::isLockActive() ){
- return textArea->removePreviousGlyph();
- } else {
- return textArea->handleEventWithText(event.text(), true, false);
- }
- }
-
if (event == Ion::Events::Backspace && textArea->selectionIsEmpty()) {
/* If the cursor is on the left of the text of a line, backspace one
@@ -131,7 +125,24 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
VariableBoxController * EditorController::variableBoxForInputEventHandler(InputEventHandler * textInput) {
VariableBoxController * varBox = App::app()->variableBoxController();
- varBox->loadFunctionsAndVariables();
+ /* If the editor should be autocompleting an identifier, the variable box has
+ * already been loaded. We check shouldAutocomplete and not isAutocompleting,
+ * because the autocompletion result might be empty. */
+ const char * beginningOfAutocompletion = nullptr;
+ const char * cursor = nullptr;
+ PythonTextArea::AutocompletionType autocompType = m_editorView.autocompletionType(&beginningOfAutocompletion, &cursor);
+ if (autocompType == PythonTextArea::AutocompletionType::NoIdentifier) {
+ varBox->loadFunctionsAndVariables(m_scriptIndex, nullptr, 0);
+ } else if (autocompType == PythonTextArea::AutocompletionType::MiddleOfIdentifier) {
+ varBox->empty();
+ } else {
+ assert(autocompType == PythonTextArea::AutocompletionType::EndOfIdentifier);
+ assert(beginningOfAutocompletion != nullptr && cursor != nullptr);
+ assert(cursor > beginningOfAutocompletion);
+ varBox->loadFunctionsAndVariables(m_scriptIndex, beginningOfAutocompletion, cursor - beginningOfAutocompletion);
+ }
+ varBox->setTitle(I18n::Message::Autocomplete);
+ varBox->setDisplaySubtitles(true);
return varBox;
}
@@ -146,7 +157,7 @@ void EditorController::cleanStorageEmptySpace() {
Ion::Storage::Record::Data scriptValue = m_script.value();
Ion::Storage::sharedStorage()->getAvailableSpaceFromEndOfRecord(
m_script,
- scriptValue.size - Script::k_importationStatusSize - (strlen(m_script.scriptContent()) + 1)); // TODO optimize number of script fetches
+ scriptValue.size - Script::StatusSize() - (strlen(m_script.content()) + 1)); // TODO optimize number of script fetches
}
diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h
index 7fb5d9560..4cd32c1ed 100644
--- a/apps/code/editor_controller.h
+++ b/apps/code/editor_controller.h
@@ -16,7 +16,8 @@ class App;
class EditorController : public ViewController, public TextAreaDelegate, public Shared::InputEventHandlerDelegate {
public:
EditorController(MenuController * menuController, App * pythonDelegate);
- void setScript(Script script);
+ void setScript(Script script, int scriptIndex);
+ int scriptIndex() const { return m_scriptIndex; }
void willExitApp();
/* ViewController */
@@ -39,6 +40,7 @@ private:
StackViewController * stackController();
EditorView m_editorView;
Script m_script;
+ int m_scriptIndex;
MenuController * m_menuController;
};
diff --git a/apps/code/editor_view.cpp b/apps/code/editor_view.cpp
index ec27baf50..8c4832c8c 100644
--- a/apps/code/editor_view.cpp
+++ b/apps/code/editor_view.cpp
@@ -17,6 +17,10 @@ EditorView::EditorView(Responder * parentResponder, App * pythonDelegate) :
m_textArea.setScrollViewDelegate(this);
}
+bool EditorView::isAutocompleting() const {
+ return m_textArea.isAutocompleting();
+}
+
void EditorView::resetSelection() {
m_textArea.resetSelection();
}
@@ -53,8 +57,8 @@ void EditorView::layoutSubviews(bool force) {
/* EditorView::GutterView */
void EditorView::GutterView::drawRect(KDContext * ctx, KDRect rect) const {
- KDColor textColor = KDColor::RGB24(0x919EA4);
- KDColor backgroundColor = KDColor::RGB24(0xE4E6E7);
+ KDColor textColor = Palette::PrimaryText;
+ KDColor backgroundColor = Palette::CodeGutterViewBackground;
ctx->fillRect(rect, backgroundColor);
diff --git a/apps/code/editor_view.h b/apps/code/editor_view.h
index bbc608e88..ab3038c94 100644
--- a/apps/code/editor_view.h
+++ b/apps/code/editor_view.h
@@ -9,6 +9,8 @@ namespace Code {
class EditorView : public Responder, public View, public ScrollViewDelegate {
public:
EditorView(Responder * parentResponder, App * pythonDelegate);
+ PythonTextArea::AutocompletionType autocompletionType(const char ** autocompletionBeginning, const char ** autocompletionEnd) const { return m_textArea.autocompletionType(nullptr, autocompletionBeginning, autocompletionEnd); }
+ bool isAutocompleting() const;
void resetSelection();
void setTextAreaDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, TextAreaDelegate * delegate) {
m_textArea.setDelegates(inputEventHandlerDelegate, delegate);
@@ -17,6 +19,9 @@ public:
void setText(char * textBuffer, size_t textBufferSize) {
m_textArea.setText(textBuffer, textBufferSize);
}
+ const char * cursorLocation() {
+ return m_textArea.cursorLocation();
+ }
bool setCursorLocation(const char * location) {
return m_textArea.setCursorLocation(location);
}
diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp
index e797ba675..a857fc539 100644
--- a/apps/code/menu_controller.cpp
+++ b/apps/code/menu_controller.cpp
@@ -385,7 +385,7 @@ void MenuController::configureScript() {
void MenuController::editScriptAtIndex(int scriptIndex) {
assert(scriptIndex >=0 && scriptIndex < m_scriptStore->numberOfScripts());
Script script = m_scriptStore->scriptAtIndex(scriptIndex);
- m_editorController.setScript(script);
+ m_editorController.setScript(script, scriptIndex);
stackViewController()->push(&m_editorController);
}
diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h
index 5730fdccb..72bb63130 100644
--- a/apps/code/menu_controller.h
+++ b/apps/code/menu_controller.h
@@ -25,6 +25,7 @@ public:
void openConsoleWithScript(Script script);
void scriptContentEditionDidFinish();
void willExitApp();
+ int editedScriptIndex() const { return m_editorController.scriptIndex(); }
/* ViewController */
View * view() override { return &m_selectableTableView; }
diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp
index a2bce364f..eef77034a 100644
--- a/apps/code/python_text_area.cpp
+++ b/apps/code/python_text_area.cpp
@@ -9,6 +9,7 @@ extern "C" {
#include "py/lexer.h"
}
#include
+#include
namespace Code {
@@ -20,8 +21,7 @@ constexpr KDColor OperatorColor = Palette::CodeOperator;
constexpr KDColor StringColor = Palette::CodeString;
constexpr KDColor BackgroundColor = Palette::CodeBackground;
constexpr KDColor HighlightColor = Palette::CodeBackgroundSelected;
-
-static inline const char * minPointer(const char * x, const char * y) { return x < y ? x : y; }
+constexpr KDColor AutocompleteColor = KDColor::RGB24(0xC6C6C6); // TODO Palette change
static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
if (tokenKind == MP_TOKEN_STRING) {
@@ -30,13 +30,96 @@ static inline KDColor TokenColor(mp_token_kind_t tokenKind) {
if (tokenKind == MP_TOKEN_INTEGER || tokenKind == MP_TOKEN_FLOAT_OR_IMAG) {
return NumberColor;
}
+ static_assert(MP_TOKEN_ELLIPSIS + 1 == MP_TOKEN_KW_FALSE
+ && MP_TOKEN_KW_FALSE + 1 == MP_TOKEN_KW_NONE
+ && MP_TOKEN_KW_NONE + 1 == MP_TOKEN_KW_TRUE
+ && MP_TOKEN_KW_TRUE + 1 == MP_TOKEN_KW___DEBUG__
+ && MP_TOKEN_KW___DEBUG__ + 1 == MP_TOKEN_KW_AND
+ && MP_TOKEN_KW_AND + 1 == MP_TOKEN_KW_AS
+ && MP_TOKEN_KW_AS + 1 == MP_TOKEN_KW_ASSERT
+ /* Here there are keywords that depend on MICROPY_PY_ASYNC_AWAIT, we do
+ * not test them */
+ && MP_TOKEN_KW_BREAK + 1 == MP_TOKEN_KW_CLASS
+ && MP_TOKEN_KW_CLASS + 1 == MP_TOKEN_KW_CONTINUE
+ && MP_TOKEN_KW_CONTINUE + 1 == MP_TOKEN_KW_DEF
+ && MP_TOKEN_KW_DEF + 1 == MP_TOKEN_KW_DEL
+ && MP_TOKEN_KW_DEL + 1 == MP_TOKEN_KW_ELIF
+ && MP_TOKEN_KW_ELIF + 1 == MP_TOKEN_KW_ELSE
+ && MP_TOKEN_KW_ELSE + 1 == MP_TOKEN_KW_EXCEPT
+ && MP_TOKEN_KW_EXCEPT + 1 == MP_TOKEN_KW_FINALLY
+ && MP_TOKEN_KW_FINALLY + 1 == MP_TOKEN_KW_FOR
+ && MP_TOKEN_KW_FOR + 1 == MP_TOKEN_KW_FROM
+ && MP_TOKEN_KW_FROM + 1 == MP_TOKEN_KW_GLOBAL
+ && MP_TOKEN_KW_GLOBAL + 1 == MP_TOKEN_KW_IF
+ && MP_TOKEN_KW_IF + 1 == MP_TOKEN_KW_IMPORT
+ && MP_TOKEN_KW_IMPORT + 1 == MP_TOKEN_KW_IN
+ && MP_TOKEN_KW_IN + 1 == MP_TOKEN_KW_IS
+ && MP_TOKEN_KW_IS + 1 == MP_TOKEN_KW_LAMBDA
+ && MP_TOKEN_KW_LAMBDA + 1 == MP_TOKEN_KW_NONLOCAL
+ && MP_TOKEN_KW_NONLOCAL + 1 == MP_TOKEN_KW_NOT
+ && MP_TOKEN_KW_NOT + 1 == MP_TOKEN_KW_OR
+ && MP_TOKEN_KW_OR + 1 == MP_TOKEN_KW_PASS
+ && MP_TOKEN_KW_PASS + 1 == MP_TOKEN_KW_RAISE
+ && MP_TOKEN_KW_RAISE + 1 == MP_TOKEN_KW_RETURN
+ && MP_TOKEN_KW_RETURN + 1 == MP_TOKEN_KW_TRY
+ && MP_TOKEN_KW_TRY + 1 == MP_TOKEN_KW_WHILE
+ && MP_TOKEN_KW_WHILE + 1 == MP_TOKEN_KW_WITH
+ && MP_TOKEN_KW_WITH + 1 == MP_TOKEN_KW_YIELD
+ && MP_TOKEN_KW_YIELD + 1 == MP_TOKEN_OP_TILDE,
+ "MP_TOKEN order changed, so Code::PythonTextArea::TokenColor might need to change too.");
if (tokenKind >= MP_TOKEN_KW_FALSE && tokenKind <= MP_TOKEN_KW_YIELD) {
return KeywordColor;
}
- if (tokenKind >= MP_TOKEN_OP_PLUS && tokenKind <= MP_TOKEN_OP_NOT_EQUAL) {
- return OperatorColor;
- }
- if (tokenKind >= MP_TOKEN_DEL_EQUAL && tokenKind <= MP_TOKEN_DEL_MINUS_MORE) {
+ static_assert(MP_TOKEN_OP_TILDE + 1 == MP_TOKEN_OP_LESS
+ && MP_TOKEN_OP_LESS + 1 == MP_TOKEN_OP_MORE
+ && MP_TOKEN_OP_MORE + 1 == MP_TOKEN_OP_DBL_EQUAL
+ && MP_TOKEN_OP_DBL_EQUAL + 1 == MP_TOKEN_OP_LESS_EQUAL
+ && MP_TOKEN_OP_LESS_EQUAL + 1 == MP_TOKEN_OP_MORE_EQUAL
+ && MP_TOKEN_OP_MORE_EQUAL + 1 == MP_TOKEN_OP_NOT_EQUAL
+ && MP_TOKEN_OP_NOT_EQUAL + 1 == MP_TOKEN_OP_PIPE
+ && MP_TOKEN_OP_PIPE + 1 == MP_TOKEN_OP_CARET
+ && MP_TOKEN_OP_CARET + 1 == MP_TOKEN_OP_AMPERSAND
+ && MP_TOKEN_OP_AMPERSAND + 1 == MP_TOKEN_OP_DBL_LESS
+ && MP_TOKEN_OP_DBL_LESS + 1 == MP_TOKEN_OP_DBL_MORE
+ && MP_TOKEN_OP_DBL_MORE + 1 == MP_TOKEN_OP_PLUS
+ && MP_TOKEN_OP_PLUS + 1 == MP_TOKEN_OP_MINUS
+ && MP_TOKEN_OP_MINUS + 1 == MP_TOKEN_OP_STAR
+ && MP_TOKEN_OP_STAR + 1 == MP_TOKEN_OP_AT
+ && MP_TOKEN_OP_AT + 1 == MP_TOKEN_OP_DBL_SLASH
+ && MP_TOKEN_OP_DBL_SLASH + 1 == MP_TOKEN_OP_SLASH
+ && MP_TOKEN_OP_SLASH + 1 == MP_TOKEN_OP_PERCENT
+ && MP_TOKEN_OP_PERCENT + 1 == MP_TOKEN_OP_DBL_STAR
+ && MP_TOKEN_OP_DBL_STAR + 1 == MP_TOKEN_DEL_PIPE_EQUAL
+ && MP_TOKEN_DEL_PIPE_EQUAL + 1 == MP_TOKEN_DEL_CARET_EQUAL
+ && MP_TOKEN_DEL_CARET_EQUAL + 1 == MP_TOKEN_DEL_AMPERSAND_EQUAL
+ && MP_TOKEN_DEL_AMPERSAND_EQUAL + 1 == MP_TOKEN_DEL_DBL_LESS_EQUAL
+ && MP_TOKEN_DEL_DBL_LESS_EQUAL + 1 == MP_TOKEN_DEL_DBL_MORE_EQUAL
+ && MP_TOKEN_DEL_DBL_MORE_EQUAL + 1 == MP_TOKEN_DEL_PLUS_EQUAL
+ && MP_TOKEN_DEL_PLUS_EQUAL + 1 == MP_TOKEN_DEL_MINUS_EQUAL
+ && MP_TOKEN_DEL_MINUS_EQUAL + 1 == MP_TOKEN_DEL_STAR_EQUAL
+ && MP_TOKEN_DEL_STAR_EQUAL + 1 == MP_TOKEN_DEL_AT_EQUAL
+ && MP_TOKEN_DEL_AT_EQUAL + 1 == MP_TOKEN_DEL_DBL_SLASH_EQUAL
+ && MP_TOKEN_DEL_DBL_SLASH_EQUAL + 1 == MP_TOKEN_DEL_SLASH_EQUAL
+ && MP_TOKEN_DEL_SLASH_EQUAL + 1 == MP_TOKEN_DEL_PERCENT_EQUAL
+ && MP_TOKEN_DEL_PERCENT_EQUAL + 1 == MP_TOKEN_DEL_DBL_STAR_EQUAL
+ && MP_TOKEN_DEL_DBL_STAR_EQUAL + 1 == MP_TOKEN_DEL_PAREN_OPEN
+ && MP_TOKEN_DEL_PAREN_OPEN + 1 == MP_TOKEN_DEL_PAREN_CLOSE
+ && MP_TOKEN_DEL_PAREN_CLOSE + 1 == MP_TOKEN_DEL_BRACKET_OPEN
+ && MP_TOKEN_DEL_BRACKET_OPEN + 1 == MP_TOKEN_DEL_BRACKET_CLOSE
+ && MP_TOKEN_DEL_BRACKET_CLOSE + 1 == MP_TOKEN_DEL_BRACE_OPEN
+ && MP_TOKEN_DEL_BRACE_OPEN + 1 == MP_TOKEN_DEL_BRACE_CLOSE
+ && MP_TOKEN_DEL_BRACE_CLOSE + 1 == MP_TOKEN_DEL_COMMA
+ && MP_TOKEN_DEL_COMMA + 1 == MP_TOKEN_DEL_COLON
+ && MP_TOKEN_DEL_COLON + 1 == MP_TOKEN_DEL_PERIOD
+ && MP_TOKEN_DEL_PERIOD + 1 == MP_TOKEN_DEL_SEMICOLON
+ && MP_TOKEN_DEL_SEMICOLON + 1 == MP_TOKEN_DEL_EQUAL
+ && MP_TOKEN_DEL_EQUAL + 1 == MP_TOKEN_DEL_MINUS_MORE,
+ "MP_TOKEN order changed, so Code::PythonTextArea::TokenColor might need to change too.");
+
+ if ((tokenKind >= MP_TOKEN_OP_TILDE && tokenKind <= MP_TOKEN_DEL_DBL_STAR_EQUAL)
+ || tokenKind == MP_TOKEN_DEL_EQUAL
+ || tokenKind == MP_TOKEN_DEL_MINUS_MORE)
+ {
return OperatorColor;
}
return Palette::CodeText;
@@ -53,6 +136,76 @@ static inline size_t TokenLength(mp_lexer_t * lex, const char * tokenPosition) {
return lex->column - lex->tok_column;
}
+PythonTextArea::AutocompletionType PythonTextArea::autocompletionType(const char * autocompletionLocation, const char ** autocompletionLocationBeginning, const char ** autocompletionLocationEnd) const {
+ const char * location = autocompletionLocation != nullptr ? autocompletionLocation : cursorLocation();
+ const char * beginningOfToken = nullptr;
+
+ /* If there is already autocompleting, the cursor must be at the end of an
+ * identifier. Trying to compute autocompletionType will fail: because of the
+ * autocompletion text, the cursor seems to be in the middle of an identifier. */
+ AutocompletionType autocompleteType = isAutocompleting() ? AutocompletionType::EndOfIdentifier : AutocompletionType::NoIdentifier;
+ if (autocompletionLocationBeginning == nullptr && autocompletionLocationEnd == nullptr) {
+ return autocompleteType;
+ }
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ const char * firstNonSpace = UTF8Helper::BeginningOfWord(m_contentView.editedText(), location);
+ mp_lexer_t * lex = mp_lexer_new_from_str_len(0, firstNonSpace, UTF8Helper::EndOfWord(location) - firstNonSpace, 0);
+
+ const char * tokenStart;
+ const char * tokenEnd;
+ _mp_token_kind_t currentTokenKind = lex->tok_kind;
+
+ while (currentTokenKind != MP_TOKEN_NEWLINE && currentTokenKind != MP_TOKEN_END) {
+ tokenStart = firstNonSpace + lex->tok_column - 1;
+ tokenEnd = tokenStart + TokenLength(lex, tokenStart);
+
+ if (location < tokenStart) {
+ // The location for autocompletion is not in an identifier
+ assert(autocompleteType == AutocompletionType::NoIdentifier);
+ break;
+ }
+ if (location <= tokenEnd) {
+ if (currentTokenKind == MP_TOKEN_NAME
+ || (currentTokenKind >= MP_TOKEN_KW_FALSE
+ && currentTokenKind <= MP_TOKEN_KW_YIELD))
+ {
+ /* The location for autocompletion is in the middle or at the end of
+ * an identifier. */
+ beginningOfToken = tokenStart;
+ /* If autocompleteType is already EndOfIdentifier, we are
+ * autocompleting, so we do not need to update autocompleteType. If we
+ * recomputed autocompleteType now, we might wrongly think that it is
+ * MiddleOfIdentifier because of the autocompetion text.
+ * Example : fin|ally -> the lexer is at the end of "fin", but because
+ * we are autocompleting with "ally", the lexer thinks the cursor is
+ * in the middle of an identifier. */
+ if (autocompleteType != AutocompletionType::EndOfIdentifier) {
+ autocompleteType = location < tokenEnd ? AutocompletionType::MiddleOfIdentifier : AutocompletionType::EndOfIdentifier;
+ }
+ }
+ break;
+ }
+ mp_lexer_to_next(lex);
+ currentTokenKind = lex->tok_kind;
+ }
+ mp_lexer_free(lex);
+ nlr_pop();
+ }
+ if (autocompletionLocationBeginning != nullptr) {
+ *autocompletionLocationBeginning = beginningOfToken;
+ }
+ if (autocompletionLocationEnd != nullptr) {
+ *autocompletionLocationEnd = location;
+ }
+ assert(!isAutocompleting() || autocompleteType == AutocompletionType::EndOfIdentifier);
+ return autocompleteType;
+}
+
+const char * PythonTextArea::ContentView::textToAutocomplete() const {
+ return UTF8Helper::BeginningOfWord(editedText(), cursorLocation());
+}
+
void PythonTextArea::ContentView::loadSyntaxHighlighter() {
m_pythonDelegate->initPythonWithUser(this);
}
@@ -76,23 +229,7 @@ void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const
void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t byteLength, int fromColumn, int toColumn, const char * selectionStart, const char * selectionEnd) const {
LOG_DRAW("Drawing \"%.*s\"\n", byteLength, text);
- if (!m_pythonDelegate->isPythonUser(this)) {
- const char * lineStart = UTF8Helper::CodePointAtGlyphOffset(text, fromColumn);
- const char * lineEnd = UTF8Helper::CodePointAtGlyphOffset(text, toColumn);
- drawStringAt(
- ctx,
- line,
- fromColumn,
- lineStart,
- minPointer(text + byteLength, lineEnd) - lineStart,
- StringColor,
- BackgroundColor,
- selectionStart,
- selectionEnd,
- HighlightColor
- );
- return;
- }
+ assert(m_pythonDelegate->isPythonUser(this));
/* We're using the MicroPython lexer to do syntax highlighting on a per-line
* basis. This can work, however the MicroPython lexer won't accept a line
@@ -107,7 +244,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
fromColumn,
spacesStart,
- minPointer(text + byteLength, firstNonSpace) - spacesStart,
+ std::min(text + byteLength, firstNonSpace) - spacesStart,
StringColor,
BackgroundColor,
selectionStart,
@@ -118,6 +255,8 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
return;
}
+ const char * autocompleteStart = m_autocomplete ? m_cursorLocation : nullptr;
+
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_lexer_t * lex = mp_lexer_new_from_str_len(0, firstNonSpace, byteLength - (firstNonSpace - text), 0);
@@ -135,7 +274,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenEnd),
tokenEnd,
- minPointer(text + byteLength, tokenFrom) - tokenEnd,
+ std::min(text + byteLength, tokenFrom) - tokenEnd,
StringColor,
BackgroundColor,
selectionStart,
@@ -144,12 +283,16 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
}
tokenLength = TokenLength(lex, tokenFrom);
tokenEnd = tokenFrom + tokenLength;
+
+ // If the token is being autocompleted, use DefaultColor
+ KDColor color = (tokenFrom <= autocompleteStart && autocompleteStart < tokenEnd) ? Palette::CodeText : TokenColor(lex->tok_kind);
+
LOG_DRAW("Draw \"%.*s\" for token %d\n", tokenLength, tokenFrom, lex->tok_kind);
drawStringAt(ctx, line,
UTF8Helper::GlyphOffsetAtCodePoint(text, tokenFrom),
tokenFrom,
tokenLength,
- TokenColor(lex->tok_kind),
+ color,
BackgroundColor,
selectionStart,
selectionEnd,
@@ -161,6 +304,7 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
tokenFrom += tokenLength;
+ // Even if the token is being autocompleted, use CommentColor
if (tokenFrom < text + byteLength) {
LOG_DRAW("Draw comment \"%.*s\" from %d\n", byteLength - (tokenFrom - text), firstNonSpace, tokenFrom);
drawStringAt(ctx, line,
@@ -177,6 +321,22 @@ void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char
mp_lexer_free(lex);
nlr_pop();
}
+
+ // Redraw the autocompleted word in the right color
+ if (m_autocomplete && autocompleteStart >= text && autocompleteStart < text + byteLength) {
+ assert(m_autocompletionEnd != nullptr && m_autocompletionEnd > autocompleteStart);
+ drawStringAt(
+ ctx,
+ line,
+ UTF8Helper::GlyphOffsetAtCodePoint(text, autocompleteStart),
+ autocompleteStart,
+ std::min(text + byteLength, m_autocompletionEnd) - autocompleteStart,
+ AutocompleteColor,
+ BackgroundColor,
+ nullptr,
+ nullptr,
+ HighlightColor);
+ }
}
KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position, bool includeFollowingLines) const {
@@ -194,4 +354,158 @@ KDRect PythonTextArea::ContentView::dirtyRectFromPosition(const char * position,
);
}
+bool PythonTextArea::handleEvent(Ion::Events::Event event) {
+ if (m_contentView.isAutocompleting()) {
+ // Handle event with autocompletion
+ if (event == Ion::Events::Right
+ || event == Ion::Events::ShiftRight
+ || event == Ion::Events::OK)
+ {
+ m_contentView.reloadRectFromPosition(m_contentView.cursorLocation(), false);
+ acceptAutocompletion(event != Ion::Events::ShiftRight);
+ if (event != Ion::Events::ShiftRight) {
+ // Do not process the event more
+ scrollToCursor();
+ return true;
+ }
+ } else if (event == Ion::Events::Toolbox
+ || event == Ion::Events::Var
+ || event == Ion::Events::Shift
+ || event == Ion::Events::Alpha
+ || event == Ion::Events::OnOff)
+ {
+ } else if(event == Ion::Events::Up
+ || event == Ion::Events::Down)
+ {
+ cycleAutocompletion(event == Ion::Events::Down);
+ return true;
+ } else {
+ removeAutocompletion();
+ m_contentView.reloadRectFromPosition(m_contentView.cursorLocation(), false);
+ if (event == Ion::Events::Back) {
+ // Do not process the event more
+ return true;
+ }
+ }
+ }
+ bool result = TextArea::handleEvent(event);
+ if (event == Ion::Events::Backspace && !m_contentView.isAutocompleting() && selectionIsEmpty()) {
+ /* We want to add autocompletion when we are editing a word (after adding or
+ * deleting text). So if nothing is selected, we add the autocompletion if
+ * the event is backspace, as autocompletion has already been added if the
+ * event added text, in handleEventWithText. */
+ addAutocompletion();
+ }
+ return result;
+}
+
+bool PythonTextArea::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) {
+ if (*text == 0) {
+ return false;
+ }
+ if (m_contentView.isAutocompleting()) {
+ removeAutocompletion();
+ }
+ bool result = TextArea::handleEventWithText(text, indentation, forceCursorRightOfText);
+ addAutocompletion();
+ return result;
+}
+
+void PythonTextArea::removeAutocompletion() {
+ assert(m_contentView.isAutocompleting());
+ removeAutocompletionText();
+ m_contentView.setAutocompleting(false);
+}
+
+void PythonTextArea::removeAutocompletionText() {
+ assert(m_contentView.isAutocompleting());
+ assert(m_contentView.autocompletionEnd() != nullptr);
+ const char * autocompleteStart = m_contentView.cursorLocation();
+ const char * autocompleteEnd = m_contentView.autocompletionEnd();
+ assert(autocompleteEnd != nullptr && autocompleteEnd > autocompleteStart);
+ m_contentView.removeText(autocompleteStart, autocompleteEnd);
+}
+
+void PythonTextArea::addAutocompletion() {
+ assert(!m_contentView.isAutocompleting());
+ const char * autocompletionTokenBeginning = nullptr;
+ const char * autocompletionLocation = const_cast(cursorLocation());
+ m_autocompletionResultIndex = 0;
+ if (autocompletionType(autocompletionLocation, &autocompletionTokenBeginning) != AutocompletionType::EndOfIdentifier) {
+ // The cursor is not at the end of an identifier.
+ return;
+ }
+
+ // First load variables and functions that complete the textToAutocomplete
+ const int scriptIndex = m_contentView.pythonDelegate()->menuController()->editedScriptIndex();
+ m_contentView.pythonDelegate()->variableBoxController()->loadFunctionsAndVariables(scriptIndex, autocompletionTokenBeginning, autocompletionLocation - autocompletionTokenBeginning);
+
+ if (addAutocompletionTextAtIndex(0)) {
+ m_contentView.setAutocompleting(true);
+ }
+}
+
+bool PythonTextArea::addAutocompletionTextAtIndex(int nextIndex, int * currentIndexToUpdate) {
+ // The variable box should be loaded at this point
+ const char * autocompletionTokenBeginning = nullptr;
+ const char * autocompletionLocation = const_cast(cursorLocation());
+ AutocompletionType type = autocompletionType(autocompletionLocation, &autocompletionTokenBeginning); // Done to get autocompletionTokenBeginning
+ assert(type == AutocompletionType::EndOfIdentifier);
+ (void)type; // Silence warnings
+ VariableBoxController * varBox = m_contentView.pythonDelegate()->variableBoxController();
+ int textToInsertLength = 0;
+ bool addParentheses = false;
+ const char * textToInsert = varBox->autocompletionAlternativeAtIndex(autocompletionLocation - autocompletionTokenBeginning, &textToInsertLength, &addParentheses, nextIndex, currentIndexToUpdate);
+
+ if (textToInsert == nullptr) {
+ return false;
+ }
+
+ if (textToInsertLength > 0) {
+ // Try to insert the text (this might fail if the buffer is full)
+ if (!m_contentView.insertTextAtLocation(textToInsert, const_cast(autocompletionLocation), textToInsertLength)) {
+ return false;
+ }
+ autocompletionLocation += textToInsertLength;
+ m_contentView.setAutocompletionEnd(autocompletionLocation);
+ }
+
+ // Try to insert the parentheses if needed
+ const char * parentheses = ScriptNodeCell::k_parentheses;
+ constexpr int parenthesesLength = 2;
+ assert(strlen(parentheses) == parenthesesLength);
+ /* If couldInsertText is false, we should not try to add the parentheses as
+ * there was already not enough space to add the autocompletion. */
+ if (addParentheses && m_contentView.insertTextAtLocation(parentheses, const_cast(autocompletionLocation), parenthesesLength)) {
+ m_contentView.setAutocompleting(true);
+ m_contentView.setAutocompletionEnd(autocompletionLocation + parenthesesLength);
+ }
+ return true;
+}
+
+void PythonTextArea::cycleAutocompletion(bool downwards) {
+ assert(m_contentView.isAutocompleting());
+ removeAutocompletionText();
+ addAutocompletionTextAtIndex(m_autocompletionResultIndex + (downwards ? 1 : -1), &m_autocompletionResultIndex);
+}
+
+void PythonTextArea::acceptAutocompletion(bool moveCursorToEndOfAutocompletion) {
+ assert(m_contentView.isAutocompleting());
+
+ // Save the cursor location
+ const char * previousCursorLocation = cursorLocation();
+
+ removeAutocompletion();
+
+ m_contentView.pythonDelegate()->variableBoxController()->setSender(this);
+ m_contentView.pythonDelegate()->variableBoxController()->insertAutocompletionResultAtIndex(m_autocompletionResultIndex);
+
+ // insertAutocompletionResultAtIndex already added the autocompletion
+
+ // If we did not want to move the cursor, restore its position.
+ if (!moveCursorToEndOfAutocompletion) {
+ setCursorLocation(previousCursorLocation);
+ }
+}
+
}
diff --git a/apps/code/python_text_area.h b/apps/code/python_text_area.h
index 149d6bfd0..c1a64db5f 100644
--- a/apps/code/python_text_area.h
+++ b/apps/code/python_text_area.h
@@ -9,21 +9,46 @@ class App;
class PythonTextArea : public TextArea {
public:
+ enum class AutocompletionType : uint8_t {
+ EndOfIdentifier,
+ MiddleOfIdentifier,
+ NoIdentifier
+ };
PythonTextArea(Responder * parentResponder, App * pythonDelegate, const KDFont * font) :
TextArea(parentResponder, &m_contentView, font),
- m_contentView(pythonDelegate, font)
+ m_contentView(pythonDelegate, font),
+ m_autocompletionResultIndex(0)
{
}
void loadSyntaxHighlighter() { m_contentView.loadSyntaxHighlighter(); }
void unloadSyntaxHighlighter() { m_contentView.unloadSyntaxHighlighter(); }
+ bool handleEvent(Ion::Events::Event event) override;
+ bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override;
+ /* autocompletionType returns:
+ * - EndOfIdentifier if there is currently autocompletion, or if the cursor is
+ * at the end of an identifier,
+ * - MiddleOfIdentifier is the cursor is in the middle of an identifier,
+ * - No identifier otherwise.
+ * The autocompletionLocation can be provided with autocompletionLocation, or
+ * retreived with autocompletionLocationBeginning and autocompletionLocationEnd. */
+ AutocompletionType autocompletionType(const char * autocompletionLocation = nullptr, const char ** autocompletionLocationBeginning = nullptr, const char ** autocompletionLocationEnd = nullptr) const;
+ bool isAutocompleting() const { return m_contentView.isAutocompleting(); }
protected:
class ContentView : public TextArea::ContentView {
public:
ContentView(App * pythonDelegate, const KDFont * font) :
TextArea::ContentView(font),
- m_pythonDelegate(pythonDelegate)
+ m_pythonDelegate(pythonDelegate),
+ m_autocomplete(false),
+ m_autocompletionEnd(nullptr)
{
}
+ App * pythonDelegate() { return m_pythonDelegate; }
+ void setAutocompleting(bool autocomplete) { m_autocomplete = autocomplete; }
+ bool isAutocompleting() const { return m_autocomplete; }
+ const char * autocompletionEnd() const { assert(m_autocomplete); return m_autocompletionEnd; }
+ void setAutocompletionEnd(const char * end) { m_autocompletionEnd = end; }
+ const char * textToAutocomplete() const;
void loadSyntaxHighlighter();
void unloadSyntaxHighlighter();
void clearRect(KDContext * ctx, KDRect rect) const override;
@@ -31,10 +56,19 @@ protected:
KDRect dirtyRectFromPosition(const char * position, bool includeFollowingLines) const override;
private:
App * m_pythonDelegate;
+ bool m_autocomplete;
+ const char * m_autocompletionEnd;
};
private:
+ void removeAutocompletion();
+ void removeAutocompletionText(); // Just removes the suggested text, not the autocompletion mode
+ void addAutocompletion();
+ bool addAutocompletionTextAtIndex(int nextIndex, int * currentIndexToUpdate = nullptr); // Assumes the var box is already loaded
+ void cycleAutocompletion(bool downwards);
+ void acceptAutocompletion(bool moveCursorToEndOfAutocompletion);
const ContentView * nonEditableContentView() const override { return &m_contentView; }
ContentView m_contentView;
+ int m_autocompletionResultIndex;
};
}
diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp
index 5043c7f7f..bcd5ec925 100644
--- a/apps/code/python_toolbox.cpp
+++ b/apps/code/python_toolbox.cpp
@@ -120,7 +120,18 @@ const ToolboxMessageTree MatplotlibPyplotModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPlot, I18n::Message::PythonPlot),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScatter, I18n::Message::PythonScatter),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandShow, I18n::Message::PythonShow),
- ToolboxMessageTree::Leaf(I18n::Message::PythonCommandText, I18n::Message::PythonText)
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandText, I18n::Message::PythonText),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false)
};
const ToolboxMessageTree TurtleModuleChildren[] = {
@@ -141,21 +152,23 @@ const ToolboxMessageTree TurtleModuleChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPenup, I18n::Message::PythonTurtlePenup, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPensize, I18n::Message::PythonTurtlePensize),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandIsdown, I18n::Message::PythonTurtleIsdown, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWrite, I18n::Message::PythonTurtleWrite),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandReset, I18n::Message::PythonTurtleReset, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor, false, I18n::Message::PythonTurtleCommandColorWithoutArg),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandYellow, I18n::Message::PythonTurtleYellow, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBrown, I18n::Message::PythonTurtleBrown, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlack, I18n::Message::PythonTurtleBlack, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWhite, I18n::Message::PythonTurtleWhite, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPink, I18n::Message::PythonTurtlePink, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandOrange, I18n::Message::PythonTurtleOrange, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPurple, I18n::Message::PythonTurtlePurple, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGrey, I18n::Message::PythonTurtleGrey, false)
+ ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false)
};
const ToolboxMessageTree RandomModuleChildren[] = {
@@ -277,14 +290,15 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBackward, I18n::Message::PythonTurtleBackward),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandBar, I18n::Message::PythonBar),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandBin, I18n::Message::PythonBin),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlack, I18n::Message::PythonTurtleBlack, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBrown, I18n::Message::PythonTurtleBrown, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlack, I18n::Message::PythonColorBlack, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBlue, I18n::Message::PythonColorBlue, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorBrown, I18n::Message::PythonColorBrown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCeil, I18n::Message::PythonCeil),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandChoice, I18n::Message::PythonChoice),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandCircle, I18n::Message::PythonTurtleCircle),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandComplex, I18n::Message::PythonComplex),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCopySign, I18n::Message::PythonCopySign),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCos, I18n::Message::PythonCos),
@@ -317,8 +331,8 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetrandbits, I18n::Message::PythonGetrandbits),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGoto, I18n::Message::PythonTurtleGoto),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGrey, I18n::Message::PythonTurtleGrey, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGreen, I18n::Message::PythonColorGreen, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGrey, I18n::Message::PythonColorGrey, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGrid, I18n::Message::PythonGrid),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHeading, I18n::Message::PythonTurtleHeading, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandHex, I18n::Message::PythonHex),
@@ -364,19 +378,19 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandModf, I18n::Message::PythonModf),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandMonotonic, I18n::Message::PythonMonotonic, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandOct, I18n::Message::PythonOct),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandOrange, I18n::Message::PythonTurtleOrange, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorOrange, I18n::Message::PythonColorOrange, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPendown, I18n::Message::PythonTurtlePendown, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPenup, I18n::Message::PythonTurtlePenup, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPensize, I18n::Message::PythonTurtlePensize),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPhase, I18n::Message::PythonPhase),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantPi, I18n::Message::PythonConstantPi, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPink, I18n::Message::PythonTurtlePink, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPink, I18n::Message::PythonColorPink, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPolar, I18n::Message::PythonPolar),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPosition, I18n::Message::PythonTurtlePosition, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPower, I18n::Message::PythonPower),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPlot, I18n::Message::PythonPlot),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandPrint, I18n::Message::PythonPrint),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandPurple, I18n::Message::PythonTurtlePurple, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorPurple, I18n::Message::PythonColorPurple, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRadians, I18n::Message::PythonRadians),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRandint, I18n::Message::PythonRandint),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRandom, I18n::Message::PythonRandom, false),
@@ -385,7 +399,7 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRangeStartStop, I18n::Message::PythonRangeStartStop),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRangeStop, I18n::Message::PythonRangeStop),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRect, I18n::Message::PythonRect),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorRed, I18n::Message::PythonColorRed, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandReset, I18n::Message::PythonTurtleReset, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRight, I18n::Message::PythonTurtleRight),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandRound, I18n::Message::PythonRound),
@@ -409,8 +423,9 @@ const ToolboxMessageTree catalogChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTrunc, I18n::Message::PythonTrunc),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandTurtleFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandTurtleFunctionWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandUniform, I18n::Message::PythonUniform),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWhite, I18n::Message::PythonTurtleWhite, false),
- ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandYellow, I18n::Message::PythonTurtleYellow, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorWhite, I18n::Message::PythonColorWhite, false),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandWrite, I18n::Message::PythonTurtleWrite),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorYellow, I18n::Message::PythonColorYellow, false),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImag, I18n::Message::PythonImag, false, I18n::Message::PythonCommandImagWithoutArg),
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReal, I18n::Message::PythonReal, false, I18n::Message::PythonCommandRealWithoutArg)
};
@@ -420,11 +435,39 @@ const ToolboxMessageTree functionsChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::PythonCommandReturn, I18n::Message::Default)
};
+const ToolboxMessageTree fileChildren[] {
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileOpen, I18n::Message::PythonFileOpen, false, I18n::Message::PythonCommandFileOpenWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClose, I18n::Message::PythonFileClose, false, I18n::Message::PythonCommandFileCloseWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileClosed, I18n::Message::PythonFileClosed, false, I18n::Message::PythonCommandFileClosedWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileMode, I18n::Message::PythonFileMode, false, I18n::Message::PythonCommandFileModeWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileName, I18n::Message::PythonFileName, false, I18n::Message::PythonCommandFileNameWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileRead, I18n::Message::PythonFileRead, false, I18n::Message::PythonCommandFileReadWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadable, I18n::Message::PythonFileReadable, false, I18n::Message::PythonCommandFileReadableWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadline, I18n::Message::PythonFileReadline, false, I18n::Message::PythonCommandFileReadlineWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileReadlines, I18n::Message::PythonFileReadlines, false, I18n::Message::PythonCommandFileReadlinesWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeek, I18n::Message::PythonFileSeek, false, I18n::Message::PythonCommandFileSeekWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileSeekable, I18n::Message::PythonFileSeekable, false, I18n::Message::PythonCommandFileSeekableWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTell, I18n::Message::PythonFileTell, false, I18n::Message::PythonCommandFileTellWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileTruncate, I18n::Message::PythonFileTruncate, false, I18n::Message::PythonCommandFileTruncateWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWrite, I18n::Message::PythonFileWrite, false, I18n::Message::PythonCommandFileWriteWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritable, I18n::Message::PythonFileWritable, false, I18n::Message::PythonCommandFileWritableWithoutArg),
+ ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFileWritelines, I18n::Message::PythonFileWritelines, false, I18n::Message::PythonCommandFileWritelinesWithoutArg),
+};
+
+const ToolboxMessageTree exceptionsChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1Error),
+ ToolboxMessageTree::Leaf(I18n::Message::TryExcept1ErrorElseWithArg, I18n::Message::Default, false, I18n::Message::TryExcept1ErrorElse),
+ ToolboxMessageTree::Leaf(I18n::Message::TryExcept2ErrorWithArg, I18n::Message::Default, false, I18n::Message::TryExcept2Error),
+ ToolboxMessageTree::Leaf(I18n::Message::WithInstructionWithArg, I18n::Message::Default, false, I18n::Message::WithInstruction),
+};
+
const ToolboxMessageTree menu[] = {
ToolboxMessageTree::Node(I18n::Message::LoopsAndTests, loopsAndTestsChildren),
ToolboxMessageTree::Node(I18n::Message::Modules, modulesChildren),
ToolboxMessageTree::Node(I18n::Message::Catalog, catalogChildren),
- ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren)
+ ToolboxMessageTree::Node(I18n::Message::Functions, functionsChildren),
+ ToolboxMessageTree::Node(I18n::Message::Files, fileChildren),
+ ToolboxMessageTree::Node(I18n::Message::Exceptions, exceptionsChildren)
};
const ToolboxMessageTree toolboxModel = ToolboxMessageTree::Node(I18n::Message::Toolbox, menu);
@@ -435,6 +478,20 @@ PythonToolbox::PythonToolbox() :
{
}
+const ToolboxMessageTree * PythonToolbox::moduleChildren(const char * name, int * numberOfNodes) const {
+ for (ToolboxMessageTree t : modulesChildren) {
+ if (strcmp(I18n::translate(t.label()), name) == 0) {
+ const int childrenCount = t.numberOfChildren();
+ if (numberOfNodes != nullptr) {
+ *numberOfNodes = childrenCount;
+ }
+ assert(childrenCount > 0);
+ return static_cast(t.childAtIndex(0));
+ }
+ }
+ return nullptr;
+}
+
bool PythonToolbox::handleEvent(Ion::Events::Event event) {
if (Toolbox::handleEvent(event)) {
return true;
@@ -450,7 +507,7 @@ bool PythonToolbox::handleEvent(Ion::Events::Event event) {
}
KDCoordinate PythonToolbox::rowHeight(int j) {
- if (typeAtLocation(0, j) == Toolbox::LeafCellType && m_messageTreeModel->label() == I18n::Message::IfStatementMenu) {
+ if (typeAtLocation(0, j) == Toolbox::LeafCellType && (m_messageTreeModel->label() == I18n::Message::IfStatementMenu || m_messageTreeModel->label() == I18n::Message::Exceptions)) {
/* To get the exact height needed for each cell, we have to compute its
* text size, which means scan the text char by char to look for '\n'
* chars. This is very costly and ruins the speed performance when
@@ -460,7 +517,7 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
* We thus decided to compute the real height only for the ifStatement
* children of the toolbox, which is the only menu that has special height
* rows. */
- const ToolboxMessageTree * messageTree = static_cast(m_messageTreeModel->children(j));
+ const ToolboxMessageTree * messageTree = static_cast(m_messageTreeModel->childAtIndex(j));
return k_font->stringSize(I18n::translate(messageTree->label())).height() + 2*Metric::TableCellVerticalMargin + (messageTree->text() == I18n::Message::Default ? 0 : Toolbox::rowHeight(j));
}
return Toolbox::rowHeight(j);
@@ -468,7 +525,7 @@ KDCoordinate PythonToolbox::rowHeight(int j) {
bool PythonToolbox::selectLeaf(int selectedRow) {
m_selectableTableView.deselectTable();
- ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
+ ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->childAtIndex(selectedRow);
const char * editedText = I18n::translate(node->insertedText());
// strippedEditedText array needs to be in the same scope as editedText
char strippedEditedText[k_maxMessageSize];
@@ -509,7 +566,7 @@ void PythonToolbox::scrollToLetter(char letter) {
char lowerLetter = tolower(letter);
int index = -1;
for (int i = 0; i < m_messageTreeModel->numberOfChildren(); i++) {
- char l = tolower(I18n::translate(m_messageTreeModel->children(i)->label())[0]);
+ char l = tolower(I18n::translate(m_messageTreeModel->childAtIndex(i)->label())[0]);
if (l == lowerLetter) {
index = i;
break;
diff --git a/apps/code/python_toolbox.h b/apps/code/python_toolbox.h
index d3e9d7f21..bfe682058 100644
--- a/apps/code/python_toolbox.h
+++ b/apps/code/python_toolbox.h
@@ -10,7 +10,11 @@ namespace Code {
class PythonToolbox : public Toolbox {
public:
+ // PythonToolbox
PythonToolbox();
+ const ToolboxMessageTree * moduleChildren(const char * name, int * numberOfNodes) const;
+
+ // Toolbox
bool handleEvent(Ion::Events::Event event) override;
const ToolboxMessageTree * rootModel() const override;
protected:
diff --git a/apps/code/script.cpp b/apps/code/script.cpp
index 1b33fd0e7..4b39b3452 100644
--- a/apps/code/script.cpp
+++ b/apps/code/script.cpp
@@ -65,22 +65,54 @@ bool Script::nameCompliant(const char * name) {
return false;
}
-bool Script::importationStatus() const {
- assert(!isNull());
- Data d = value();
- return (((char *)d.buffer)[0] == 1);
+uint8_t * StatusFromData(Script::Data d) {
+ return const_cast(static_cast(d.buffer));
}
-void Script::toggleImportationStatus() {
+bool Script::autoImportationStatus() const {
+ return getStatutBit(k_autoImportationStatusMask);
+}
+
+void Script::toggleAutoimportationStatus() {
+ assert(!isNull());
Data d = value();
- ((char *)d.buffer)[0] = (((char *)d.buffer)[0] == 1 ? 0 : 1);
+ *StatusFromData(d) ^= k_autoImportationStatusMask;
setValue(d);
}
-const char * Script::scriptContent() const {
+const char * Script::content() const {
+ Data d = value();
+ return ((const char *)d.buffer) + StatusSize();
+}
+
+bool Script::fetchedFromConsole() const {
+ return getStatutBit(k_fetchedFromConsoleMask);
+}
+
+void Script::setFetchedFromConsole(bool fetched) {
+ setStatutBit(k_fetchedFromConsoleMask, k_fetchedFromConsoleOffset, fetched);
+}
+
+bool Script::fetchedForVariableBox() const {
+ return getStatutBit(k_fetchedForVariableBoxMask);
+}
+
+void Script::setFetchedForVariableBox(bool fetched) {
+ setStatutBit(k_fetchedForVariableBoxMask, k_fetchedForVariableBoxOffset, fetched);
+}
+
+bool Script::getStatutBit(uint8_t mask) const {
assert(!isNull());
Data d = value();
- return (const char *)d.buffer + k_importationStatusSize;
+ return ((*StatusFromData(d)) & mask) != 0;
+}
+
+void Script::setStatutBit(uint8_t mask, uint8_t offset, bool statusBit) {
+ assert(!isNull());
+ Data d = value();
+ uint8_t * status = StatusFromData(d);
+ *status = ((*status) & ~mask) | (static_cast(statusBit) << offset); //TODO Create and use a bit operations library
+ setValue(d);
}
}
diff --git a/apps/code/script.h b/apps/code/script.h
index fc10352f3..9d2df78c6 100644
--- a/apps/code/script.h
+++ b/apps/code/script.h
@@ -5,16 +5,36 @@
namespace Code {
-/* Record : | Total Size | Name | Body |
- * Script: | AutoImportationStatus | Content |*/
+/* Record: | Size | Name | Body |
+ * Script: | | | Status | Content |
+ *
+ *
+ * |FetchedForVariableBoxBit
+ * Status is one byte long: xxxxxxxx
+ * ^ ^
+ * FetchedFromConsoleBit AutoImportationBit
+ *
+ * AutoImportationBit is 1 if the script should be auto imported when the
+ * console opens.
+ *
+ * FetchedFromConsoleBit is 1 if its content has been fetched from the console,
+ * so we can retrieve the correct variables afterwards in the variable box.
+ *
+ * FetchedForVariableBoxBit is used to prevent circular importation problems,
+ * such as scriptA importing scriptB, which imports scriptA. Once we get the
+ * variables from a script to put them in the variable box, we switch the bit to
+ * 1 and won't reload it afterwards. */
class Script : public Ion::Storage::Record {
private:
// Default script names are chosen between script1 and script99
static constexpr int k_maxNumberOfDefaultScriptNames = 99;
static constexpr int k_defaultScriptNameNumberMaxSize = 2; // Numbers from 1 to 99 have 2 digits max
+
+ // See the comment at the beginning of the file
+ static constexpr size_t k_statusSize = 1;
+
public:
- static constexpr size_t k_importationStatusSize = 1;
static constexpr int k_defaultScriptNameMaxSize = 6 + k_defaultScriptNameNumberMaxSize + 1;
/* 6 = strlen("script")
* k_defaultScriptNameNumberMaxSize = maxLength of integers between 1 and 99
@@ -22,11 +42,29 @@ public:
static bool DefaultName(char buffer[], size_t bufferSize);
static bool nameCompliant(const char * name);
+ static constexpr size_t StatusSize() { return k_statusSize; }
- Script(Ion::Storage::Record r) : Record(r) {}
- bool importationStatus() const;
- void toggleImportationStatus();
- const char * scriptContent() const;
+
+ Script(Ion::Storage::Record r = Ion::Storage::Record()) : Record(r) {}
+ bool autoImportationStatus() const;
+ void toggleAutoimportationStatus();
+ const char * content() const;
+
+ /* Fetched status */
+ bool fetchedFromConsole() const;
+ void setFetchedFromConsole(bool fetched);
+ bool fetchedForVariableBox() const;
+ void setFetchedForVariableBox(bool fetched);
+
+private:
+ static constexpr uint8_t k_autoImportationStatusMask = 0b1;
+ static constexpr uint8_t k_fetchedForVariableBoxOffset = 7;
+ static constexpr uint8_t k_fetchedFromConsoleOffset = 6;
+ static constexpr uint8_t k_fetchedForVariableBoxMask = 0b1 << k_fetchedForVariableBoxOffset;
+ static constexpr uint8_t k_fetchedFromConsoleMask = 0b1 << k_fetchedFromConsoleOffset;
+
+ bool getStatutBit(uint8_t offset) const;
+ void setStatutBit(uint8_t mask, uint8_t offset, bool value);
};
}
diff --git a/apps/code/script_node.h b/apps/code/script_node.h
index 1bac0f81d..6f2bd49f0 100644
--- a/apps/code/script_node.h
+++ b/apps/code/script_node.h
@@ -1,33 +1,35 @@
#ifndef CODE_SCRIPT_NODE_H
#define CODE_SCRIPT_NODE_H
+#include
#include
namespace Code {
class ScriptNode {
public:
- enum class Type {
- Function = 0,
- Variable = 1
+ enum class Type : bool {
+ WithoutParentheses,
+ WithParentheses
};
- ScriptNode() :
- m_type(Type::Function), m_name(nullptr), m_scriptIndex(0) {}
- static ScriptNode FunctionNode(const char * name, uint16_t scriptIndex) {
- return ScriptNode(Type::Function, name, scriptIndex);
- }
- static ScriptNode VariableNode(const char * name, uint16_t scriptIndex) {
- return ScriptNode(Type::Variable, name, scriptIndex);
- }
+ ScriptNode(Type type = Type::WithoutParentheses, const char * name = nullptr, int nameLength = -1, const char * nodeSourceName = nullptr, const char * description = nullptr) :
+ m_type(type),
+ m_name(name),
+ m_nodeSourceName(nodeSourceName),
+ m_description(description),
+ m_nameLength(nameLength)
+ {}
Type type() const { return m_type; }
const char * name() const { return m_name; }
- uint16_t scriptIndex() const { return m_scriptIndex; }
+ int nameLength() const { return static_cast(m_nameLength); }
+ const char * nodeSourceName() const { return m_nodeSourceName; }
+ const char * description() const { return m_description; }
private:
- ScriptNode(Type type, const char * name, uint16_t scriptIndex) :
- m_type(type), m_name(name), m_scriptIndex(scriptIndex) {}
Type m_type;
const char * m_name;
- uint16_t m_scriptIndex;
+ const char * m_nodeSourceName;
+ const char * m_description;
+ size_t m_nameLength;
};
}
diff --git a/apps/code/script_node_cell.cpp b/apps/code/script_node_cell.cpp
index a3e265bf2..1c8580a44 100644
--- a/apps/code/script_node_cell.cpp
+++ b/apps/code/script_node_cell.cpp
@@ -7,47 +7,51 @@ namespace Code {
constexpr char ScriptNodeCell::k_parentheses[];
constexpr char ScriptNodeCell::k_parenthesesWithEmpty[];
-ScriptNodeCell::ScriptNodeView::ScriptNodeView() :
- HighlightCell(),
- m_scriptNode(nullptr),
- m_scriptStore(nullptr)
-{
-}
-
-void ScriptNodeCell::ScriptNodeView::setScriptNode(ScriptNode * scriptNode) {
- m_scriptNode = scriptNode;
-}
-
-void ScriptNodeCell::ScriptNodeView::setScriptStore(ScriptStore * scriptStore) {
- m_scriptStore = scriptStore;
-}
-
void ScriptNodeCell::ScriptNodeView::drawRect(KDContext * ctx, KDRect rect) const {
- ctx->drawString(m_scriptNode->name(), KDPoint(0, Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
- KDSize nameSize = k_font->stringSize(m_scriptNode->name());
- if (m_scriptNode->type() == ScriptNode::Type::Function) {
- ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), Metric::TableCellVerticalMargin), k_font, Palette::CodeText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
+ const KDColor backgroundColor = isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground;
+
+ // If it exists, draw the description name.
+ const char * descriptionName = m_scriptNode->description();
+ if (descriptionName != nullptr) {
+ ctx->drawString(descriptionName, KDPoint(0, m_frame.height() - k_bottomMargin - k_font->glyphSize().height()), k_font, Palette::GreyDark, backgroundColor);
+ }
+
+ // Draw the node name
+ const char * nodeName = m_scriptNode->name();
+ const int nodeNameLength = m_scriptNode->nameLength();
+ KDSize nameSize = k_font->stringSize(nodeName, nodeNameLength);
+ const KDCoordinate nodeNameY = k_topMargin;
+ ctx->drawString(nodeName, KDPoint(0, nodeNameY), k_font, KDColorBlack, backgroundColor, nodeNameLength);
+ // If it is needed, draw the parentheses
+ if (m_scriptNode->type() == ScriptNode::Type::WithParentheses) {
+ ctx->drawString(ScriptNodeCell::k_parentheses, KDPoint(nameSize.width(), nodeNameY), k_font, KDColorBlack, backgroundColor);
+ }
+
+ /* If it exists, draw the source name. If it did not fit, we would have put
+ * nullptr at the node creation. */
+ const char * sourceName = m_scriptNode->nodeSourceName();
+ if (sourceName != nullptr) {
+ KDSize sourceNameSize = k_font->stringSize(sourceName);
+ ctx->drawString(sourceName, KDPoint(m_frame.width() - sourceNameSize.width(), nodeNameY), k_font, Palette::CodeText, backgroundColor);
}
- ctx->drawString(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName(), KDPoint(0, Metric::TableCellVerticalMargin + nameSize.height() + k_verticalMargin), k_font, Palette::SecondaryText, isHighlighted()? Palette::CodeBackgroundSelected : Palette::CodeBackground);
}
KDSize ScriptNodeCell::ScriptNodeView::minimalSizeForOptimalDisplay() const {
if (m_scriptNode->name() == nullptr) {
return KDSizeZero;
}
- KDSize size1 = k_font->stringSize(m_scriptNode->name());
- KDSize size2 = k_font->stringSize(m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName());
- KDSize size3 = KDSizeZero;
- if (m_scriptNode->type() == ScriptNode::Type::Function) {
- size3 = k_font->stringSize(ScriptNodeCell::k_parentheses);
- }
- return KDSize(size1.width() + size3.width() > size2.width() ? size1.width() + size3.width() : size2.width(), Metric::TableCellVerticalMargin + size1.width() + k_verticalMargin + size2.width());
+ return KDSize(
+ k_optimalWidth,
+ m_scriptNode->description() == nullptr ? k_simpleItemHeight : k_complexItemHeight);
}
-ScriptNodeCell::ScriptNodeCell() :
- TableCell(),
- m_scriptNodeView()
-{
+bool ScriptNodeCell::CanDisplayNameAndSource(int nameLength, const char * source) {
+ if (source == nullptr) {
+ return true;
+ }
+ assert(nameLength > 0);
+ const KDFont * font = ScriptNodeView::k_font;
+ return font->glyphSize().width()*(nameLength + 1) + font->stringSize(source).width() <= ScriptNodeView::k_optimalWidth; // + 1 for the separating space
}
void ScriptNodeCell::setScriptNode(ScriptNode * scriptNode) {
@@ -55,10 +59,6 @@ void ScriptNodeCell::setScriptNode(ScriptNode * scriptNode) {
reloadCell();
}
-void ScriptNodeCell::setScriptStore(ScriptStore * scriptStore) {
- m_scriptNodeView.setScriptStore(scriptStore);
-}
-
void ScriptNodeCell::setHighlighted(bool highlight) {
TableCell::setHighlighted(highlight);
m_scriptNodeView.setHighlighted(highlight);
diff --git a/apps/code/script_node_cell.h b/apps/code/script_node_cell.h
index c4d7651b7..672a9fb44 100644
--- a/apps/code/script_node_cell.h
+++ b/apps/code/script_node_cell.h
@@ -10,9 +10,18 @@ namespace Code {
class ScriptNodeCell : public TableCell {
public:
- ScriptNodeCell();
+ static_assert('\x11' == UCodePointEmpty, "Unicode error");
+ constexpr static char k_parentheses[] = "()";
+ constexpr static char k_parenthesesWithEmpty[] = "(\x11)";
+ constexpr static KDCoordinate k_simpleItemHeight = 27;
+ constexpr static KDCoordinate k_complexItemHeight = 42;
+
+ ScriptNodeCell() :
+ TableCell(),
+ m_scriptNodeView()
+ {}
void setScriptNode(ScriptNode * node);
- void setScriptStore(ScriptStore * scriptStore);
+ static bool CanDisplayNameAndSource(int nameLength, const char * source);
/* TableCell */
View * labelView() const override { return const_cast(static_cast(&m_scriptNodeView)); }
@@ -22,26 +31,25 @@ public:
void reloadCell() override;
const char * text() const override { return m_scriptNodeView.text(); }
- static_assert('\x11' == UCodePointEmpty, "Unicode error");
- constexpr static char k_parentheses[] = "()";
- constexpr static char k_parenthesesWithEmpty[] = "(\x11)";
-
protected:
class ScriptNodeView : public HighlightCell {
public:
- ScriptNodeView();
- void setScriptNode(ScriptNode * scriptNode);
- void setScriptStore(ScriptStore * scriptStore);
+ constexpr static const KDFont * k_font = KDFont::SmallFont;
+ constexpr static KDCoordinate k_optimalWidth = Ion::Display::Width - Metric::PopUpLeftMargin - Metric::PopUpRightMargin;
+ ScriptNodeView() :
+ HighlightCell(),
+ m_scriptNode(nullptr)
+ {}
+ void setScriptNode(ScriptNode * node) { m_scriptNode = node; }
void drawRect(KDContext * ctx, KDRect rect) const override;
virtual KDSize minimalSizeForOptimalDisplay() const override;
const char * text() const override {
- return m_scriptStore->scriptAtIndex(m_scriptNode->scriptIndex()).fullName();
+ return m_scriptNode->description();
}
private:
- constexpr static const KDFont * k_font = KDFont::SmallFont;
- constexpr static KDCoordinate k_verticalMargin = 7;
+ constexpr static KDCoordinate k_bottomMargin = 5;
+ constexpr static KDCoordinate k_topMargin = k_bottomMargin + k_separatorThickness;
ScriptNode * m_scriptNode;
- ScriptStore * m_scriptStore;
};
ScriptNodeView m_scriptNodeView;
};
diff --git a/apps/code/script_parameter_controller.cpp b/apps/code/script_parameter_controller.cpp
index 83bdc5f5c..1390fcd99 100644
--- a/apps/code/script_parameter_controller.cpp
+++ b/apps/code/script_parameter_controller.cpp
@@ -1,5 +1,6 @@
#include "script_parameter_controller.h"
#include "menu_controller.h"
+#include
namespace Code {
@@ -11,6 +12,7 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder
m_autoImportScript(I18n::Message::AutoImportScript),
m_deleteScript(I18n::Message::DeleteScript),
m_duplicateScript(I18n::Message::DuplicateScript),
+ m_size(I18n::Message::ScriptSize),
m_selectableTableView(this),
m_script(Ion::Storage::Record()),
m_menuController(menuController)
@@ -43,21 +45,27 @@ bool ScriptParameterController::handleEvent(Ion::Events::Event event) {
m_menuController->renameSelectedScript();
return true;
case 2:
- m_script.toggleImportationStatus();
+ m_script.toggleAutoimportationStatus();
m_selectableTableView.reloadData();
m_menuController->reloadConsole();
Container::activeApp()->setFirstResponder(&m_selectableTableView);
return true;
- case 3:
- dismissScriptParameterController();
- m_menuController->deleteScript(s);
- m_menuController->reloadConsole();
+ case 3:{
+ MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)m_selectableTableView.selectedCell();
+ m_sizedisplaypercent = !m_sizedisplaypercent;
+ GetScriptSize(myCell);
return true;
+ }
case 4:
dismissScriptParameterController();
m_menuController->duplicateScript(s);
m_menuController->reloadConsole();
return true;
+ case 5:
+ dismissScriptParameterController();
+ m_menuController->deleteScript(s);
+ m_menuController->reloadConsole();
+ return true;
default:
assert(false);
return false;
@@ -80,14 +88,43 @@ void ScriptParameterController::didBecomeFirstResponder() {
HighlightCell * ScriptParameterController::reusableCell(int index) {
assert(index >= 0);
assert(index < k_totalNumberOfCell);
- HighlightCell * cells[] = {&m_executeScript, &m_renameScript, &m_autoImportScript, &m_deleteScript, &m_duplicateScript};
+ HighlightCell * cells[] = {&m_executeScript, &m_renameScript, &m_autoImportScript, &m_size, &m_duplicateScript, &m_deleteScript};
return cells[index];
}
void ScriptParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (cell == &m_autoImportScript) {
SwitchView * switchView = (SwitchView *)m_autoImportScript.accessoryView();
- switchView->setState(m_script.importationStatus());
+ switchView->setState(m_script.autoImportationStatus());
+ } else if (cell == &m_size) {
+ MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)cell;
+ GetScriptSize(myCell);
+ myCell->setAccessoryFont(KDFont::SmallFont);
+ myCell->setAccessoryTextColor(Palette::SecondaryText);
+ }
+}
+
+void ScriptParameterController::GetScriptSize(MessageTableCellWithBuffer* myCell){
+ if(m_sizedisplaypercent){
+ char size[18];
+ int sizelen = Poincare::Integer((int)m_script.value().size).serialize(size, 6);
+ size[sizelen] = ' ';
+ size[sizelen+1] = 'o';
+ size[sizelen+2] = ' ';
+ size[sizelen+3] = '/';
+ size[sizelen+4] = ' ';
+ int sizelen2 = Poincare::Integer((int)Ion::Storage::k_storageSize).serialize(size+sizelen+5, 6) + sizelen + 5;
+ size[sizelen2] = ' ';
+ size[sizelen2+1] = 'o';
+ size[sizelen2+2] = '\0';
+ myCell->setAccessoryText(size);
+ }else{
+ char size[18];
+ int sizelen = Poincare::Integer((int)(((float)((int)m_script.value().size)/((int)Ion::Storage::k_storageSize)) * 100.f)).serialize(size, 3);
+ size[sizelen] = ' ';
+ size[sizelen+1] = '%';
+ size[sizelen+2] = '\0';
+ myCell->setAccessoryText(size);
}
}
diff --git a/apps/code/script_parameter_controller.h b/apps/code/script_parameter_controller.h
index 049049de8..e7065e733 100644
--- a/apps/code/script_parameter_controller.h
+++ b/apps/code/script_parameter_controller.h
@@ -31,7 +31,7 @@ public:
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
private:
- constexpr static int k_totalNumberOfCell = 5;
+ constexpr static int k_totalNumberOfCell = 6;
StackViewController * stackViewController();
I18n::Message m_pageTitle;
MessageTableCell m_executeScript;
@@ -39,9 +39,12 @@ private:
MessageTableCellWithSwitch m_autoImportScript;
MessageTableCell m_deleteScript;
MessageTableCell m_duplicateScript;
+ MessageTableCellWithBuffer m_size;
+ void GetScriptSize(MessageTableCellWithBuffer* myCell);
SelectableTableView m_selectableTableView;
Script m_script;
MenuController * m_menuController;
+ bool m_sizedisplaypercent = false;
};
}
diff --git a/apps/code/script_store.cpp b/apps/code/script_store.cpp
index e21047d7c..ebc358827 100644
--- a/apps/code/script_store.cpp
+++ b/apps/code/script_store.cpp
@@ -1,23 +1,14 @@
#include "script_store.h"
-#include "string.h"
-#include
-
-extern "C" {
-#include "py/lexer.h"
-#include "py/nlr.h"
-}
namespace Code {
constexpr char ScriptStore::k_scriptExtension[];
-
bool ScriptStore::ScriptNameIsFree(const char * baseName) {
- return Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(baseName, k_scriptExtension).isNull();
+ return ScriptBaseNamed(baseName).isNull();
}
-ScriptStore::ScriptStore()
-{
+ScriptStore::ScriptStore() {
addScriptFromTemplate(ScriptTemplate::Squares());
addScriptFromTemplate(ScriptTemplate::Parabola());
addScriptFromTemplate(ScriptTemplate::Mandelbrot());
@@ -34,118 +25,39 @@ bool ScriptStore::isFull() {
return Ion::Storage::sharedStorage()->availableSize() < k_fullFreeSpaceSizeLimit;
}
-void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction, ScanCallback storeVariable) {
- for (int scriptIndex = 0; scriptIndex < numberOfScripts(); scriptIndex++) {
- // Handle lexer or parser errors with nlr.
- nlr_buf_t nlr;
- if (nlr_push(&nlr) == 0) {
- const char * scriptContent = scriptAtIndex(scriptIndex).scriptContent();
- if (scriptContent == nullptr) {
- continue;
- }
- mp_lexer_t *lex = mp_lexer_new_from_str_len(0, scriptContent, strlen(scriptContent), false);
- mp_parse_tree_t parseTree = mp_parse(lex, MP_PARSE_FILE_INPUT);
- mp_parse_node_t pn = parseTree.root;
-
- if (!MP_PARSE_NODE_IS_STRUCT(pn)) {
- mp_parse_tree_clear(&parseTree);
- nlr_pop();
- continue;
- }
-
- mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
-
- // The script is only a single function definition.
- if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) == k_functionDefinitionParseNodeStructKind) {
- const char * id = structID(pns);
- if (id == nullptr) {
- continue;
- }
- storeFunction(context, id, scriptIndex);
- mp_parse_tree_clear(&parseTree);
- nlr_pop();
- continue;
- }
-
- // The script is only a single global variable definition.
- if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) == k_expressionStatementParseNodeStructKind) {
- const char * id = structID(pns);
- if (id == nullptr) {
- continue;
- }
- storeVariable(context, id, scriptIndex);
- mp_parse_tree_clear(&parseTree);
- nlr_pop();
- continue;
- }
-
- if (((uint)(MP_PARSE_NODE_STRUCT_KIND(pns))) != k_fileInput2ParseNodeStructKind) {
- // The script node is not of type "file_input_2", thus it will not have main
- // structures of the wanted type.
- mp_parse_tree_clear(&parseTree);
- nlr_pop();
- continue;
- }
-
- // Count the number of structs in child nodes.
-
- size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
- for (size_t i = 0; i < n; i++) {
- mp_parse_node_t child = pns->nodes[i];
- if (MP_PARSE_NODE_IS_STRUCT(child)) {
- mp_parse_node_struct_t *child_pns = (mp_parse_node_struct_t*)(child);
- if (((uint)(MP_PARSE_NODE_STRUCT_KIND(child_pns))) == k_functionDefinitionParseNodeStructKind) {
- const char * id = structID(child_pns);
- if (id == nullptr) {
- continue;
- }
- storeFunction(context, id, scriptIndex);
- } else if (((uint)(MP_PARSE_NODE_STRUCT_KIND(child_pns))) == k_expressionStatementParseNodeStructKind) {
- const char * id = structID(child_pns);
- if (id == nullptr) {
- continue;
- }
- storeVariable(context, id, scriptIndex);
- }
- }
- }
-
- mp_parse_tree_clear(&parseTree);
- nlr_pop();
- }
- }
-}
-
-const char * ScriptStore::contentOfScript(const char * name) {
- Script script = scriptNamed(name);
+const char * ScriptStore::contentOfScript(const char * name, bool markAsFetched) {
+ Script script = ScriptNamed(name);
if (script.isNull()) {
return nullptr;
}
- return script.scriptContent();
+ if (markAsFetched) {
+ script.setFetchedFromConsole(true);
+ }
+ return script.content();
+}
+
+void ScriptStore::clearVariableBoxFetchInformation() {
+ // TODO optimize fetches
+ const int scriptsCount = numberOfScripts();
+ for (int i = 0; i < scriptsCount; i++) {
+ scriptAtIndex(i).setFetchedForVariableBox(false);
+ }
+}
+
+void ScriptStore::clearConsoleFetchInformation() {
+ // TODO optimize fetches
+ const int scriptsCount = numberOfScripts();
+ for (int i = 0; i < scriptsCount; i++) {
+ scriptAtIndex(i).setFetchedFromConsole(false);
+ }
}
Script::ErrorStatus ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {
- size_t valueSize = strlen(scriptTemplate->content())+1+1;// scriptcontent size + 1 char for the importation status
+ size_t valueSize = Script::StatusSize() + strlen(scriptTemplate->content()) + 1; // (auto importation status + content fetched status) + scriptcontent size + null-terminating char
assert(Script::nameCompliant(scriptTemplate->name()));
Script::ErrorStatus err = Ion::Storage::sharedStorage()->createRecordWithFullName(scriptTemplate->name(), scriptTemplate->value(), valueSize);
assert(err != Script::ErrorStatus::NonCompliantName);
return err;
}
-const char * ScriptStore::structID(mp_parse_node_struct_t *structNode) {
- // Find the id child node, which stores the struct's name
- size_t childNodesCount = MP_PARSE_NODE_STRUCT_NUM_NODES(structNode);
- if (childNodesCount < 1) {
- return nullptr;
- }
- mp_parse_node_t child = structNode->nodes[0];
- if (MP_PARSE_NODE_IS_LEAF(child)
- && MP_PARSE_NODE_LEAF_KIND(child) == MP_PARSE_NODE_ID)
- {
- uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(child);
- return qstr_str(arg);
- }
- return nullptr;
-}
-
}
diff --git a/apps/code/script_store.h b/apps/code/script_store.h
index 46273c57d..ad9b59ff7 100644
--- a/apps/code/script_store.h
+++ b/apps/code/script_store.h
@@ -23,8 +23,11 @@ public:
Script scriptAtIndex(int index) {
return Script(Ion::Storage::sharedStorage()->recordWithExtensionAtIndex(k_scriptExtension, index));
}
- Script scriptNamed(const char * name) {
- return Script(Ion::Storage::sharedStorage()->recordNamed(name));
+ static Script ScriptNamed(const char * fullName) {
+ return Script(Ion::Storage::sharedStorage()->recordNamed(fullName));
+ }
+ static Script ScriptBaseNamed(const char * baseName) {
+ return Script(Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(baseName, k_scriptExtension));
}
int numberOfScripts() {
return Ion::Storage::sharedStorage()->numberOfRecordsWithExtension(k_scriptExtension);
@@ -35,12 +38,10 @@ public:
void deleteAllScripts();
bool isFull();
- /* Provide scripts content information */
- typedef void (* ScanCallback)(void * context, const char * p, int n);
- void scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction,ScanCallback storeVariable);
-
/* MicroPython::ScriptProvider */
- const char * contentOfScript(const char * name) override;
+ const char * contentOfScript(const char * name, bool markAsFetched) override;
+ void clearVariableBoxFetchInformation();
+ void clearConsoleFetchInformation();
Ion::Storage::Record::ErrorStatus addScriptFromTemplate(const ScriptTemplate * scriptTemplate);
private:
@@ -51,10 +52,6 @@ private:
* importation status (1 char), the default content "from math import *\n"
* (20 char) and 10 char of free space. */
static constexpr int k_fullFreeSpaceSizeLimit = sizeof(Ion::Storage::record_size_t)+Script::k_defaultScriptNameMaxSize+k_scriptExtensionLength+1+20+10;
- static constexpr size_t k_fileInput2ParseNodeStructKind = 1;
- static constexpr size_t k_functionDefinitionParseNodeStructKind = 3;
- static constexpr size_t k_expressionStatementParseNodeStructKind = 5;
- const char * structID(mp_parse_node_struct_t *structNode);
};
}
diff --git a/apps/code/script_template.h b/apps/code/script_template.h
index d0d048ab1..ec32e7052 100644
--- a/apps/code/script_template.h
+++ b/apps/code/script_template.h
@@ -1,6 +1,8 @@
#ifndef CODE_SCRIPT_TEMPLATE_H
#define CODE_SCRIPT_TEMPLATE_H
+#include "script.h"
+
namespace Code {
class ScriptTemplate {
@@ -12,11 +14,11 @@ public:
static const ScriptTemplate * Polynomial();
static const ScriptTemplate * Parabola();
const char * name() const { return m_name; }
- const char * content() const { return m_value+1; }
+ const char * content() const { return m_value + Script::StatusSize(); }
const char * value() const { return m_value; }
private:
const char * m_name;
- const char * m_value; // hold the 'importation status' flag concatenate with the script content
+ const char * m_value; // holds the 'importation status' and 'current importation status' flags concatenated with the script content
};
}
diff --git a/apps/code/test/variable_box_controller.cpp b/apps/code/test/variable_box_controller.cpp
new file mode 100644
index 000000000..2c7e4abfc
--- /dev/null
+++ b/apps/code/test/variable_box_controller.cpp
@@ -0,0 +1,70 @@
+#include
+#include "../script_store.h"
+#include "../variable_box_controller.h"
+#include
+
+using namespace Code;
+
+void assert_variables_are(const char * script, const char * nameToComplete, const char * * expectedVariables, int expectedVariablesCount) {
+ // Clean the store
+ ScriptStore store;
+ store.deleteAllScripts();
+
+ // Add the script
+ store.addNewScript();
+ constexpr int dataBufferSize = 500;
+ char dataBuffer[dataBufferSize];
+ Ion::Storage::Record::Data data = {
+ .buffer = &dataBuffer,
+ .size = dataBufferSize
+ };
+ strlcpy(dataBuffer, script, dataBufferSize);
+ constexpr int scriptIndex = 0;
+ store.scriptAtIndex(scriptIndex).setValue(data);
+
+ // Load the variable box
+ VariableBoxController varBox(&store);
+
+ const size_t nameToCompleteLength = strlen(nameToComplete);
+ varBox.loadFunctionsAndVariables(scriptIndex, nameToComplete, nameToCompleteLength);
+
+ // Compare the variables
+ int index = 0; // Index to make sure we are not cycling through the results
+ int textToInsertLength;
+ bool addParentheses;
+ for (int i = 0; i < expectedVariablesCount; i++) {
+ quiz_assert(i == index);
+ const char * autocompletionI = varBox.autocompletionAlternativeAtIndex(
+ nameToCompleteLength,
+ &textToInsertLength,
+ &addParentheses,
+ i,
+ &index);
+ quiz_assert(i == index); // If false, the autompletion has cycled: there are not as many results as expected
+ quiz_assert(strncmp(*(expectedVariables + i), autocompletionI - nameToCompleteLength, textToInsertLength + nameToCompleteLength) == 0);
+ index++;
+ }
+ varBox.autocompletionAlternativeAtIndex(
+ strlen(nameToComplete),
+ &textToInsertLength,
+ &addParentheses,
+ index,
+ &index);
+ /* Assert the autocompletion has cycles: otherwise, there are more results
+ * than expected. */
+ quiz_assert(index == 0);
+}
+
+QUIZ_CASE(variable_box_controller) {
+ const char * expectedVariables[] = {
+ "froo",
+ "from",
+ "frozenset()"
+ };
+ // FIXME This test does not load imported variables for now
+ assert_variables_are(
+ "\x01 from math import *\nfroo=3",
+ "fr",
+ expectedVariables,
+ sizeof(expectedVariables) / sizeof(const char *));
+}
diff --git a/apps/code/toolbox.de.i18n b/apps/code/toolbox.de.i18n
index 41775c90c..34840329b 100644
--- a/apps/code/toolbox.de.i18n
+++ b/apps/code/toolbox.de.i18n
@@ -2,3 +2,5 @@ Functions = "Funktionen"
Catalog = "Katalog"
Modules = "Module"
LoopsAndTests = "Schleifen und Tests"
+Files = "Dateien"
+Exceptions = "Ausnahmen"
diff --git a/apps/code/toolbox.en.i18n b/apps/code/toolbox.en.i18n
index 178d42b7b..81e60c7da 100644
--- a/apps/code/toolbox.en.i18n
+++ b/apps/code/toolbox.en.i18n
@@ -2,3 +2,5 @@ Functions = "Functions"
Catalog = "Catalog"
Modules = "Modules"
LoopsAndTests = "Loops and tests"
+Files = "Files"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.es.i18n b/apps/code/toolbox.es.i18n
index 178d42b7b..81e60c7da 100644
--- a/apps/code/toolbox.es.i18n
+++ b/apps/code/toolbox.es.i18n
@@ -2,3 +2,5 @@ Functions = "Functions"
Catalog = "Catalog"
Modules = "Modules"
LoopsAndTests = "Loops and tests"
+Files = "Files"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.fr.i18n b/apps/code/toolbox.fr.i18n
index 6442521cc..724abb7a5 100644
--- a/apps/code/toolbox.fr.i18n
+++ b/apps/code/toolbox.fr.i18n
@@ -2,3 +2,5 @@ Functions = "Fonctions"
Catalog = "Catalogue"
Modules = "Modules"
LoopsAndTests = "Boucles et tests"
+Files = "Fichiers"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.hu.i18n b/apps/code/toolbox.hu.i18n
index 930be1430..890151220 100644
--- a/apps/code/toolbox.hu.i18n
+++ b/apps/code/toolbox.hu.i18n
@@ -2,3 +2,5 @@ Functions = "Funkciók"
Catalog = "Katalógus"
Modules = "Modulok"
LoopsAndTests = "Hurkok és tesztek"
+Files = "Fájlok"
+Exceptions = "Kivételek"
diff --git a/apps/code/toolbox.it.i18n b/apps/code/toolbox.it.i18n
new file mode 100644
index 000000000..50fbd1632
--- /dev/null
+++ b/apps/code/toolbox.it.i18n
@@ -0,0 +1,6 @@
+Functions = "Funzioni"
+Catalog = "Catalogo"
+Modules = "Moduli"
+LoopsAndTests = "Loops e test"
+Files = "Files"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.nl.i18n b/apps/code/toolbox.nl.i18n
new file mode 100644
index 000000000..84b6e1f4c
--- /dev/null
+++ b/apps/code/toolbox.nl.i18n
@@ -0,0 +1,6 @@
+Functions = "Functies"
+Catalog = "Catalogus"
+Modules = "Modules"
+LoopsAndTests = "Loops and tests"
+Files = "Files"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.pt.i18n b/apps/code/toolbox.pt.i18n
index 178d42b7b..f7cfad07b 100644
--- a/apps/code/toolbox.pt.i18n
+++ b/apps/code/toolbox.pt.i18n
@@ -1,4 +1,6 @@
-Functions = "Functions"
-Catalog = "Catalog"
-Modules = "Modules"
-LoopsAndTests = "Loops and tests"
+Functions = "Funções"
+Catalog = "Catálogo"
+Modules = "Módulos"
+LoopsAndTests = "Laços e testes"
+Files = "Files"
+Exceptions = "Exceptions"
diff --git a/apps/code/toolbox.universal.i18n b/apps/code/toolbox.universal.i18n
index 5a5072475..0600a4dfd 100644
--- a/apps/code/toolbox.universal.i18n
+++ b/apps/code/toolbox.universal.i18n
@@ -44,6 +44,14 @@ ForInRange2ArgsLoop = "for i in range(\x11,):\n "
ForInRange2ArgsLoopWithArg = "for i in range(start, stop):\n instruction"
ForInRange1ArgLoop = "for i in range(\x11):\n "
ForInRange1ArgLoopWithArg = "for i in range(size):\n instruction"
+TryExcept1Error = "try:\n \nexcept \x11:\n "
+TryExcept1ErrorWithArg = "try:\n instruction\nexcept Error:\n reaction"
+TryExcept1ErrorElse = "try:\n \nexcept \x11:\n \nelse:\n "
+TryExcept1ErrorElseWithArg = "try:\n instruction\nexcept Error:\n reaction\nelse:\n clean"
+TryExcept2Error = "try:\n \nexcept (\x11):\n "
+TryExcept2ErrorWithArg = "try:\n instruction\nexcept (Error1, Error2):\n reaction"
+WithInstruction = "with \x11 as :\n "
+WithInstructionWithArg = "with expression as target:\n instructions"
PythonCommandDef = "def \x11():\n "
PythonCommandDefWithArg = "def function(x):"
PythonCommandReturn = "return "
diff --git a/apps/code/variable_box_controller.cpp b/apps/code/variable_box_controller.cpp
index c937b4896..9881b213d 100644
--- a/apps/code/variable_box_controller.cpp
+++ b/apps/code/variable_box_controller.cpp
@@ -1,4 +1,5 @@
#include "variable_box_controller.h"
+#include "python_toolbox.h"
#include "script.h"
#include "app.h"
#include "../shared/toolbox_helpers.h"
@@ -8,16 +9,43 @@
#include
#include
#include
+#include
+
+extern "C" {
+#include "py/lexer.h"
+#include "py/nlr.h"
+#include "py/objmodule.h"
+}
namespace Code {
+// Got these in python/py/src/compile.cpp compiled file
+constexpr static uint PN_file_input_2 = 1;
+constexpr static uint PN_funcdef = 3;
+constexpr static uint PN_expr_stmt = 5;
+constexpr static uint PN_import_name = 14; // import math // import math as m // import math, cmath // import math as m, cmath as cm
+constexpr static uint PN_import_from = 15; // from math import * // from math import sin // from math import sin as stew // from math import sin, cos // from math import sin as stew, cos as cabbage // from a.b import *
+constexpr static uint PN_import_as_name = 99; // sin as stew
+constexpr static uint PN_import_as_names = 102; // ... import sin as stew, cos as cabbage
+constexpr static uint PN_dotted_name = 104;
+/* These are not used for now but might be relevant at some point?
+constexpr static uint PN_import_stmt = 92;
+constexpr static uint PN_import_from_2 = 93;
+constexpr static uint PN_import_from_2b = 94; // "from .foo import"
+constexpr static uint PN_import_from_3 = 95;
+constexpr static uint PN_import_as_names_paren = 96;
+*/
+
VariableBoxController::VariableBoxController(ScriptStore * scriptStore) :
- NestedMenuController(nullptr, I18n::Message::FunctionsAndVariables),
- m_scriptNodesCount(0),
- m_scriptStore(scriptStore)
+ AlternateEmptyNestedMenuController(I18n::Message::FunctionsAndVariables),
+ m_scriptStore(scriptStore),
+ m_currentScriptNodesCount(0),
+ m_builtinNodesCount(0),
+ m_importedNodesCount(0)
{
- for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
- m_leafCells[i].setScriptStore(scriptStore);
+ for (int i = 0; i < k_scriptOriginsCount; i++) {
+ m_subtitleCells[i].setBackgroundColor(Palette::WallScreen);
+ m_subtitleCells[i].setTextColor(Palette::BlueishGrey);
}
}
@@ -29,101 +57,920 @@ bool VariableBoxController::handleEvent(Ion::Events::Event event) {
}
void VariableBoxController::didEnterResponderChain(Responder * previousFirstResponder) {
- /* This Code::VariableBoxController should always be called from an
- * environment where Python has already been inited. This way, we do not
- * deinit Python when leaving the VariableBoxController, so we do not lose the
- * environment that was loaded when entering the VariableBoxController. */
+ /* Code::VariableBoxController should always be called from an environment
+ * where Python has already been inited. This way, we do not deinit Python
+ * when leaving the VariableBoxController, so we do not lose the environment
+ * that was loaded when entering the VariableBoxController. */
assert(App::app()->pythonIsInited());
+ displayEmptyControllerIfNeeded();
}
-static bool shouldAddObject(const char * name, int maxLength) {
- if (strlen(name)+1 > (size_t)maxLength) {
- return false;
+KDCoordinate VariableBoxController::rowHeight(int j) {
+ NodeOrigin cellOrigin = NodeOrigin::CurrentScript;
+ int cumulatedOriginsCount = 0;
+ int cellType = typeAndOriginAtLocation(j, &cellOrigin, &cumulatedOriginsCount);
+ if (cellType == k_itemCellType) {
+ if (scriptNodeAtIndex(j - (m_displaySubtitles ? cumulatedOriginsCount : 0))->description() != nullptr) {
+ // If there is a node description, the cell is bigger
+ return ScriptNodeCell::k_complexItemHeight;
+ }
+ return ScriptNodeCell::k_simpleItemHeight;
}
- assert(name != nullptr);
- if (UTF8Helper::CodePointIs(name, '_')) {
- return false;
- }
- return true;
+ assert(m_displaySubtitles);
+ assert(cellType == k_subtitleCellType);
+ return k_subtitleRowHeight;
}
int VariableBoxController::numberOfRows() const {
- assert(m_scriptNodesCount <= k_maxScriptNodesCount);
- return m_scriptNodesCount;
+ int result = 0;
+ NodeOrigin origins[] = {NodeOrigin::CurrentScript, NodeOrigin::Builtins, NodeOrigin::Importation};
+ for (NodeOrigin origin : origins) {
+ int nodeCount = nodesCountForOrigin(origin);
+ if (nodeCount > 0) {
+ result += nodeCount + (m_displaySubtitles ? 1 : 0);
+ }
+ }
+ return result;
+}
+
+HighlightCell * VariableBoxController::reusableCell(int index, int type) {
+ assert(index >= 0 && index < reusableCellCount(type));
+ if (type == k_itemCellType) {
+ return m_itemCells + index;
+ }
+ assert(m_displaySubtitles);
+ assert(type == k_subtitleCellType);
+ return m_subtitleCells + index;
}
int VariableBoxController::reusableCellCount(int type) {
- assert(type == 0);
- return k_maxNumberOfDisplayedRows;
+ if (type == k_subtitleCellType) {
+ assert(m_displaySubtitles);
+ return k_scriptOriginsCount;
+ }
+ assert(type == k_itemCellType);
+ return k_maxNumberOfDisplayedItems;
}
void VariableBoxController::willDisplayCellForIndex(HighlightCell * cell, int index) {
- assert(index < m_scriptNodesCount);
- assert(m_scriptNodesCount <= k_maxScriptNodesCount);
- ScriptNodeCell * myCell = static_cast(cell);
- myCell->setScriptNode(&m_scriptNodes[index]);
+ assert(index >= 0 && index < numberOfRows());
+ NodeOrigin cellOrigin = NodeOrigin::CurrentScript;
+ int cumulatedOriginsCount = 0;
+ int cellType = typeAndOriginAtLocation(index, &cellOrigin, &cumulatedOriginsCount);
+ if (cellType == k_itemCellType) {
+ static_cast(cell)->setScriptNode(scriptNodeAtIndex(index - (m_displaySubtitles ? cumulatedOriginsCount : 0)));
+ return;
+ }
+ assert(m_displaySubtitles);
+ assert(cellType == k_subtitleCellType);
+ I18n::Message subtitleMessages[k_scriptOriginsCount] = {
+ I18n::Message::ScriptInProgress,
+ I18n::Message::BuiltinsAndKeywords,
+ I18n::Message::ImportedModulesAndScripts
+ };
+ static_cast(cell)->setMessage(subtitleMessages[(int)cellOrigin]);
+}
+
+void VariableBoxController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
+ if (withinTemporarySelection || !m_displaySubtitles) {
+ return;
+ }
+ // Make sure subtitle cells cannot be selected
+ const int currentSelectedRow = selectedRow();
+ if (currentSelectedRow >= 0 && typeAtLocation(0, currentSelectedRow) == k_subtitleCellType) {
+ if (currentSelectedRow == 0) {
+ // We scroll to the first cell, otherwise it will never appear again
+ t->scrollToCell(0, 0);
+ t->selectCellAtLocation(0, 1);
+ } else {
+ t->selectCellAtLocation(0, selectedRow() + (previousSelectedCellY < currentSelectedRow ? 1 : -1));
+ }
+ }
}
int VariableBoxController::typeAtLocation(int i, int j) {
- return 0;
+ assert(i == 0);
+ return typeAndOriginAtLocation(j);
}
-void VariableBoxController::loadFunctionsAndVariables() {
- m_scriptNodesCount = 0;
- m_scriptStore->scanScriptsForFunctionsAndVariables(
- this,
- [](void * context, const char * functionName, int scriptIndex) {
- if (!shouldAddObject(functionName, k_maxScriptObjectNameSize)) {
- return;
- }
- VariableBoxController * cvc = static_cast(context);
- cvc->addFunctionAtIndex(functionName, scriptIndex);},
- [](void * context, const char * variableName, int scriptIndex) {
- if (!shouldAddObject(variableName, k_maxScriptObjectNameSize)) {
- return;
- }
- VariableBoxController * cvc = static_cast(context);
- cvc->addVariableAtIndex(variableName, scriptIndex);});
+void VariableBoxController::loadFunctionsAndVariables(int scriptIndex, const char * textToAutocomplete, int textToAutocompleteLength) {
+ assert(scriptIndex >= 0);
+
+ // Reset the node counts
+ empty();
+
+ if (textToAutocomplete != nullptr && textToAutocompleteLength < 0) {
+ textToAutocompleteLength = strlen(textToAutocomplete);
+ }
+ /* If we are autocompleting a text, we want the returned text to not include
+ * the beginning that is equal to the text to autocomplete.
+ * For instance, if we are displaying the variable box with the text "for" to
+ * autocomplete, when the user selects "forward", we want to insert the text
+ * "ward" only.
+ * While loading the functions and variables, we thus set
+ * m_shortenResultCharCount, the number of chars to cut from the text
+ * returned. */
+ m_shortenResultCharCount = textToAutocomplete == nullptr ? 0 : textToAutocompleteLength;
+
+ // Always load the builtin functions and variables
+ loadBuiltinNodes(textToAutocomplete, textToAutocompleteLength);
+ Script script = m_scriptStore->scriptAtIndex(scriptIndex);
+ assert(!script.isNull());
+
+ /* Handle the fetchedForVariableBox status: we will import the current script
+ * variables in loadCurrentVariablesInScript, so we do not want to import
+ * those variables before, if any imported script also imported the current
+ * script. */
+ assert(!script.fetchedForVariableBox());
+ script.setFetchedForVariableBox(true);
+
+ // Load the imported and current variables
+ const char * scriptContent = script.content();
+ assert(scriptContent != nullptr);
+ loadImportedVariablesInScript(scriptContent, textToAutocomplete, textToAutocompleteLength);
+ loadCurrentVariablesInScript(scriptContent, textToAutocomplete, textToAutocompleteLength);
}
-HighlightCell * VariableBoxController::leafCellAtIndex(int index) {
- assert(index >= 0 && index < k_maxNumberOfDisplayedRows);
- return &m_leafCells[index];
+const char * VariableBoxController::autocompletionAlternativeAtIndex(int textToAutocompleteLength, int * textToInsertLength, bool * addParentheses, int index, int * indexToUpdate) {
+ if (numberOfRows() == 0) {
+ return nullptr;
+ }
+
+ int nodesCount = 0; // We cannot use numberOfRows as it contains the banners
+ NodeOrigin origins[] = {NodeOrigin::CurrentScript, NodeOrigin::Builtins, NodeOrigin::Importation};
+ for (NodeOrigin origin : origins) {
+ nodesCount += nodesCountForOrigin(origin);
+ }
+ if (index < 0) {
+ assert(index == -1);
+ index = nodesCount - 1;
+ } else if (index >= nodesCount) {
+ assert(index == nodesCount);
+ index = 0;
+ }
+
+ if (indexToUpdate != nullptr) {
+ *indexToUpdate = index;
+ }
+
+ ScriptNode * node = scriptNodeAtIndex(index);
+ const char * currentName = node->name();
+ int currentNameLength = node->nameLength();
+ if (currentNameLength < 0) {
+ currentNameLength = strlen(currentName);
+ }
+ *addParentheses = node->type() == ScriptNode::Type::WithParentheses;
+ // Return the text without the beginning that matches the text to autocomplete
+ *textToInsertLength = currentNameLength - textToAutocompleteLength;
+ return currentName + textToAutocompleteLength;
+}
+
+void VariableBoxController::loadVariablesImportedFromScripts() {
+ empty();
+ const int scriptsCount = m_scriptStore->numberOfScripts();
+ for (int i = 0; i < scriptsCount; i++) {
+ Script script = m_scriptStore->scriptAtIndex(i);
+ if (script.fetchedFromConsole()) {
+ loadGlobalAndImportedVariablesInScriptAsImported(script, nullptr, -1, false);
+ }
+ }
+}
+
+void VariableBoxController::empty() {
+ m_builtinNodesCount = 0;
+ m_currentScriptNodesCount = 0;
+ m_importedNodesCount = 0;
+ m_shortenResultCharCount = 0;
+ m_scriptStore->clearVariableBoxFetchInformation();
+}
+
+void VariableBoxController::insertAutocompletionResultAtIndex(int index) {
+ ScriptNode * selectedScriptNode = scriptNodeAtIndex(index);
+
+ /* We need to check now if we need to add parentheses: insertTextInCaller
+ * calls handleEventWithText, which will reload the autocompletion for the
+ * added text, which will probably change the script nodes and
+ * selectedScriptNode will become invalid. */
+ const bool shouldAddParentheses = selectedScriptNode->type() == ScriptNode::Type::WithParentheses;
+ insertTextInCaller(selectedScriptNode->name() + m_shortenResultCharCount, selectedScriptNode->nameLength() - m_shortenResultCharCount);
+ // WARNING: selectedScriptNode is now invalid
+
+ if (shouldAddParentheses) {
+ insertTextInCaller(ScriptNodeCell::k_parenthesesWithEmpty);
+ }
+}
+
+// PRIVATE METHODS
+
+int VariableBoxController::NodeNameCompare(ScriptNode * node, const char * name, int nameLength, bool * strictlyStartsWith) {
+ assert(strictlyStartsWith == nullptr || *strictlyStartsWith == false);
+ assert(nameLength > 0);
+ const char * nodeName = node->name();
+ const int nodeNameLength = node->nameLength() < 0 ? strlen(nodeName) : node->nameLength();
+ const int comparisonLength = std::min(nameLength, nodeNameLength);
+ int result = strncmp(nodeName, name, comparisonLength);
+ if (result != 0) {
+ return result;
+ }
+ if (nodeNameLength == nameLength) {
+ return 0;
+ }
+ bool nodeNameLengthStartsWithName = nodeNameLength > nameLength;
+ if (strictlyStartsWith != nullptr && nodeNameLengthStartsWithName) {
+ *strictlyStartsWith = true;
+ }
+ return nodeNameLengthStartsWithName ? *(nodeName + nameLength) : - *(name + nodeNameLength) ;
+}
+
+int VariableBoxController::nodesCountForOrigin(NodeOrigin origin) const {
+ if (origin == NodeOrigin::Builtins) {
+ return static_cast(m_builtinNodesCount);
+ }
+ return static_cast(*(const_cast(this)->nodesCountPointerForOrigin(origin)));
+}
+
+size_t * VariableBoxController::nodesCountPointerForOrigin(NodeOrigin origin) {
+ switch(origin) {
+ case NodeOrigin::CurrentScript:
+ return &m_currentScriptNodesCount;
+ case NodeOrigin::Builtins:
+ return &m_builtinNodesCount;
+ default:
+ assert(origin == NodeOrigin::Importation);
+ return &m_importedNodesCount;
+ }
+}
+
+ScriptNode * VariableBoxController::nodesForOrigin(NodeOrigin origin) {
+ switch(origin) {
+ case NodeOrigin::CurrentScript:
+ return m_currentScriptNodes;
+ case NodeOrigin::Builtins:
+ return m_builtinNodes;
+ default:
+ assert(origin == NodeOrigin::Importation);
+ return m_importedNodes;
+ }
+}
+
+ScriptNode * VariableBoxController::scriptNodeAtIndex(int index) {
+ assert(index >= 0 && index < numberOfRows());
+ assert(m_currentScriptNodesCount <= k_maxScriptNodesCount);
+ assert(m_builtinNodesCount <= k_totalBuiltinNodesCount);
+ assert(m_importedNodesCount <= k_maxScriptNodesCount);
+
+ NodeOrigin origins[] = {NodeOrigin::CurrentScript, NodeOrigin::Builtins, NodeOrigin::Importation};
+ for (NodeOrigin origin : origins) {
+ const int nodesCount = nodesCountForOrigin(origin);
+ if (index < nodesCount) {
+ return nodesForOrigin(origin) + index;
+ }
+ index -= nodesCount;
+ }
+ assert(false);
+ return nullptr;
+}
+
+int VariableBoxController::typeAndOriginAtLocation(int i, NodeOrigin * resultOrigin, int * cumulatedOriginsCount) const {
+ int cellIndex = 0;
+ int originsCount = 0;
+ NodeOrigin origins[] = {NodeOrigin::CurrentScript, NodeOrigin::Builtins, NodeOrigin::Importation};
+ for (NodeOrigin origin : origins) {
+ int nodeCount = nodesCountForOrigin(origin);
+ if (nodeCount > 0) {
+ originsCount++;
+ int result = -1;
+ if (m_displaySubtitles && i == cellIndex) {
+ result = k_subtitleCellType;
+ } else {
+ cellIndex += nodeCount + (m_displaySubtitles ? 1 : 0);
+ if (i < cellIndex) {
+ result = k_itemCellType;
+ }
+ }
+ if (result != -1) {
+ if (resultOrigin != nullptr) {
+ *resultOrigin = origin;
+ }
+ if (cumulatedOriginsCount != nullptr) {
+ *cumulatedOriginsCount = originsCount;
+ }
+ assert(result != k_subtitleCellType || m_displaySubtitles);
+ return result;
+ }
+ }
+ }
+ assert(false);
+ return k_itemCellType;
+
}
bool VariableBoxController::selectLeaf(int rowIndex) {
- assert(rowIndex >= 0 && rowIndex < m_scriptNodesCount);
- assert(m_scriptNodesCount <= k_maxScriptNodesCount);
+ assert(rowIndex >= 0 && rowIndex < numberOfRows());
m_selectableTableView.deselectTable();
- ScriptNode selectedScriptNode = m_scriptNodes[rowIndex];
- insertTextInCaller(selectedScriptNode.name());
- if (selectedScriptNode.type() == ScriptNode::Type::Function) {
- insertTextInCaller(ScriptNodeCell::k_parenthesesWithEmpty);
- }
+
+ int cumulatedOriginsCount = 0;
+ int cellType = typeAndOriginAtLocation(rowIndex, nullptr, &cumulatedOriginsCount);
+ assert(cellType == k_itemCellType);
+ (void)cellType; // Silence warnings
+
+ insertAutocompletionResultAtIndex(rowIndex - (m_displaySubtitles ? cumulatedOriginsCount : 0));
+
Container::activeApp()->dismissModalViewController();
return true;
}
-void VariableBoxController::insertTextInCaller(const char * text) {
- int commandBufferMaxSize = strlen(text)+1;
+void VariableBoxController::insertTextInCaller(const char * text, int textLength) {
+ int textLen = textLength < 0 ? strlen(text) : textLength;
+ constexpr int k_maxScriptObjectNameSize = 100; // Ad hoc value
+ int commandBufferMaxSize = std::min(k_maxScriptObjectNameSize, textLen + 1);
char commandBuffer[k_maxScriptObjectNameSize];
- assert(commandBufferMaxSize <= k_maxScriptObjectNameSize);
- Shared::ToolboxHelpers::TextToInsertForCommandText(text, commandBuffer, commandBufferMaxSize, true);
+ Shared::ToolboxHelpers::TextToInsertForCommandText(text, textLen, commandBuffer, commandBufferMaxSize, true);
sender()->handleEventWithText(commandBuffer);
}
-void VariableBoxController::addFunctionAtIndex(const char * functionName, int scriptIndex) {
- if (m_scriptNodesCount < k_maxScriptNodesCount) {
- m_scriptNodes[m_scriptNodesCount] = ScriptNode::FunctionNode(functionName, scriptIndex);
- m_scriptNodesCount++;
+void VariableBoxController::loadBuiltinNodes(const char * textToAutocomplete, int textToAutocompleteLength) {
+ //TODO Could be great to use strings defined in STATIC const char *const tok_kw[] in python/lexer.c
+ /* The commented values do not work with our current MicroPython but might
+ * work later, which is why we keep them. */
+ static const struct { const char * name; ScriptNode::Type type; } builtinNames[] = {
+ {"False", ScriptNode::Type::WithoutParentheses},
+ {"None", ScriptNode::Type::WithoutParentheses},
+ {"True", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_abs), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_all), ScriptNode::Type::WithParentheses},
+ {"and", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_any), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_append), ScriptNode::Type::WithParentheses},
+ {"as", ScriptNode::Type::WithoutParentheses},
+ //{qstr_str(MP_QSTR_ascii), ScriptNode::Type::WithParentheses},
+ {"assert", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_bin), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_bool), ScriptNode::Type::WithParentheses},
+ {"break", ScriptNode::Type::WithoutParentheses},
+ //{qstr_str(MP_QSTR_breakpoint), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_bytearray), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_bytes), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_callable), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_chr), ScriptNode::Type::WithParentheses},
+ {"class", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_classmethod), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_clear), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_cmath), ScriptNode::Type::WithoutParentheses},
+ //{qstr_str(MP_QSTR_compile), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_complex), ScriptNode::Type::WithParentheses},
+ {"continue", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_count), ScriptNode::Type::WithParentheses},
+ {"def", ScriptNode::Type::WithoutParentheses},
+ {"del", ScriptNode::Type::WithoutParentheses},
+ //{qstr_str(MP_QSTR_delattr), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_dict), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_dir), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_divmod), ScriptNode::Type::WithParentheses},
+ {"elif", ScriptNode::Type::WithoutParentheses},
+ {"else", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_enumerate), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_eval), ScriptNode::Type::WithParentheses},
+ {"except", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_exec), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_filter), ScriptNode::Type::WithParentheses},
+ {"finally", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_float), ScriptNode::Type::WithParentheses},
+ {"for", ScriptNode::Type::WithoutParentheses},
+ //{qstr_str(MP_QSTR_format), ScriptNode::Type::WithParentheses},
+ {"from", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_frozenset), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_getattr), ScriptNode::Type::WithParentheses},
+ {"global", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_globals), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_hasattr), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_hash), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_help), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_hex), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_id), ScriptNode::Type::WithParentheses},
+ {"if", ScriptNode::Type::WithoutParentheses},
+ {"import", ScriptNode::Type::WithoutParentheses},
+ {"in", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_index), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_input), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_insert), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_int), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_ion), ScriptNode::Type::WithoutParentheses},
+ {"is", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_isinstance), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_issubclass), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_iter), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_kandinsky), ScriptNode::Type::WithoutParentheses},
+ {"lambda", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_len), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_list), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_locals), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_map), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_math), ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_matplotlib_dot_pyplot), ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_max), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_memoryview), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_min), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_next), ScriptNode::Type::WithParentheses},
+ {"nonlocal", ScriptNode::Type::WithoutParentheses},
+ {"not", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_object), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_oct), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_open), ScriptNode::Type::WithParentheses},
+ {"or", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_ord), ScriptNode::Type::WithParentheses},
+ {"pass", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_pow), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_pop), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_print), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_property), ScriptNode::Type::WithParentheses},
+ {"raise", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_random), ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_range), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_remove), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_repr), ScriptNode::Type::WithParentheses},
+ {"return", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_reverse), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_reversed), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_round), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_set), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_setattr), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_slice), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_sort), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_sorted), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_staticmethod), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_str), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_sum), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_super), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_time), ScriptNode::Type::WithoutParentheses},
+ {"try", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_tuple), ScriptNode::Type::WithParentheses},
+ {qstr_str(MP_QSTR_turtle), ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_type), ScriptNode::Type::WithParentheses},
+ //{qstr_str(MP_QSTR_vars), ScriptNode::Type::WithParentheses},
+ {"while", ScriptNode::Type::WithoutParentheses},
+ {"with", ScriptNode::Type::WithoutParentheses},
+ {"yield", ScriptNode::Type::WithoutParentheses},
+ {qstr_str(MP_QSTR_zip), ScriptNode::Type::WithParentheses}
+ };
+ assert(sizeof(builtinNames) / sizeof(builtinNames[0]) == k_totalBuiltinNodesCount);
+ for (int i = 0; i < k_totalBuiltinNodesCount; i++) {
+ if (addNodeIfMatches(textToAutocomplete, textToAutocompleteLength, builtinNames[i].type, NodeOrigin::Builtins, builtinNames[i].name)) {
+ /* We can leverage on the fact that buitin nodes are stored in
+ * alphabetical order. */
+ return;
+ }
}
}
-void VariableBoxController::addVariableAtIndex(const char * variableName, int scriptIndex) {
- if (m_scriptNodesCount < k_maxScriptNodesCount) {
- m_scriptNodes[m_scriptNodesCount] = ScriptNode::VariableNode(variableName, scriptIndex);
- m_scriptNodesCount++;
+/* WARNING: This is very dirty.
+ * This is done to get the lexer position during lexing. As the _mp_reader_mem_t
+ * struct is private and declared in python/src/py/reader.c, we copy-paste it
+ * here to be able to use it. */
+typedef struct _mp_reader_mem_t {
+ size_t free_len; // if >0 mem is freed on close by: m_free(beg, free_len)
+ const byte *beg;
+ const byte *cur;
+ const byte *end;
+} mp_reader_mem_t;
+
+void VariableBoxController::loadImportedVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength) {
+ /* Load the imported variables and functions: lex and the parse on a line per
+ * line basis until parsing fails, while detecting import structures. */
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ const char * parseStart = scriptContent;
+ // Skip new lines at the beginning of the script
+ while (*parseStart == '\n' && *parseStart != 0) {
+ parseStart++;
+ }
+ const char * parseEnd = UTF8Helper::CodePointSearch(parseStart, '\n');
+
+ while (parseStart != parseEnd) {
+ mp_lexer_t *lex = mp_lexer_new_from_str_len(0, parseStart, parseEnd - parseStart, 0);
+ mp_parse_tree_t parseTree = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
+ mp_parse_node_t pn = parseTree.root;
+
+ if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+ addNodesFromImportMaybe((mp_parse_node_struct_t *) pn, textToAutocomplete, textToAutocompleteLength);
+ }
+
+ mp_parse_tree_clear(&parseTree);
+
+ if (*parseEnd == 0) {
+ // End of file
+ nlr_pop();
+ return;
+ }
+
+ parseStart = parseEnd;
+ // Skip the following \n
+ while (*parseStart == '\n' && *parseStart != 0) {
+ parseStart++;
+ }
+ parseEnd = UTF8Helper::CodePointSearch(parseStart, '\n');
+ }
+ nlr_pop();
}
}
+void VariableBoxController::loadCurrentVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength) {
+ /* To find variable and funtion names: we lex the script and keep all
+ * MP_TOKEN_NAME that complete the text to autocomplete and are not already in
+ * the builtins or imported scripts. */
+
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+
+ // Lex the script
+ _mp_lexer_t *lex = mp_lexer_new_from_str_len(0, scriptContent, strlen(scriptContent), false);
+
+ // Keep track of DEF tokens to differentiate between variables and functions
+ bool defToken = false;
+ const char * beginningLine = scriptContent;
+ size_t beginningLineIndex = 0;
+
+ while (lex->tok_kind != MP_TOKEN_END) {
+ // Keep only MP_TOKEN_NAME tokens
+ if (lex->tok_kind == MP_TOKEN_NAME) {
+
+ int nameLength = lex->vstr.len;
+
+ /* If the token autocompletes the text and it is not already in the
+ * variable box, add it. */
+ const NodeOrigin origin = NodeOrigin::CurrentScript;
+
+ // Find the token position in the text
+ size_t line = lex->tok_line - 1; // tok_line starts at 1, not 0
+ if (beginningLineIndex < line) {
+ while (beginningLineIndex < line) {
+ beginningLine = UTF8Helper::CodePointSearch(beginningLine, '\n') + 1;
+ beginningLineIndex++;
+ assert(*beginningLine != 0); // We should not get to the end of the text
+ }
+ }
+ assert(beginningLineIndex == line);
+ const char * tokenInText = beginningLine + lex->tok_column - 1; // tok_column starts at 1, not 0
+ assert(strncmp(tokenInText, lex->vstr.buf, nameLength) == 0);
+
+ ScriptNode::Type nodeType = (defToken || *(tokenInText + nameLength) == '(')? ScriptNode::Type::WithParentheses : ScriptNode::Type::WithoutParentheses;
+ if (addNodeIfMatches(textToAutocomplete, textToAutocompleteLength, nodeType, origin, tokenInText, nameLength)) {
+ break;
+ }
+ }
+
+ defToken = lex->tok_kind == MP_TOKEN_KW_DEF;
+ mp_lexer_to_next(lex);
+ }
+
+ mp_lexer_free(lex);
+ nlr_pop();
+ }
+}
+
+void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(Script script, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules) {
+ if (script.fetchedForVariableBox()) {
+ // We already fetched these script variables
+ return;
+ }
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ const char * scriptName = script.fullName();
+ const char * scriptContent = script.content();
+ mp_lexer_t *lex = mp_lexer_new_from_str_len(0, scriptContent, strlen(scriptContent), false);
+ mp_parse_tree_t parseTree = mp_parse(lex, MP_PARSE_FILE_INPUT);
+ mp_parse_node_t pn = parseTree.root;
+
+ if (MP_PARSE_NODE_IS_STRUCT(pn)) {
+ mp_parse_node_struct_t * pns = (mp_parse_node_struct_t *)pn;
+ uint structKind = (uint)MP_PARSE_NODE_STRUCT_KIND(pns);
+ if (structKind == PN_funcdef || structKind == PN_expr_stmt) {
+ // The script is only a single function or variable definition
+ addImportStructFromScript(pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength);
+ } else if (addNodesFromImportMaybe(pns, textToAutocomplete, textToAutocompleteLength, importFromModules)) {
+ // The script is is only an import, handled in addNodesFromImportMaybe
+ } else if (structKind == PN_file_input_2) {
+ /* At this point, if the script node is not of type "file_input_2", it
+ * will not have main structures of the wanted type.
+ * We look for structures at first level (not inside nested scopes) that
+ * are either dunction definitions, variables statements or imports. */
+ size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
+ for (size_t i = 0; i < n; i++) {
+ mp_parse_node_t child = pns->nodes[i];
+ if (MP_PARSE_NODE_IS_STRUCT(child)) {
+ mp_parse_node_struct_t *child_pns = (mp_parse_node_struct_t*)(child);
+ structKind = (uint)MP_PARSE_NODE_STRUCT_KIND(child_pns);
+ if (structKind == PN_funcdef || structKind == PN_expr_stmt) {
+ if (addImportStructFromScript(child_pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength)) {
+ break;
+ }
+ } else {
+ addNodesFromImportMaybe(child_pns, textToAutocomplete, textToAutocompleteLength, importFromModules);
+ }
+ }
+ }
+ }
+ }
+ mp_parse_tree_clear(&parseTree);
+ nlr_pop();
+ }
+ // Mark that we already fetched these script variables
+ script.setFetchedForVariableBox(true);
+}
+
+bool VariableBoxController::addNodesFromImportMaybe(mp_parse_node_struct_t * parseNode, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules) {
+ // Determine if the node is an import structure
+ uint structKind = (uint) MP_PARSE_NODE_STRUCT_KIND(parseNode);
+ bool structKindIsImportWithoutFrom = structKind == PN_import_name;
+ if (!structKindIsImportWithoutFrom
+ && structKind != PN_import_from
+ && structKind != PN_import_as_names
+ && structKind != PN_import_as_name)
+ {
+ // This was not an import structure
+ return false;
+ }
+
+ /* loadAllSourceContent will be True if the struct imports all the content
+ * from a script / module (for instance, "import math"), instead of single
+ * items (for instance, "from math import sin"). */
+ bool loadAllSourceContent = structKindIsImportWithoutFrom;
+
+ size_t childNodesCount = MP_PARSE_NODE_STRUCT_NUM_NODES(parseNode);
+ for (size_t i = 0; i < childNodesCount; i++) {
+ mp_parse_node_t child = parseNode->nodes[i];
+ if (MP_PARSE_NODE_IS_LEAF(child) && MP_PARSE_NODE_LEAF_KIND(child) == MP_PARSE_NODE_ID) {
+ // Parsing something like "import xyz"
+ const char * id = qstr_str(MP_PARSE_NODE_LEAF_ARG(child));
+
+ /* xyz might be:
+ * - a module name -> in which case we want no importation source on the
+ * node. The node will not be added if it is already in the builtins.
+ * - a script name -> we want to have xyz.py as the importation source
+ * - a non-existing identifier -> we want no source */
+ const char * sourceId = nullptr;
+ if (importationSourceIsModule(id)) {
+ if (!importFromModules) {
+ return true;
+ }
+ } else {
+ /* If a module and a script have the same name, the micropython
+ * importation algorithm first looks for a module then for a script. We
+ * should thus check that the id is not a module name before retreiving
+ * a script name to put it as source. */
+ if (!importationSourceIsScript(id, &sourceId) && !importFromModules) { // Warning : must be done in this order
+ /* We call importationSourceIsScript to load the script name in
+ * sourceId. We also use it to make sure, if importFromModules is
+ * false, that we are not importing variables from something else than
+ * scripts. */
+ return true;
+ }
+ }
+ if (addNodeIfMatches(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, id, -1, sourceId)) {
+ break;
+ }
+ } else if (MP_PARSE_NODE_IS_STRUCT(child)) {
+ // Parsing something like "from math import sin"
+ addNodesFromImportMaybe((mp_parse_node_struct_t *)child, textToAutocomplete, textToAutocompleteLength, importFromModules);
+ } else if (MP_PARSE_NODE_IS_TOKEN(child) && MP_PARSE_NODE_IS_TOKEN_KIND(child, MP_TOKEN_OP_STAR)) {
+ /* Parsing something like "from math import *"
+ * -> Load all the module content */
+ loadAllSourceContent = true;
+ }
+ }
+
+ // Fetch a script / module content if needed
+ if (loadAllSourceContent) {
+ assert(childNodesCount > 0);
+ const char * importationSourceName = importationSourceNameFromNode(parseNode->nodes[0]);
+ if (importationSourceName == nullptr) {
+ // For instance, the name is a "dotted name" but not matplotlib.pyplot
+ return true;
+ }
+ int numberOfModuleChildren = 0;
+ const ToolboxMessageTree * moduleChildren = nullptr;
+ if (importationSourceIsModule(importationSourceName, &moduleChildren, &numberOfModuleChildren)) {
+ if (!importFromModules) {
+ return true;
+ }
+ if (moduleChildren != nullptr) {
+ /* The importation source is a module that we display in the toolbox:
+ * get the nodes from the toolbox
+ * We skip the 3 first nodes, which are "import ...", "from ... import *"
+ * and "....function". */
+ constexpr int numberOfNodesToSkip = 3;
+ assert(numberOfModuleChildren > numberOfNodesToSkip);
+ for (int i = numberOfNodesToSkip; i < numberOfModuleChildren; i++) {
+ const char * name = I18n::translate((moduleChildren + i)->label());
+ if (addNodeIfMatches(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, name, -1, importationSourceName, I18n::translate((moduleChildren + i)->text()))) {
+ break;
+ }
+ }
+ } else {
+ //TODO get module variables that are not in the toolbox
+ }
+ } else {
+ // Try fetching the nodes from a script
+ Script importedScript;
+ const char * scriptFullName;
+ if (importationSourceIsScript(importationSourceName, &scriptFullName, &importedScript)) {
+ loadGlobalAndImportedVariablesInScriptAsImported(importedScript, textToAutocomplete, textToAutocompleteLength);
+ }
+ }
+ }
+ return true;
+}
+
+const char * VariableBoxController::importationSourceNameFromNode(mp_parse_node_t & node) {
+ if (MP_PARSE_NODE_IS_LEAF(node) && MP_PARSE_NODE_LEAF_KIND(node) == MP_PARSE_NODE_ID) {
+ // The importation source is "simple", for instance: from math import *
+ return qstr_str(MP_PARSE_NODE_LEAF_ARG(node));
+ }
+ if (MP_PARSE_NODE_IS_STRUCT(node)) { //TODO replace this with an assert?
+ mp_parse_node_struct_t * nodePNS = (mp_parse_node_struct_t *)node;
+ uint nodeStructKind = MP_PARSE_NODE_STRUCT_KIND(nodePNS);
+ if (nodeStructKind != PN_dotted_name) {
+ return nullptr;
+ }
+ /* The importation source is "complex", for instance:
+ * from matplotlib.pyplot import *
+ * FIXME The solution would be to build a single qstr for this name,
+ * such as in python/src/compile.c, function do_import_name, from line
+ * 1117 (found by searching PN_dotted_name).
+ * We might do this later, for now the only dotted name we might want to
+ * find is matplolib.pyplot, so we do a very specific search. */
+ int numberOfSplitNames = MP_PARSE_NODE_STRUCT_NUM_NODES(nodePNS);
+ if (numberOfSplitNames != 2) {
+ return nullptr;
+ }
+ const char * nodeSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(nodePNS->nodes[0]));
+ if (strcmp(nodeSubName, qstr_str(MP_QSTR_matplotlib)) == 0) {
+ nodeSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(nodePNS->nodes[1]));
+ if (strcmp(nodeSubName, qstr_str(MP_QSTR_pyplot)) == 0) {
+ return qstr_str(MP_QSTR_matplotlib_dot_pyplot);
+ }
+ }
+ }
+ return nullptr;
+}
+
+bool VariableBoxController::importationSourceIsModule(const char * sourceName, const ToolboxMessageTree * * moduleChildren, int * numberOfModuleChildren) {
+ const ToolboxMessageTree * children = static_cast(App::app()->toolboxForInputEventHandler(nullptr))->moduleChildren(sourceName, numberOfModuleChildren);
+ if (moduleChildren != nullptr) {
+ *moduleChildren = children;
+ }
+ if (children != nullptr) {
+ return true;
+ }
+ // The sourceName might be a module that is not in the toolbox
+ return mp_module_get(qstr_from_str(sourceName)) != MP_OBJ_NULL;
+}
+
+bool VariableBoxController::importationSourceIsScript(const char * sourceName, const char * * scriptFullName, Script * retreivedScript) {
+ // Try fetching the nodes from a script
+ Script importedScript = ScriptStore::ScriptBaseNamed(sourceName);
+ if (importedScript.isNull()) {
+ return false;
+ }
+ *scriptFullName = importedScript.fullName();
+ if (retreivedScript != nullptr) {
+ *retreivedScript = importedScript;
+ }
+ return true;
+}
+
+const char * structName(mp_parse_node_struct_t * structNode) {
+ // Find the id child node, which stores the struct's name
+ size_t childNodesCount = MP_PARSE_NODE_STRUCT_NUM_NODES(structNode);
+ if (childNodesCount < 1) {
+ return nullptr;
+ }
+ mp_parse_node_t child = structNode->nodes[0];
+ if (MP_PARSE_NODE_IS_LEAF(child)
+ && MP_PARSE_NODE_LEAF_KIND(child) == MP_PARSE_NODE_ID)
+ {
+ uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(child);
+ return qstr_str(arg);
+ }
+ return nullptr;
+}
+
+bool VariableBoxController::addImportStructFromScript(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength) {
+ assert(structKind == PN_funcdef || structKind == PN_expr_stmt);
+ // Find the id child node, which stores the struct's name
+ const char * name = structName(pns);
+ if (name == nullptr) {
+ return false;
+ }
+ return addNodeIfMatches(textToAutocomplete, textToAutocompleteLength, structKind == PN_funcdef ? ScriptNode::Type::WithParentheses : ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, name, -1, scriptName);
+}
+
+// The returned boolean means we should escape the process
+bool VariableBoxController::addNodeIfMatches(const char * textToAutocomplete, int textToAutocompleteLength, ScriptNode::Type nodeType, NodeOrigin nodeOrigin, const char * nodeName, int nodeNameLength, const char * nodeSourceName, const char * nodeDescription) {
+ assert(nodeName != nullptr);
+ if (nodeNameLength < 0) {
+ nodeNameLength = strlen(nodeName);
+ }
+ // Step 1: Check if the node matches the textToAutocomplete
+
+ // Step 1.1: Few escape cases
+ /* If the node will go to imported, do not add it if it starts with an
+ * underscore : such identifiers are meant to be private. */
+ if (nodeOrigin == NodeOrigin::Importation && UTF8Helper::CodePointIs(nodeName, '_')) {
+ return false;
+ }
+ /* If the node is extracted from the current script, escape the current
+ * autocompleted word. */
+ if (nodeOrigin == NodeOrigin::CurrentScript && nodeName == textToAutocomplete) {
+ return false;
+ }
+ bool nodeInLexicographicalOrder = nodeOrigin == NodeOrigin::Builtins;
+
+ ScriptNode node(nodeType, nodeName, nodeNameLength, nodeSourceName, nodeDescription);
+
+ // Step 1.2: check if textToAutocomplete matches the node
+ if (textToAutocomplete != nullptr) {
+ /* Check that nodeName autocompletes the text to autocomplete
+ * - The start of nodeName must be equal to the text to autocomplete */
+ bool strictlyStartsWith = false;
+ int cmp = NodeNameCompare(&node, textToAutocomplete, textToAutocompleteLength, &strictlyStartsWith);
+ if (cmp == 0) {
+ // We don't accept the node if it has no parentheses
+ if (node.type() != ScriptNode::Type::WithParentheses) {
+ return false;
+ }
+ } else {
+ // We don't accept the node if it doesn't start as the textToAutocomplete
+ if (!strictlyStartsWith) {
+ if (nodeInLexicographicalOrder && cmp > 0) {
+ /* Signal to end the nodes scanning because we went past the
+ * textToAutocomplete in lexicographical order. */
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+
+ // Step 2: Add Node
+
+ // Step 2.1: don't overflow the node list
+ size_t * currentNodeCount = nodesCountPointerForOrigin(nodeOrigin);
+ ScriptNode * nodes = nodesForOrigin(nodeOrigin);
+ if (*currentNodeCount >= MaxNodesCountForOrigin(nodeOrigin)) {
+ // There is no room to add another node
+ return true;
+ }
+
+ // Step 2.2: find where to add the node (and check that it doesn't exist yet)
+ size_t insertionIndex = *currentNodeCount;
+ if (nodeOrigin == NodeOrigin::Builtins) {
+ /* For builtin nodes, we don't need to check whether the node was already
+ * added because they're added first in lexicographical order. Plus, we
+ * want to add it at the end of list to respect the lexicographical order. */
+ assert(nodeInLexicographicalOrder);
+ } else {
+ // Look where to add
+ bool alreadyInVarBox = false;
+ // This could be faster with dichotomia, but there is no speed problem for now
+ NodeOrigin origins[] = {NodeOrigin::CurrentScript, NodeOrigin::Builtins, NodeOrigin::Importation};
+ for (NodeOrigin origin : origins) {
+ const int nodesCount = nodesCountForOrigin(origin);
+ ScriptNode * nodes = nodesForOrigin(origin);
+ for (int i = 0; i < nodesCount; i++) {
+ ScriptNode * matchingNode = nodes + i;
+ int comparisonResult = NodeNameCompare(matchingNode, nodeName, nodeNameLength);
+ if (comparisonResult == 0 || (comparisonResult == '(' && nodeType == ScriptNode::Type::WithParentheses)) {
+ alreadyInVarBox = true;
+ break;
+ }
+ if (comparisonResult > 0) {
+ if (nodeOrigin == origin) {
+ insertionIndex = i;
+ }
+ break;
+ }
+ }
+ if (alreadyInVarBox) {
+ return false;
+ }
+ }
+ }
+
+ // Step 2.3: Shift all the following nodes
+ for (size_t i = *currentNodeCount; i > insertionIndex; i--) {
+ nodes[i] = nodes[i - 1];
+ }
+
+ // Step 2.4: Check if the node source name fits, if not, do not use it
+ if (!ScriptNodeCell::CanDisplayNameAndSource(nodeNameLength, nodeSourceName)) {
+ nodeSourceName = nullptr;
+ }
+ // Step 2.5: Add the node
+ nodes[insertionIndex] = ScriptNode(nodeType, nodeName, nodeNameLength, nodeSourceName, nodeDescription);
+ // Increase the node count
+ *currentNodeCount = *currentNodeCount + 1;
+ return false;
+}
+
}
diff --git a/apps/code/variable_box_controller.h b/apps/code/variable_box_controller.h
index 79d107f00..f8968b41b 100644
--- a/apps/code/variable_box_controller.h
+++ b/apps/code/variable_box_controller.h
@@ -1,14 +1,17 @@
#ifndef CODE_VARIABLE_BOX_CONTROLLER_H
#define CODE_VARIABLE_BOX_CONTROLLER_H
-#include
+#include
+#include
+#include
#include "script_node.h"
#include "script_node_cell.h"
#include "script_store.h"
+#include "variable_box_empty_controller.h"
namespace Code {
-class VariableBoxController : public NestedMenuController {
+class VariableBoxController : public AlternateEmptyNestedMenuController {
public:
VariableBoxController(ScriptStore * scriptStore);
@@ -16,28 +19,98 @@ public:
bool handleEvent(Ion::Events::Event event) override;
void didEnterResponderChain(Responder * previousFirstResponder) override;
- /* ListViewDataSource */
+ /* TableViewDataSource */
+ KDCoordinate rowHeight(int j) override;
int numberOfRows() const override;
+ HighlightCell * reusableCell(int index, int type) override;
int reusableCellCount(int type) override;
- void willDisplayCellForIndex(HighlightCell * cell, int index) override;
int typeAtLocation(int i, int j) override;
+ /* ListViewDataSource */
+ void willDisplayCellForIndex(HighlightCell * cell, int index) override;
+ /* SelectableTableViewDelegate */
+ void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection = false) override;
+
+ //AlternateEmptyNestedMenuController
+ ViewController * emptyViewController() override { return &m_variableBoxEmptyController; }
/* VariableBoxController */
- void loadFunctionsAndVariables();
+ void setDisplaySubtitles(bool display) { m_displaySubtitles = display; }
+ void loadFunctionsAndVariables(int scriptIndex, const char * textToAutocomplete, int textToAutocompleteLength);
+ const char * autocompletionAlternativeAtIndex(int textToAutocompleteLength, int * textToInsertLength, bool * addParentheses, int index, int * indexToUpdate = nullptr);
+ void loadVariablesImportedFromScripts();
+ void empty();
+ void insertAutocompletionResultAtIndex(int index);
+
private:
- constexpr static int k_maxScriptObjectNameSize = 100;
- constexpr static int k_maxNumberOfDisplayedRows = 6; //240/40
- constexpr static int k_maxScriptNodesCount = 32;
- HighlightCell * leafCellAtIndex(int index) override;
- HighlightCell * nodeCellAtIndex(int index) override { return nullptr; }
+ constexpr static size_t k_maxNumberOfDisplayedItems = (Ion::Display::Height - Metric::TitleBarHeight - Metric::PopUpTopMargin) / ScriptNodeCell::k_simpleItemHeight + 2; // +2 if the cells are cropped on top and at the bottom
+ constexpr static size_t k_maxScriptNodesCount = 32; // Chosen without particular reasons
+ constexpr static int k_totalBuiltinNodesCount = 107;
+ constexpr static uint8_t k_scriptOriginsCount = 3;
+ constexpr static uint8_t k_subtitleCellType = NodeCellType; // We don't care as it is not selectable
+ constexpr static uint8_t k_itemCellType = LeafCellType; // So that upper class NestedMenuController knows it's a leaf
+ constexpr static KDCoordinate k_subtitleRowHeight = 23;
+
+ enum class NodeOrigin : uint8_t {
+ CurrentScript = 0,
+ Builtins = 1,
+ Importation = 2
+ };
+
+ /* Returns:
+ * - a negative int if the node name is before name in alphabetical
+ * order
+ * - 0 if they are equal
+ * - a positive int if it is after in alphabetical order.
+ * strictlyStartsWith is set to True if the node name starts with name but
+ * they are not equal.*/
+ static int NodeNameCompare(ScriptNode * node, const char * name, int nameLength, bool * strictlyStartsWith = nullptr);
+
+ // Nodes and nodes count
+ static size_t MaxNodesCountForOrigin(NodeOrigin origin) {
+ return origin == NodeOrigin::Builtins ? k_totalBuiltinNodesCount : k_maxScriptNodesCount;
+ }
+ int nodesCountForOrigin(NodeOrigin origin) const;
+ size_t * nodesCountPointerForOrigin(NodeOrigin origin);
+ ScriptNode * nodesForOrigin(NodeOrigin origin);
+ ScriptNode * scriptNodeAtIndex(int index);
+
+ // Cell getters
+ int typeAndOriginAtLocation(int i, NodeOrigin * resultOrigin = nullptr, int * cumulatedOriginsCount = nullptr) const;
+
+ // NestedMenuController
+ HighlightCell * leafCellAtIndex(int index) override { assert(false); return nullptr; }
+ HighlightCell * nodeCellAtIndex(int index) override { assert(false); return nullptr; }
bool selectLeaf(int rowIndex) override;
- void insertTextInCaller(const char * text);
- void addFunctionAtIndex(const char * functionName, int scriptIndex);
- void addVariableAtIndex(const char * variableName, int scriptIndex);
- ScriptNode m_scriptNodes[k_maxScriptNodesCount];
- int m_scriptNodesCount;
+ void insertTextInCaller(const char * text, int textLength = -1);
+
+ // Loading
+ void loadBuiltinNodes(const char * textToAutocomplete, int textToAutocompleteLength);
+ void loadImportedVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength);
+ void loadCurrentVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength);
+ void loadGlobalAndImportedVariablesInScriptAsImported(Script script, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules = true);
+ // Returns true if this was an import structure
+ bool addNodesFromImportMaybe(mp_parse_node_struct_t * parseNode, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules = true);
+ const char * importationSourceNameFromNode(mp_parse_node_t & node);
+ bool importationSourceIsModule(const char * sourceName, const ToolboxMessageTree * * moduleChildren = nullptr, int * numberOfModuleChildren = nullptr);
+ bool importationSourceIsScript(const char * sourceName, const char * * scriptFullName, Script * retreivedScript = nullptr);
+ bool addImportStructFromScript(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength);
+ /* Add a node if it completes the text to autocomplete and if it is not
+ * already contained in the variable box. The returned boolean means we
+ * should escape the node scanning process (due to the lexicographical order
+ * or full node table). */
+ bool addNodeIfMatches(const char * textToAutocomplete, int textToAutocompleteLength, ScriptNode::Type type, NodeOrigin origin, const char * nodeName, int nodeNameLength = -1, const char * nodeSourceName = nullptr, const char * description = nullptr);
+ VariableBoxEmptyController m_variableBoxEmptyController;
+ ScriptNode m_currentScriptNodes[k_maxScriptNodesCount];
+ ScriptNode m_builtinNodes[k_totalBuiltinNodesCount];
+ ScriptNode m_importedNodes[k_maxScriptNodesCount];
+ ScriptNodeCell m_itemCells[k_maxNumberOfDisplayedItems];
+ MessageTableCell m_subtitleCells[k_scriptOriginsCount];
ScriptStore * m_scriptStore;
- ScriptNodeCell m_leafCells[k_maxNumberOfDisplayedRows];
+ size_t m_currentScriptNodesCount;
+ size_t m_builtinNodesCount;
+ size_t m_importedNodesCount;
+ int m_shortenResultCharCount; // This is used to send only the completing text when we are autocompleting
+ bool m_displaySubtitles;
};
}
diff --git a/apps/code/variable_box_empty_controller.cpp b/apps/code/variable_box_empty_controller.cpp
new file mode 100644
index 000000000..83bd42471
--- /dev/null
+++ b/apps/code/variable_box_empty_controller.cpp
@@ -0,0 +1,13 @@
+#include "variable_box_empty_controller.h"
+#include
+
+namespace Code {
+
+VariableBoxEmptyController::VariableBoxEmptyView::VariableBoxEmptyView() :
+ ::ModalViewEmptyController::ModalViewEmptyView()
+{
+ initMessageViews();
+ m_message.setMessage(I18n::Message::NoWordAvailableHere);
+}
+
+}
diff --git a/apps/code/variable_box_empty_controller.h b/apps/code/variable_box_empty_controller.h
new file mode 100644
index 000000000..d78cea504
--- /dev/null
+++ b/apps/code/variable_box_empty_controller.h
@@ -0,0 +1,34 @@
+#ifndef APPS_CODE_VARIABLE_BOX_EMPTY_CONTROLLER_H
+#define APPS_CODE_VARIABLE_BOX_EMPTY_CONTROLLER_H
+
+#include
+
+namespace Code {
+
+class VariableBoxEmptyController : public ModalViewEmptyController {
+public:
+ VariableBoxEmptyController() :
+ ModalViewEmptyController(),
+ m_view()
+ {}
+ // View Controller
+ View * view() override { return &m_view; }
+private:
+ class VariableBoxEmptyView : public ModalViewEmptyController::ModalViewEmptyView {
+ public:
+ constexpr static int k_numberOfMessages = 1;
+ VariableBoxEmptyView();
+ private:
+ int numberOfMessageTextViews() const override { return k_numberOfMessages; }
+ MessageTextView * messageTextViewAtIndex(int index) override {
+ assert(index >= 0 && index < k_numberOfMessages);
+ return &m_message;
+ }
+ MessageTextView m_message;
+ };
+ VariableBoxEmptyView m_view;
+};
+
+}
+
+#endif
diff --git a/apps/empty_battery_window.cpp b/apps/empty_battery_window.cpp
index b0e58e2dd..e324ae2a2 100644
--- a/apps/empty_battery_window.cpp
+++ b/apps/empty_battery_window.cpp
@@ -1,5 +1,4 @@
#include "empty_battery_window.h"
-#include "global_preferences.h"
#include
extern "C" {
#include
@@ -12,7 +11,7 @@ EmptyBatteryWindow::EmptyBatteryWindow() :
void EmptyBatteryWindow::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(bounds(), KDColorWhite);
- const char * warningMessage = I18n::translate(I18n::Message::LowBattery, GlobalPreferences::sharedGlobalPreferences()->language());
+ const char * warningMessage = I18n::translate(I18n::Message::LowBattery);
KDSize warningSize = KDFont::LargeFont->stringSize(warningMessage);
ctx->drawString(warningMessage, KDPoint((Ion::Display::Width - warningSize.width())/2, (Ion::Display::Height - warningSize.height())/2), KDFont::LargeFont);
}
diff --git a/apps/exam_mode_configuration.h b/apps/exam_mode_configuration.h
index 695c2b249..e269ede0f 100644
--- a/apps/exam_mode_configuration.h
+++ b/apps/exam_mode_configuration.h
@@ -22,7 +22,7 @@ I18n::Message examModeActivationWarningMessage(GlobalPreferences::ExamMode mode,
// Exam mode behaviour
KDColor examModeColor(GlobalPreferences::ExamMode mode);
-bool appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode);
+bool appIsForbiddenInExamMode(App::Descriptor::ExaminationLevel appExaminationLevel, GlobalPreferences::ExamMode mode);
bool exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode);
}
diff --git a/apps/exam_mode_configuration_non_official.cpp b/apps/exam_mode_configuration_non_official.cpp
index 01307ecec..d22a24913 100644
--- a/apps/exam_mode_configuration_non_official.cpp
+++ b/apps/exam_mode_configuration_non_official.cpp
@@ -58,10 +58,13 @@ KDColor ExamModeConfiguration::examModeColor(GlobalPreferences::ExamMode mode) {
}
}
-bool ExamModeConfiguration::appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode) {
+bool ExamModeConfiguration::appIsForbiddenInExamMode(App::Descriptor::ExaminationLevel appExaminationLevel, GlobalPreferences::ExamMode mode) {
+ if (mode == GlobalPreferences::ExamMode::NoSymNoText) {
+ return appExaminationLevel == App::Descriptor::ExaminationLevel::Basic;
+ }
return false;
}
bool ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode) {
- return false;
+ return mode == GlobalPreferences::ExamMode::NoSymNoText ? true : false;
}
diff --git a/apps/exam_mode_configuration_official.cpp b/apps/exam_mode_configuration_official.cpp
deleted file mode 100644
index 860b2f65c..000000000
--- a/apps/exam_mode_configuration_official.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: CC-BY-NC-ND-4.0
-// Caution: Dutch exam mode is subject to a compliance certification by a government agency. Distribution of a non-certified Dutch exam mode is illegal.
-
-#include "exam_mode_configuration.h"
-
-constexpr Shared::SettingsMessageTree ExamModeConfiguration::s_modelExamChildren[2] = {Shared::SettingsMessageTree(I18n::Message::ActivateExamMode), Shared::SettingsMessageTree(I18n::Message::ActivateDutchExamMode)};
-
-int ExamModeConfiguration::numberOfAvailableExamMode() {
- if (GlobalPreferences::sharedGlobalPreferences()->language() != I18n::Language::EN || GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) {
- return 1;
- }
- return 2;
-}
-
-GlobalPreferences::ExamMode ExamModeConfiguration::examModeAtIndex(int index) {
- return index == 0 ? GlobalPreferences::ExamMode::Standard : GlobalPreferences::ExamMode::Dutch;
-}
-
-I18n::Message ExamModeConfiguration::examModeActivationMessage(int index) {
- return index == 0 ? I18n::Message::ActivateExamMode : I18n::Message::ActivateDutchExamMode;
-}
-
-I18n::Message ExamModeConfiguration::examModeActivationWarningMessage(GlobalPreferences::ExamMode mode, int line) {
- if (mode == GlobalPreferences::ExamMode::Off) {
- I18n::Message warnings[] = {I18n::Message::ExitExamMode1, I18n::Message::ExitExamMode2, I18n::Message::Default};
- return warnings[line];
- } else if (mode == GlobalPreferences::ExamMode::Standard) {
- I18n::Message warnings[] = {I18n::Message::ActiveExamModeMessage1, I18n::Message::ActiveExamModeMessage2, I18n::Message::ActiveExamModeMessage3};
- return warnings[line];
- }
- assert(mode == GlobalPreferences::ExamMode::Dutch);
- I18n::Message warnings[] = {I18n::Message::ActiveDutchExamModeMessage1, I18n::Message::ActiveDutchExamModeMessage2, I18n::Message::ActiveDutchExamModeMessage3};
- return warnings[line];
-}
-
-KDColor ExamModeConfiguration::examModeColor(GlobalPreferences::ExamMode mode) {
- /* The Dutch exam mode LED is supposed to be orange but we can only make
- * blink "pure" colors: with RGB leds on or off (as the PWM is used for
- * blinking). The closest "pure" color is Yellow. Moreover, Orange LED is
- * already used when the battery is charging. Using yellow, we can assert
- * that the yellow LED only means that Dutch exam mode is on and avoid
- * confusing states when the battery is charging and states when the Dutch
- * exam mode is on. */
- return mode == GlobalPreferences::ExamMode::Dutch ? KDColorYellow : KDColorRed;
-}
-
-bool ExamModeConfiguration::appIsForbiddenInExamMode(I18n::Message appName, GlobalPreferences::ExamMode mode) {
- return appName == I18n::Message::CodeApp && mode == GlobalPreferences::ExamMode::Dutch;
-}
-
-bool ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::ExamMode mode) {
- return mode == GlobalPreferences::ExamMode::Dutch;
-}
diff --git a/apps/exam_pop_up_controller.h b/apps/exam_pop_up_controller.h
index 3ef7285eb..c335126f7 100644
--- a/apps/exam_pop_up_controller.h
+++ b/apps/exam_pop_up_controller.h
@@ -8,7 +8,7 @@
class HighContrastButton : public Button {
public:
using Button::Button;
- virtual KDColor highlightedBackgroundColor() const override { return Palette::ButtonBackgroundSelectedHighContrast; }
+ KDColor highlightedBackgroundColor() const override { return Palette::ButtonBackgroundSelectedHighContrast; }
};
class ExamPopUpController : public ViewController {
diff --git a/apps/external/Makefile b/apps/external/Makefile
index 82e6541f9..0929e8d21 100644
--- a/apps/external/Makefile
+++ b/apps/external/Makefile
@@ -1,3 +1,14 @@
+ifdef HOME_DISPLAY_EXTERNALS
+
+app_external_src = $(addprefix apps/external/,\
+ extapp_api.cpp \
+ archive.cpp \
+)
+
+$(eval $(call depends_on_image,apps/home/controller.cpp,apps/external/external_icon.png))
+
+else
+
apps += External::App
app_headers += apps/external/app.h
@@ -9,6 +20,10 @@ app_external_src = $(addprefix apps/external/,\
pointer_text_table_cell.cpp \
)
+$(eval $(call depends_on_image,apps/external/app.cpp,apps/external/external_icon.png))
+
+endif
+
SFLAGS += -Iapps/external/
EXTAPP_PATH ?= apps/external/app/
@@ -18,7 +33,11 @@ else
include $(EXTAPP_PATH)/sources.mak
endif
-app_src += $(app_external_src)
+ifdef EXTERNAL_BUILTIN
+SFLAGS += -DEXTERNAL_BUILTIN
+endif
+
+apps_src += $(app_external_src)
i18n_files += $(addprefix apps/external/,\
base.de.i18n\
@@ -26,8 +45,9 @@ i18n_files += $(addprefix apps/external/,\
base.es.i18n\
base.fr.i18n\
base.pt.i18n\
+ base.it.i18n\
+ base.nl.i18n\
base.hu.i18n\
base.universal.i18n\
)
-$(eval $(call depends_on_image,apps/external/app.cpp,apps/external/external_icon.png))
diff --git a/apps/external/app.cpp b/apps/external/app.cpp
index 81d39e354..fba8ffccc 100644
--- a/apps/external/app.cpp
+++ b/apps/external/app.cpp
@@ -12,8 +12,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::ExternalAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::BasicExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Basic;
}
const Image * App::Descriptor::icon() {
diff --git a/apps/external/app.h b/apps/external/app.h
index 887c96e1c..01ddaa800 100644
--- a/apps/external/app.h
+++ b/apps/external/app.h
@@ -12,7 +12,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
diff --git a/apps/external/archive.cpp b/apps/external/archive.cpp
index d25c888e0..5636f0ef6 100644
--- a/apps/external/archive.cpp
+++ b/apps/external/archive.cpp
@@ -40,6 +40,9 @@ bool isExamModeAndFileNotExecutable(const TarHeader* tar) {
}
bool fileAtIndex(size_t index, File &entry) {
+ if (index == -1)
+ return false;
+
const TarHeader* tar = reinterpret_cast(0x90200000);
unsigned size = 0;
@@ -115,16 +118,62 @@ size_t numberOfFiles() {
return count;
}
+bool executableAtIndex(size_t index, File &entry) {
+ File dummy;
+ size_t count;
+ size_t final_count = 0;
+
+ for (count = 0; fileAtIndex(count, dummy); count++) {
+ if (dummy.isExecutable) {
+ if (final_count == index) {
+ entry.name = dummy.name;
+ entry.data = dummy.data;
+ entry.dataLength = dummy.dataLength;
+ entry.isExecutable = dummy.isExecutable;
+ return true;
+ }
+ final_count++;
+ }
+ }
+
+ return false;
+}
+
+size_t numberOfExecutables() {
+ File dummy;
+ size_t count;
+ size_t final_count = 0;
+
+ for (count = 0; fileAtIndex(count, dummy); count++)
+ if (dummy.isExecutable)
+ final_count++;
+
+ return final_count;
+}
+
+
+
#else
bool fileAtIndex(size_t index, File &entry) {
- entry.name = "No apps installed ";
+ if (index != 0)
+ return false;
+
+ entry.name = "Built-in";
entry.data = NULL;
entry.dataLength = 0;
entry.isExecutable = true;
return true;
}
+bool executableAtIndex(size_t index, File &entry) {
+ return fileAtIndex(index, entry);
+}
+
+size_t numberOfExecutables() {
+ return 1;
+}
+
extern "C" void extapp_main(void);
uint32_t executeFile(const char *name, void * heap, const uint32_t heapSize) {
@@ -133,11 +182,14 @@ uint32_t executeFile(const char *name, void * heap, const uint32_t heapSize) {
}
int indexFromName(const char *name) {
- return 0;
+ if (strcmp(name, "Built-in") == 0)
+ return 0;
+ else
+ return -1;
}
size_t numberOfFiles() {
- return 0;
+ return 1;
}
#endif
diff --git a/apps/external/archive.h b/apps/external/archive.h
index 80b71d0e1..670bd138f 100644
--- a/apps/external/archive.h
+++ b/apps/external/archive.h
@@ -19,6 +19,8 @@ struct File {
bool fileAtIndex(size_t index, File &entry);
int indexFromName(const char *name);
size_t numberOfFiles();
+size_t numberOfExecutables();
+bool executableAtIndex(size_t index, File &entry);
uint32_t executeFile(const char *name, void * heap, const uint32_t heapSize);
}
diff --git a/apps/external/base.it.i18n b/apps/external/base.it.i18n
new file mode 100644
index 000000000..49b8a2173
--- /dev/null
+++ b/apps/external/base.it.i18n
@@ -0,0 +1,9 @@
+ExternalApp = "External"
+ExternalAppCapital = "EXTERNAL"
+ExternalAppApiMismatch = "API mismatch"
+ExternalAppExecError = "Cannot execute file"
+ExternalNotCompatible = "External is not compatible"
+WithSimulator = "with the simulator"
+WithN0100 = "with n0100"
+GetMoreAppsAt = "Get more apps at"
+NoAppsInstalled = "No apps installed"
diff --git a/apps/external/base.nl.i18n b/apps/external/base.nl.i18n
new file mode 100644
index 000000000..49b8a2173
--- /dev/null
+++ b/apps/external/base.nl.i18n
@@ -0,0 +1,9 @@
+ExternalApp = "External"
+ExternalAppCapital = "EXTERNAL"
+ExternalAppApiMismatch = "API mismatch"
+ExternalAppExecError = "Cannot execute file"
+ExternalNotCompatible = "External is not compatible"
+WithSimulator = "with the simulator"
+WithN0100 = "with n0100"
+GetMoreAppsAt = "Get more apps at"
+NoAppsInstalled = "No apps installed"
diff --git a/apps/external/extapp_api.cpp b/apps/external/extapp_api.cpp
index 248fc6ee2..eefd19f58 100644
--- a/apps/external/extapp_api.cpp
+++ b/apps/external/extapp_api.cpp
@@ -269,7 +269,7 @@ int extapp_getKey(bool allowSuspend, bool *alphaWasActive) {
continue;
}
if (event.isKeyboardEvent()) {
- key = event.id();
+ key = static_cast(event);
if (key == 17 || key == 4 || key == 5 || key == 52) {
extapp_resetKeyboard();
}
diff --git a/apps/external/main_controller.cpp b/apps/external/main_controller.cpp
index b2b1d06df..acf837441 100644
--- a/apps/external/main_controller.cpp
+++ b/apps/external/main_controller.cpp
@@ -86,37 +86,37 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) {
PointerTextTableCell * myTextCell = (PointerTextTableCell *)cell;
myTextCell->setHighlighted(myTextCell->isHighlighted());
struct File f;
- #ifdef DEVICE
- if(Ion::fccId() == "2ALWP-N0100"){
- if(index == 0){
- myTextCell->setText(I18n::translate(I18n::Message::ExternalNotCompatible));
- myTextCell->setTextColor(Palette::Red);
- } else {
- myTextCell->setText(I18n::translate(I18n::Message::WithN0100));
- myTextCell->setTextColor(Palette::Red);
- }
- }else{
- if(index == k_numberOfCells-1){
- myTextCell->setText(I18n::translate(I18n::Message::URL));
- myTextCell->setTextColor(Palette::AccentText);
- return;
- }
- if(index == k_numberOfCells-2){
- myTextCell->setText(I18n::translate(I18n::Message::GetMoreAppsAt));
- myTextCell->setTextColor(Palette::AccentText);
- return;
- }
- if(index == 0 && numberOfFiles() == 0){
- myTextCell->setText(I18n::translate(I18n::Message::NoAppsInstalled));
- myTextCell->setTextColor(Palette::Red);
- }
- if(numberOfFiles() > 0){
- if(fileAtIndex(index, f)) {
- myTextCell->setText(f.name);
- myTextCell->setTextColor(f.isExecutable ? Palette::PrimaryText : Palette::Palette::SecondaryText);
- }
- }
- }
+ #if defined(DEVICE) || defined(EXTERNAL_BUILTIN)
+ #if defined(DEVICE_N0100) && !defined(EXTERNAL_BUILTIN)
+ if(index == 0){
+ myTextCell->setText(I18n::translate(I18n::Message::ExternalNotCompatible));
+ myTextCell->setTextColor(Palette::Red);
+ } else {
+ myTextCell->setText(I18n::translate(I18n::Message::WithN0100));
+ myTextCell->setTextColor(Palette::Red);
+ }
+ #else
+ if(index == k_numberOfCells-1){
+ myTextCell->setText(I18n::translate(I18n::Message::URL));
+ myTextCell->setTextColor(Palette::AccentText);
+ return;
+ }
+ if(index == k_numberOfCells-2){
+ myTextCell->setText(I18n::translate(I18n::Message::GetMoreAppsAt));
+ myTextCell->setTextColor(Palette::AccentText);
+ return;
+ }
+ if(index == 0 && numberOfFiles() == 0){
+ myTextCell->setText(I18n::translate(I18n::Message::NoAppsInstalled));
+ myTextCell->setTextColor(Palette::Red);
+ }
+ if(numberOfFiles() > 0){
+ if(fileAtIndex(index, f)) {
+ myTextCell->setText(f.name);
+ myTextCell->setTextColor(f.isExecutable ? Palette::PrimaryText : Palette::Palette::SecondaryText);
+ }
+ }
+ #endif
#else
if(index == 0){
myTextCell->setText(I18n::translate(I18n::Message::ExternalNotCompatible));
@@ -130,16 +130,16 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) {
void MainController::viewWillAppear() {
int count;
- #ifdef DEVICE
- if(Ion::fccId() == "2ALWP-N0100"){
+ #if defined(DEVICE) || defined(EXTERNAL_BUILTIN)
+ #if !defined(DEVICE_N0110) && !defined(EXTERNAL_BUILTIN)
count = 2;
- }else {
- if(numberOfFiles() > 0){
- count = numberOfFiles()+2;
- } else {
- count = 3;
- }
- }
+ #else
+ if(numberOfFiles() > 0){
+ count = numberOfFiles()+2;
+ } else {
+ count = 3;
+ }
+ #endif
#else
count = 2;
diff --git a/apps/global_preferences.h b/apps/global_preferences.h
index 0ff7c00be..d0038910b 100644
--- a/apps/global_preferences.h
+++ b/apps/global_preferences.h
@@ -30,8 +30,9 @@ public:
void setFont(const KDFont * font) { m_font = font; }
constexpr static int NumberOfBrightnessStates = 15;
private:
+ static_assert(I18n::NumberOfLanguages > 0, "I18n::NumberOfLanguages is not superior to 0"); // There should already have be an error when processing an empty EPSILON_I18N flag
GlobalPreferences() :
- m_language(I18n::Language::EN),
+ m_language((I18n::Language)0),
m_examMode(ExamMode::Unknown),
m_tempExamMode(ExamMode::Standard),
m_showPopUp(true),
diff --git a/apps/graph/Makefile b/apps/graph/Makefile
index 16811168a..4f440fea8 100644
--- a/apps/graph/Makefile
+++ b/apps/graph/Makefile
@@ -31,15 +31,8 @@ app_graph_src = $(addprefix apps/graph/,\
values/values_controller.cpp \
)
-app_src += $(app_graph_src)
+apps_src += $(app_graph_src)
-i18n_files += $(addprefix apps/graph/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
-)
+i18n_files += $(call i18n_without_universal_for,graph/base)
$(eval $(call depends_on_image,apps/graph/app.cpp,apps/graph/graph_icon.png))
diff --git a/apps/graph/app.cpp b/apps/graph/app.cpp
index 264e699da..a856c2c1a 100644
--- a/apps/graph/app.cpp
+++ b/apps/graph/app.cpp
@@ -16,8 +16,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::FunctionAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
@@ -81,9 +81,9 @@ CodePoint App::XNT() {
}
NestedMenuController * App::variableBoxForInputEventHandler(InputEventHandler * textInput) {
- VariableBoxController * varBox = AppsContainer::sharedAppsContainer()->variableBoxController();
+ MathVariableBoxController * varBox = AppsContainer::sharedAppsContainer()->variableBoxController();
varBox->setSender(textInput);
- varBox->lockDeleteEvent(VariableBoxController::Page::Function);
+ varBox->lockDeleteEvent(MathVariableBoxController::Page::Function);
return varBox;
}
diff --git a/apps/graph/app.h b/apps/graph/app.h
index 94a2c778e..f92557329 100644
--- a/apps/graph/app.h
+++ b/apps/graph/app.h
@@ -17,7 +17,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public Shared::FunctionApp::Snapshot {
diff --git a/apps/graph/base.de.i18n b/apps/graph/base.de.i18n
index 3eed42db5..c816e3d06 100644
--- a/apps/graph/base.de.i18n
+++ b/apps/graph/base.de.i18n
@@ -20,8 +20,8 @@ Zeros = "Nullstellen"
Tangent = "Tangente"
Intersection = "Schnittmenge"
Preimage = "Urbild"
-SelectLowerBound = "Untere Integrationsgrenze"
-SelectUpperBound = "Obere Integrationsgrenze"
+SelectLowerBound = "Untere Integrationsgrenze "
+SelectUpperBound = "Obere Integrationsgrenze "
NoMaximumFound = "Kein Maximalwert gefunden"
NoMinimumFound = "Kein Mindestwert gefunden"
NoZeroFound = "Keine Nullstelle gefunden"
@@ -29,5 +29,5 @@ NoIntersectionFound = "Kein Schnittpunkt gefunden"
NoPreimageFound = "Kein Urbild gefunden"
DerivativeFunctionColumn = "Spalte der Ableitungsfunktion"
HideDerivativeColumn = "Ableitungsfunktion ausblenden"
-AllowedCharactersAZaz09 = "Erlaubte Zeichen: A-Z, a-z, 0-9, _"
+AllowedCharactersAZaz09 = "Erlaubte Zeichen: A..Z, a..z, 0..9, _"
ReservedName = "Reserviertes Wort"
diff --git a/apps/graph/base.en.i18n b/apps/graph/base.en.i18n
index c65a3370e..347425517 100644
--- a/apps/graph/base.en.i18n
+++ b/apps/graph/base.en.i18n
@@ -20,8 +20,8 @@ Zeros = "Zeros"
Tangent = "Tangent"
Intersection = "Intersection"
Preimage = "Inverse image"
-SelectLowerBound = "Select lower bound"
-SelectUpperBound = "Select upper bound"
+SelectLowerBound = "Select lower bound "
+SelectUpperBound = "Select upper bound "
NoMaximumFound = "No maximum found"
NoMinimumFound = "No minimum found"
NoZeroFound = "No zero found"
@@ -29,5 +29,5 @@ NoIntersectionFound = "No intersection found"
NoPreimageFound = "No inverse image found"
DerivativeFunctionColumn = "Derivative function column"
HideDerivativeColumn = "Hide the derivative function"
-AllowedCharactersAZaz09 = "Allowed characters: A-Z, a-z, 0-9, _"
+AllowedCharactersAZaz09 = "Allowed characters: A.Z, a..z, 0..9, _"
ReservedName = "Reserved name"
diff --git a/apps/graph/base.es.i18n b/apps/graph/base.es.i18n
index c2200311d..967511092 100644
--- a/apps/graph/base.es.i18n
+++ b/apps/graph/base.es.i18n
@@ -20,8 +20,8 @@ Zeros = "Raíces"
Tangent = "Tangente"
Intersection = "Intersección"
Preimage = "Imagen inversa"
-SelectLowerBound = "Seleccionar el límite inferior"
-SelectUpperBound = "Seleccionar el límite superior"
+SelectLowerBound = "Seleccionar el límite inferior "
+SelectUpperBound = "Seleccionar el límite superior "
NoMaximumFound = "Níngun máximo encontrado"
NoMinimumFound = "Níngun mínimo encontrado"
NoZeroFound = "Ninguna raíz encontrada"
@@ -29,5 +29,5 @@ NoIntersectionFound = "Ninguna intersección encontrada"
NoPreimageFound = "Ninguna imagen inversa encontrada"
DerivativeFunctionColumn = "Columna de la derivada"
HideDerivativeColumn = "Ocultar la derivada"
-AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _"
+AllowedCharactersAZaz09 = "Caracteres permitidos : A..Z, a..z, 0..9, _"
ReservedName = "Nombre reservado"
diff --git a/apps/graph/base.fr.i18n b/apps/graph/base.fr.i18n
index d95a5ae63..dffa6585f 100644
--- a/apps/graph/base.fr.i18n
+++ b/apps/graph/base.fr.i18n
@@ -4,7 +4,7 @@ FunctionTab = "Fonctions"
AddFunction = "Ajouter une fonction"
DeleteFunction = "Supprimer la fonction"
CurveType = "Type de courbe"
-CartesianType = "Cartésien "
+CartesianType = "Cartésienne "
PolarType = "Polaire "
ParametricType = "Paramétrique "
IntervalT = "Intervalle t"
@@ -16,18 +16,18 @@ NoFunction = "Aucune fonction"
NoActivatedFunction = "Aucune fonction activée"
PlotOptions = "Options de la courbe"
Compute = "Calculer"
-Zeros = "Zéros"
+Zeros = "Racines"
Tangent = "Tangente"
Intersection = "Intersection"
Preimage = "Antécédent"
-SelectLowerBound = "Sélectionner la borne inférieure"
-SelectUpperBound = "Sélectionner la borne supérieure"
+SelectLowerBound = "Sélectionner la borne inférieure "
+SelectUpperBound = "Sélectionner la borne supérieure "
NoMaximumFound = "Aucun maximum trouvé"
NoMinimumFound = "Aucun minimum trouvé"
-NoZeroFound = "Aucun zéro trouvé"
+NoZeroFound = "Aucune racine trouvée"
NoIntersectionFound = "Aucune intersection trouvée"
NoPreimageFound = "Aucun antécédent trouvé"
DerivativeFunctionColumn = "Colonne de la fonction dérivée"
HideDerivativeColumn = "Masquer la fonction dérivée"
-AllowedCharactersAZaz09 = "Caractères autorisés : A-Z, a-z, 0-9, _"
+AllowedCharactersAZaz09 = "Caractères autorisés : A..Z, a..z, 0..9, _"
ReservedName = "Nom réservé"
diff --git a/apps/graph/base.it.i18n b/apps/graph/base.it.i18n
new file mode 100644
index 000000000..656d79726
--- /dev/null
+++ b/apps/graph/base.it.i18n
@@ -0,0 +1,33 @@
+FunctionApp = "Funzioni"
+FunctionAppCapital = "FUNZIONI"
+FunctionTab = "Funzioni"
+AddFunction = "Aggiungi funzione"
+DeleteFunction = "Cancella funzione"
+CurveType = "Tipo di curva"
+CartesianType = "Cartesiana "
+PolarType = "Polare "
+ParametricType = "Parametrica "
+IntervalT = "Intervallo t"
+IntervalTheta = "Intervallo θ"
+IntervalX = "Intervallo x"
+FunctionDomain = "Int. tracciamento"
+FunctionColor = "Colore della funzione"
+NoFunction = "Nessuna funzione"
+NoActivatedFunction = "Nessuna funzione attivata"
+PlotOptions = "Opzioni della curva"
+Compute = "Calcolare"
+Zeros = "Zeri"
+Tangent = "Tangente"
+Intersection = "Intersezione"
+Preimage = "Controimmagine"
+SelectLowerBound = "Scegliere il limite inferiore"
+SelectUpperBound = "Scegliere il limite superiore"
+NoMaximumFound = "Nessun massimo trovato"
+NoMinimumFound = "Nessun minimo trovato"
+NoZeroFound = "Nessuno zero trovato"
+NoIntersectionFound = "Nessuna intersezione trovata"
+NoPreimageFound = "Nessuna immagine trovata"
+DerivativeFunctionColumn = "Colonna della funzione derivata"
+HideDerivativeColumn = "Nascondere la funzione derivata"
+AllowedCharactersAZaz09 = "Caratteri consentiti : A-Z, a-z, 0-9, _"
+ReservedName = "Nome riservato"
diff --git a/apps/graph/base.nl.i18n b/apps/graph/base.nl.i18n
new file mode 100644
index 000000000..a809429e5
--- /dev/null
+++ b/apps/graph/base.nl.i18n
@@ -0,0 +1,33 @@
+FunctionApp = "Functies"
+FunctionAppCapital = "FUNCTIES"
+FunctionTab = "Functies"
+AddFunction = "Functie toevoegen"
+DeleteFunction = "Functie verwijderen"
+CurveType = "Kromme type"
+CartesianType = "Cartesiaans "
+PolarType = "Polair "
+ParametricType = "Parametrisch "
+IntervalT = "t interval"
+IntervalTheta = "θ interval"
+IntervalX = "x interval"
+FunctionDomain = "Plotbereik"
+FunctionColor = "Functiekleur"
+NoFunction = "Geen functie"
+NoActivatedFunction = "Geen functie is ingeschakelt"
+PlotOptions = "Plot opties"
+Compute = "Bereken"
+Zeros = "Nulpunten"
+Tangent = "Tangens"
+Intersection = "Snijpunt"
+Preimage = "Inverse beeld"
+SelectLowerBound = "Selecteer ondergrens "
+SelectUpperBound = "Selecteer bovengrens "
+NoMaximumFound = "Geen maximum gevonden"
+NoMinimumFound = "Geen minimum gevonden"
+NoZeroFound = "Geen nulpunt gevonden"
+NoIntersectionFound = "Geen snijpunt gevonden"
+NoPreimageFound = "Geen inverse beeld gevonden"
+DerivativeFunctionColumn = "Afgeleide functie kolom"
+HideDerivativeColumn = "Verberg de afgeleide functie"
+AllowedCharactersAZaz09 = "Toegestane tekens: A-Z, a-z, 0-9, _"
+ReservedName = "Voorbehouden naam"
diff --git a/apps/graph/base.pt.i18n b/apps/graph/base.pt.i18n
index 49ec2be81..707a10013 100644
--- a/apps/graph/base.pt.i18n
+++ b/apps/graph/base.pt.i18n
@@ -1,5 +1,5 @@
-FunctionApp = "Função"
-FunctionAppCapital = "FUNÇÃO"
+FunctionApp = "Funções"
+FunctionAppCapital = "FUNÇÕES"
FunctionTab = "Funções"
AddFunction = "Adicionar uma função"
DeleteFunction = "Eliminar a função"
@@ -16,18 +16,18 @@ NoFunction = "Nenhuma função"
NoActivatedFunction = "Sem função activada"
PlotOptions = "Opções da curva"
Compute = "Calcular"
-Zeros = "Raízes"
+Zeros = "Zeros"
Tangent = "Tangente"
-Intersection = "Intersecção"
+Intersection = "Interseção"
Preimage = "Imagem inversa"
-SelectLowerBound = "Selecionar limite superior"
-SelectUpperBound = "Selecionar limite inferior"
+SelectLowerBound = "Selecionar limite inferior"
+SelectUpperBound = "Selecionar limite superior"
NoMaximumFound = "Nenhum máximo encontrado"
NoMinimumFound = "Nenhum mínimo encontrado"
NoZeroFound = "Nenhuma raiz encontrada"
-NoIntersectionFound = "Nenhuma intersecção encontrada"
+NoIntersectionFound = "Nenhuma interseção encontrada"
NoPreimageFound = "Nenhuma imagem inversa encontrada"
DerivativeFunctionColumn = "Coluna da função derivada"
HideDerivativeColumn = "Esconder função derivada"
-AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _"
+AllowedCharactersAZaz09 = "Caracteres permitidos : A..Z, a..z, 0..9, _"
ReservedName = "Nome reservado"
diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp
index 74cc400dc..cc9dbb93a 100644
--- a/apps/graph/graph/graph_controller.cpp
+++ b/apps/graph/graph/graph_controller.cpp
@@ -1,16 +1,12 @@
#include "graph_controller.h"
#include "../app.h"
+#include
using namespace Poincare;
using namespace Shared;
namespace Graph {
-static inline float minFloat(float x, float y) { return x < y ? x : y; }
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-static inline double minDouble(double x, double y) { return x < y ? x : y; }
-static inline double maxDouble(double x, double y) { return x > y ? x : y; }
-
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -52,10 +48,10 @@ void GraphController::interestingFunctionRange(ExpiringPointertMin()));
assert(!std::isnan(f->tMax()));
- const double tMin = maxFloat(f->tMin(), resultxMin);
- const double tMax = minFloat(f->tMax(), resultxMax);
+ const double tMin = std::max(f->tMin(), resultxMin);
+ const double tMax = std::min(f->tMax(), resultxMax);
const double step = (tMax - tMin) / (2.0 * (m_view.bounds().width() - 1.0));
interestingFunctionRange(f, tMin, tMax, step, &resultxMin, &resultxMax, &resultyMin, &resultyMax);
}
@@ -129,12 +125,12 @@ float GraphController::interestingXHalfRange() const {
ExpiringPointer f = store->modelForRecord(store->activeRecordAtIndex(i));
float fRange = f->expressionReduced(context).characteristicXRange(context, Poincare::Preferences::sharedPreferences()->angleUnit());
if (!std::isnan(fRange) && !std::isinf(fRange)) {
- characteristicRange = maxFloat(fRange, characteristicRange);
+ characteristicRange = std::max(fRange, characteristicRange);
}
// Compute the combined range of the functions
assert(f->plotType() == ContinuousFunction::PlotType::Cartesian); // So that tMin tMax represents xMin xMax
- tMin = minDouble(tMin, f->tMin());
- tMax = maxDouble(tMax, f->tMax());
+ tMin = std::min(tMin, f->tMin());
+ tMax = std::max(tMax, f->tMax());
}
constexpr float rangeMultiplicator = 1.6f;
if (characteristicRange > 0.0f ) {
@@ -145,7 +141,7 @@ float GraphController::interestingXHalfRange() const {
if (tMin >= -defaultXHalfRange && tMax <= defaultXHalfRange) {
/* If the combined Range of the functions is smaller than the default range,
* use it. */
- float f = rangeMultiplicator * (float)maxDouble(std::fabs(tMin), std::fabs(tMax));
+ float f = rangeMultiplicator * (float)std::max(std::fabs(tMin), std::fabs(tMax));
return (std::isnan(f) || std::isinf(f)) ? defaultXHalfRange : f;
}
return defaultXHalfRange;
@@ -226,7 +222,7 @@ void GraphController::jumpToLeftRightCurve(double t, int direction, int function
if (currentXDelta <= xDelta) {
double potentialNextTMin = f->tMin();
double potentialNextTMax = f->tMax();
- double potentialNextT = maxDouble(potentialNextTMin, minDouble(potentialNextTMax, t));
+ double potentialNextT = std::max(potentialNextTMin, std::min(potentialNextTMax, t));
Coordinate2D xy = f->evaluateXYAtParameter(potentialNextT, App::app()->localContext());
if (currentXDelta < xDelta || std::abs(xy.x2() - m_cursor->y()) < std::abs(nextY - m_cursor->y())) {
nextCurveIndex = i;
diff --git a/apps/graph/graph/graph_controller_helper.cpp b/apps/graph/graph/graph_controller_helper.cpp
index 0c21be79c..ad42ac727 100644
--- a/apps/graph/graph/graph_controller_helper.cpp
+++ b/apps/graph/graph/graph_controller_helper.cpp
@@ -3,15 +3,13 @@
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include
+#include
using namespace Shared;
using namespace Poincare;
namespace Graph {
-static inline double minDouble(double x, double y) { return x < y ? x : y; }
-static inline double maxDouble(double x, double y) { return x > y ? x : y; }
-
bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCursor * cursor, int direction, Shared::InteractiveCurveViewRange * range, int numberOfStepsInGradUnit, Ion::Storage::Record record, bool fast) {
ExpiringPointer function = App::app()->functionStore()->modelForRecord(record);
double tCursorPosition = cursor->t();
@@ -34,7 +32,7 @@ bool GraphControllerHelper::privateMoveCursorHorizontally(Shared::CurveViewCurso
step *= 5.0;
}
t += dir * step;
- t = maxDouble(tMin, minDouble(tMax, t));
+ t = std::max(tMin, std::min(tMax, t));
Coordinate2D xy = function->evaluateXYAtParameter(t, App::app()->localContext());
cursor->moveTo(t, xy.x1(), xy.x2());
return true;
diff --git a/apps/graph/list/text_field_function_title_cell.cpp b/apps/graph/list/text_field_function_title_cell.cpp
index a1d138e24..38f0284f2 100644
--- a/apps/graph/list/text_field_function_title_cell.cpp
+++ b/apps/graph/list/text_field_function_title_cell.cpp
@@ -1,12 +1,10 @@
#include "text_field_function_title_cell.h"
#include "list_controller.h"
#include
+#include
namespace Graph {
-static inline float minFloat(float x, float y) { return x < y ? x : y; }
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-
TextFieldFunctionTitleCell::TextFieldFunctionTitleCell(ListController * listController, Orientation orientation, const KDFont * font) :
Shared::FunctionTitleCell(orientation),
Responder(listController),
@@ -29,8 +27,8 @@ void TextFieldFunctionTitleCell::setEditing(bool editing) {
int extensionLength = UTF8Helper::HasCodePoint(previousText, UCodePointGreekSmallLetterTheta) ? Shared::Function::k_parenthesedThetaArgumentByteLength : Shared::Function::k_parenthesedXNTArgumentByteLength;
m_textField.setExtensionLength(extensionLength);
m_textField.setEditing(true);
- m_textField.setText(previousText);
m_textField.setDraftTextBufferSize(Poincare::SymbolAbstract::k_maxNameSize+extensionLength);
+ m_textField.setText(previousText);
}
bool TextFieldFunctionTitleCell::isEditing() const {
@@ -56,9 +54,9 @@ void TextFieldFunctionTitleCell::layoutSubviews(bool force) {
KDRect frame = subviewFrame();
m_textField.setFrame(frame, force);
KDCoordinate maxTextFieldX = frame.width() - m_textField.minimalSizeForOptimalDisplay().width();
- float horizontalAlignment = maxFloat(
+ float horizontalAlignment = std::max(
0.0f,
- minFloat(
+ std::min(
1.0f,
((float)(maxTextFieldX - k_textFieldRightMargin))/((float)maxTextFieldX)));
m_textField.setAlignment(horizontalAlignment, verticalAlignment());
diff --git a/apps/hardware_test/Makefile b/apps/hardware_test/Makefile
index 339b8887c..960ebcc3c 100644
--- a/apps/hardware_test/Makefile
+++ b/apps/hardware_test/Makefile
@@ -16,4 +16,4 @@ app_hardware_test_src = $(addprefix apps/hardware_test/,\
vblank_test_controller.cpp \
)
-app_src += $(app_hardware_test_src)
+apps_src += $(app_hardware_test_src)
diff --git a/apps/hardware_test/keyboard_view.cpp b/apps/hardware_test/keyboard_view.cpp
index 1b15f28df..9af6b9169 100644
--- a/apps/hardware_test/keyboard_view.cpp
+++ b/apps/hardware_test/keyboard_view.cpp
@@ -59,7 +59,7 @@ void KeyboardView::drawKey(int keyIndex, KDContext * ctx, KDRect rect) const {
KDColor KeyboardView::keyColor(Ion::Keyboard::Key key) const {
if (!m_keyboardModel.belongsToTestedKeysSubset(key)) {
- return Palette::GreyBright;
+ return Palette::ListCellBorder;
}
if (m_keyboardModel.testedKey() == key) {
return KDColorBlue;
diff --git a/apps/helpers.mk b/apps/helpers.mk
new file mode 100644
index 000000000..1d86bb88c
--- /dev/null
+++ b/apps/helpers.mk
@@ -0,0 +1,19 @@
+# i18n helpers
+
+# Should be called as: i18n_for_locale basename locale
+define i18n_for_locale
+$(addprefix apps/, $(addprefix $(1), $(addprefix ., $(addsuffix .i18n,$(2)))))
+endef
+
+# Should be called as: i18n_without_universal_for basename
+# Adds the basename.**.i18n for all locales in EPSILON_I18N
+define i18n_without_universal_for
+$(foreach LOCALE,$(EPSILON_I18N),$(call i18n_for_locale, $(1), $(LOCALE)))
+endef
+
+# Should be called as: i18n_with_universal_for basename
+# Adds basename.**.i18n for all EPSILON_I18N locales + basename.universal.i18n
+define i18n_with_universal_for
+$(call i18n_without_universal_for,$(1))
+$(call i18n_for_locale,$(1),universal)
+endef
diff --git a/apps/home/Makefile b/apps/home/Makefile
index 319cbb29b..fc45cb479 100644
--- a/apps/home/Makefile
+++ b/apps/home/Makefile
@@ -4,13 +4,6 @@ app_home_src = $(addprefix apps/home/,\
controller.cpp \
)
-app_src += $(app_home_src)
+apps_src += $(app_home_src)
-i18n_files += $(addprefix apps/home/,\
- base.de.i18n \
- base.en.i18n \
- base.es.i18n \
- base.fr.i18n \
- base.pt.i18n \
- base.hu.i18n \
-)
+i18n_files += $(call i18n_without_universal_for,home/base)
diff --git a/apps/home/app.cpp b/apps/home/app.cpp
index 5c5cc8f9c..51194e4f7 100644
--- a/apps/home/app.cpp
+++ b/apps/home/app.cpp
@@ -24,9 +24,18 @@ App::Descriptor * App::Snapshot::descriptor() {
return &descriptor;
}
+void App::didBecomeActive(Window * window) {
+ ::App::didBecomeActive(window);
+ m_window = window;
+}
+
+void App::redraw() {
+ m_window->redraw(true);
+}
+
App::App(Snapshot * snapshot) :
::App(snapshot, &m_controller, I18n::Message::Warning),
- m_controller(&m_modalViewController, snapshot)
+ m_controller(&m_modalViewController, snapshot, this)
{
}
diff --git a/apps/home/app.h b/apps/home/app.h
index 59325a440..a912ec3ee 100644
--- a/apps/home/app.h
+++ b/apps/home/app.h
@@ -18,6 +18,8 @@ public:
App * unpack(Container * container) override;
Descriptor * descriptor() override;
};
+ void redraw();
+ virtual void didBecomeActive(Window * window);
static App * app() {
return static_cast(Container::activeApp());
}
@@ -25,9 +27,18 @@ public:
return static_cast(::App::snapshot());
}
TELEMETRY_ID("Home");
+#if HOME_DISPLAY_EXTERNALS
+ int heapSize() { return k_externalHeapSize; }
+ char * heap() { return m_externalHeap; }
+#endif
private:
App(Snapshot * snapshot);
Controller m_controller;
+#if HOME_DISPLAY_EXTERNALS
+ static constexpr int k_externalHeapSize = 80000;
+ char m_externalHeap[k_externalHeapSize];
+#endif
+ Window * m_window;
};
}
diff --git a/apps/home/app_cell.cpp b/apps/home/app_cell.cpp
index 27600e379..adac67b32 100644
--- a/apps/home/app_cell.cpp
+++ b/apps/home/app_cell.cpp
@@ -1,12 +1,14 @@
#include "app_cell.h"
#include
+#include
+#include
namespace Home {
AppCell::AppCell() :
HighlightCell(),
- m_nameView(KDFont::SmallFont, (I18n::Message)0, 0.5f, 0.5f, Palette::HomeCellText, Palette::HomeCellTextActive),
- m_visible(true)
+ m_nameView(KDFont::SmallFont, (I18n::Message)0, 0.5f, 0.5f, Palette::HomeCellText, Palette::HomeCellBackground),
+ m_visible(true), m_external_app(false)
{
}
@@ -31,9 +33,21 @@ void AppCell::layoutSubviews(bool force) {
m_nameView.setFrame(KDRect((bounds().width()-nameSize.width())/2-k_nameWidthMargin, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, nameSize.width()+2*k_nameWidthMargin, nameSize.height()+2*k_nameHeightMargin), force);
}
+void AppCell::setExtAppDescriptor(const char* name, const Image* icon) {
+ m_external_app = true;
+ m_iconView.setImage(icon);
+ m_nameView.setText(name);
+ m_nameView.setTextColor(Palette::HomeCellTextExternal);
+ m_nameView.setMessage(I18n::Message::Default);
+ layoutSubviews();
+}
+
void AppCell::setAppDescriptor(::App::Descriptor * descriptor) {
+ m_external_app = false;
m_iconView.setImage(descriptor->icon());
m_nameView.setMessage(descriptor->name());
+ m_nameView.setTextColor(Palette::HomeCellText);
+ m_nameView.setText(nullptr);
layoutSubviews();
}
@@ -45,7 +59,7 @@ void AppCell::setVisible(bool visible) {
}
void AppCell::reloadCell() {
- m_nameView.setTextColor(isHighlighted() ? Palette::HomeCellTextActive : Palette::HomeCellText);
+ m_nameView.setTextColor(isHighlighted() ? (m_external_app ? Palette::HomeCellTextExternalActive : Palette::HomeCellTextActive) : (m_external_app ? Palette::HomeCellTextExternal : Palette::HomeCellText));
m_nameView.setBackgroundColor(isHighlighted() ? Palette::HomeCellBackgroundActive : Palette::HomeCellBackground);
}
diff --git a/apps/home/app_cell.h b/apps/home/app_cell.h
index 73cb7e0f9..fb4c682c7 100644
--- a/apps/home/app_cell.h
+++ b/apps/home/app_cell.h
@@ -17,6 +17,7 @@ public:
void setVisible(bool visible);
void reloadCell() override;
void setAppDescriptor(::App::Descriptor * appDescriptor);
+ void setExtAppDescriptor(const char* name, const Image* icon);
private:
static constexpr KDCoordinate k_iconMargin = 22;
static constexpr KDCoordinate k_iconWidth = 55;
@@ -26,6 +27,7 @@ private:
ImageView m_iconView;
MessageTextView m_nameView;
bool m_visible;
+ bool m_external_app;
};
}
diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n
index a28432cae..5243a554b 100644
--- a/apps/home/base.de.i18n
+++ b/apps/home/base.de.i18n
@@ -1,4 +1,4 @@
Apps = "Anwendungen"
AppsCapital = "STAY HOME(GA)"
ForbidenAppInExamMode1 = "Diese Anwendung ist im"
-ForbidenAppInExamMode2 = "Prüfungsmodus verboten"
+ForbidenAppInExamMode2 = "Prüfungsmodus nicht erlaubt."
diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n
index fd5f8048b..8280b4584 100644
--- a/apps/home/base.fr.i18n
+++ b/apps/home/base.fr.i18n
@@ -1,4 +1,4 @@
Apps = "Applications"
-AppsCapital = "STAY HOME(GA)"
-ForbidenAppInExamMode1 = "This application is"
-ForbidenAppInExamMode2 = "forbidden in exam mode"
+AppsCapital = "OMEGA"
+ForbidenAppInExamMode1 = "Cette application n'est"
+ForbidenAppInExamMode2 = "pas autorisée en mode examen."
diff --git a/apps/home/base.it.i18n b/apps/home/base.it.i18n
new file mode 100644
index 000000000..95373c040
--- /dev/null
+++ b/apps/home/base.it.i18n
@@ -0,0 +1,4 @@
+Apps = "Applicazioni"
+AppsCapital = "OMEGA"
+ForbidenAppInExamMode1 = "Questa applicazione è"
+ForbidenAppInExamMode2 = "proibita nella modalità d'esame"
diff --git a/apps/home/base.nl.i18n b/apps/home/base.nl.i18n
new file mode 100644
index 000000000..dd6a89919
--- /dev/null
+++ b/apps/home/base.nl.i18n
@@ -0,0 +1,4 @@
+Apps = "Applicaties"
+AppsCapital = "OMEGA"
+ForbidenAppInExamMode1 = "Deze applicatie is"
+ForbidenAppInExamMode2 = "uitgesloten in examenstand"
diff --git a/apps/home/base.pt.i18n b/apps/home/base.pt.i18n
index 8031958d8..f3efc8ffd 100644
--- a/apps/home/base.pt.i18n
+++ b/apps/home/base.pt.i18n
@@ -1,4 +1,4 @@
Apps = "Aplicações"
-AppsCapital = "STAY HOME(GA)"
-ForbidenAppInExamMode1 = "This application is"
-ForbidenAppInExamMode2 = "forbidden in exam mode"
+AppsCapital = "OMEGA"
+ForbidenAppInExamMode1 = "Esta aplicação é"
+ForbidenAppInExamMode2 = "proibida no Modo de Exame"
diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp
index 5969657a3..2faa2e488 100644
--- a/apps/home/controller.cpp
+++ b/apps/home/controller.cpp
@@ -2,10 +2,18 @@
#include "app.h"
#include "../apps_container.h"
#include "../global_preferences.h"
+#include "../exam_mode_configuration.h"
+
extern "C" {
#include
}
+#ifdef HOME_DISPLAY_EXTERNALS
+#include "../external/external_icon.h"
+#include "../external/archive.h"
+#include
+#endif
+
namespace Home {
Controller::ContentView::ContentView(Controller * controller, SelectableTableViewDataSource * selectionDataSource) :
@@ -49,24 +57,56 @@ void Controller::ContentView::layoutSubviews(bool force) {
m_selectableTableView.setFrame(bounds(), force);
}
-Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource) :
+Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, ::App * app) :
ViewController(parentResponder),
m_view(this, selectionDataSource)
{
+ m_app = app;
}
bool Controller::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
AppsContainer * container = AppsContainer::sharedAppsContainer();
- ::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1);
- if (((GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSymNoText) && selectedSnapshot->descriptor()->examinationLevel() < 2) ||
- ((GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Standard || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSym) && selectedSnapshot->descriptor()->examinationLevel() < 1)) {
+ int index = selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1;
+
+#ifdef HOME_DISPLAY_EXTERNALS
+ if (index >= container->numberOfApps()) {
+ if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Dutch || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSymNoText || GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::NoSym) {
+ App::app()->displayWarning(I18n::Message::ForbidenAppInExamMode1, I18n::Message::ForbidenAppInExamMode2);
+ } else {
+ External::Archive::File executable;
+ if (External::Archive::executableAtIndex(index - container->numberOfApps(), executable)) {
+ uint32_t res = External::Archive::executeFile(executable.name, ((App *)m_app)->heap(), ((App *)m_app)->heapSize());
+ ((App*)m_app)->redraw();
+ switch(res) {
+ case 0:
+ break;
+ case 1:
+ Container::activeApp()->displayWarning(I18n::Message::ExternalAppApiMismatch);
+ break;
+ case 2:
+ Container::activeApp()->displayWarning(I18n::Message::StorageMemoryFull1);
+ break;
+ default:
+ Container::activeApp()->displayWarning(I18n::Message::ExternalAppExecError);
+ break;
+ }
+ return true;
+ }
+ }
+ } else {
+#endif
+ ::App::Snapshot * selectedSnapshot = container->appSnapshotAtIndex(index);
+ if (ExamModeConfiguration::appIsForbiddenInExamMode(selectedSnapshot->descriptor()->examinationLevel(), GlobalPreferences::sharedGlobalPreferences()->examMode())) {
App::app()->displayWarning(I18n::Message::ForbidenAppInExamMode1, I18n::Message::ForbidenAppInExamMode2);
} else {
bool switched = container->switchTo(selectedSnapshot);
assert(switched);
(void) switched; // Silence compilation warning about unused variable.
}
+#ifdef HOME_DISPLAY_EXTERNALS
+ }
+#endif
return true;
}
@@ -134,7 +174,37 @@ void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
AppsContainer * container = AppsContainer::sharedAppsContainer();
int appIndex = (j*k_numberOfColumns+i)+1;
if (appIndex >= container->numberOfApps()) {
+#ifdef HOME_DISPLAY_EXTERNALS
+ External::Archive::File app_file;
+
+
+ if (External::Archive::executableAtIndex(appIndex - container->numberOfApps(), app_file)) {
+ char temp_name_buffer[100];
+ strlcpy(temp_name_buffer, app_file.name, 94);
+ strlcat(temp_name_buffer, ".icon", 99);
+
+ int img_index = External::Archive::indexFromName(temp_name_buffer);
+
+ if (img_index != -1) {
+ External::Archive::File image_file;
+ if (External::Archive::fileAtIndex(img_index, image_file)) {
+ const Image* img = new Image(55, 56, image_file.data, image_file.dataLength);
+ appCell->setExtAppDescriptor(app_file.name, img);
+
+ } else {
+ appCell->setExtAppDescriptor(app_file.name, ImageStore::ExternalIcon);
+ }
+ } else {
+ appCell->setExtAppDescriptor(app_file.name, ImageStore::ExternalIcon);
+ }
+
+ appCell->setVisible(true);
+ } else {
+ appCell->setVisible(false);
+ }
+#else
appCell->setVisible(false);
+#endif
} else {
appCell->setVisible(true);
::App::Descriptor * descriptor = container->appSnapshotAtIndex(appIndex)->descriptor();
@@ -145,14 +215,29 @@ void Controller::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
int Controller::numberOfIcons() const {
AppsContainer * container = AppsContainer::sharedAppsContainer();
assert(container->numberOfApps() > 0);
+#ifdef HOME_DISPLAY_EXTERNALS
+ return container->numberOfApps() - 1 + External::Archive::numberOfExecutables();
+#else
return container->numberOfApps() - 1;
+#endif
}
void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
- AppsContainer * container = AppsContainer::sharedAppsContainer();
if (withinTemporarySelection) {
return;
}
+ /* To prevent the selectable table view to select cells that are unvisible,
+ * we reselect the previous selected cell as soon as the selected cell is
+ * unvisible. This trick does not create an endless loop as we ensure not to
+ * stay on a unvisible cell and to initialize the first cell on a visible one
+ * (so the previous one is always visible). */
+ int appIndex = (t->selectedColumn()+t->selectedRow()*k_numberOfColumns)+1;
+ if (appIndex >= this->numberOfIcons()+1) {
+ t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY);
+ }
+}
+
+void Controller::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
/* If the number of apps (including home) is != 3*n+1, when we display the
* lowest icons, the other(s) are empty. As no icon is thus redrawn on the
* previous ones, the cell is not cleaned. We need to redraw a white rect on
@@ -162,16 +247,7 @@ void Controller::tableViewDidChangeSelection(SelectableTableView * t, int previo
* background complete redrawing but the code is a bit
* clumsy. */
if (t->selectedRow() == numberOfRows()-1) {
- m_view.reloadBottomRow(this, container->numberOfApps()-1, k_numberOfColumns);
- }
- /* To prevent the selectable table view to select cells that are unvisible,
- * we reselect the previous selected cell as soon as the selected cell is
- * unvisible. This trick does not create an endless loop as we ensure not to
- * stay on a unvisible cell and to initialize the first cell on a visible one
- * (so the previous one is always visible). */
- int appIndex = (t->selectedColumn()+t->selectedRow()*k_numberOfColumns)+1;
- if (appIndex >= container->numberOfApps()) {
- t->selectCellAtLocation(previousSelectedCellX, previousSelectedCellY);
+ m_view.reloadBottomRow(this, this->numberOfIcons(), k_numberOfColumns);
}
}
diff --git a/apps/home/controller.h b/apps/home/controller.h
index 428de3ccb..90a5604a3 100644
--- a/apps/home/controller.h
+++ b/apps/home/controller.h
@@ -8,7 +8,7 @@ namespace Home {
class Controller : public ViewController, public SimpleTableViewDataSource, public SelectableTableViewDelegate {
public:
- Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource);
+ Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, App * app);
View * view() override;
@@ -17,14 +17,15 @@ public:
void viewWillAppear() override;
void viewDidDisappear() override;
- virtual int numberOfRows() const override;
- virtual int numberOfColumns() const override;
- virtual KDCoordinate cellHeight() override;
- virtual KDCoordinate cellWidth() override;
- virtual HighlightCell * reusableCell(int index) override;
- virtual int reusableCellCount() const override;
+ int numberOfRows() const override;
+ int numberOfColumns() const override;
+ KDCoordinate cellHeight() override;
+ KDCoordinate cellWidth() override;
+ HighlightCell * reusableCell(int index) override;
+ int reusableCellCount() const override;
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
+ void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
private:
int numberOfIcons() const;
SelectableTableViewDataSource * selectionDataSource() const;
@@ -49,6 +50,7 @@ private:
static constexpr int k_cellWidth = 104;
ContentView m_view;
AppCell m_cells[k_maxNumberOfCells];
+ App * m_app;
};
}
diff --git a/apps/i18n.py b/apps/i18n.py
index 4f4b44d49..a6130e49a 100644
--- a/apps/i18n.py
+++ b/apps/i18n.py
@@ -12,6 +12,23 @@ import unicodedata
import argparse
import io
+
+parser = argparse.ArgumentParser(description="Process some i18n files.")
+parser.add_argument('--header', help='the .h file to generate')
+parser.add_argument('--implementation', help='the .cpp file to generate')
+parser.add_argument('--locales', nargs='+', help='locale to actually generate')
+parser.add_argument('--codepoints', help='the code_points.h file')
+parser.add_argument('--files', nargs='+', help='an i18n file')
+parser.add_argument('--generateISO6391locales', type=int, nargs='+', help='whether to generate the ISO6391 codes for the languages (for instance "en" for english)')
+
+args = parser.parse_args()
+
+def generate_ISO6391():
+ return args.generateISO6391locales[0] == 1
+
+def has_glyph(glyph):
+ return glyph in codepoints
+
def source_definition(i18n_string):
s = unicodedata.normalize("NFKD", i18n_string)
result = u"\""
@@ -29,6 +46,9 @@ def source_definition(i18n_string):
# Remove the uppercase characters with combining chars
checkForCombining = s[i].isupper()
result = result + s[i]
+ if not has_glyph(s[i]):
+ sys.stderr.write(s[i] + " (" + str(hex(ord(s[i]))) + ") is not a character present in " + args.codepoints + " . Exiting !\n")
+ sys.exit(-1)
i = i+1
result = result + u"\""
return result.encode("utf-8")
@@ -73,6 +93,27 @@ def parse_files(files):
data[locale][name] = definition
return {"messages": sorted(messages), "universal_messages": sorted(universal_messages), "data": data}
+def parse_codepoints(file):
+ codepoints = []
+ with io.open(file, "r", encoding='utf-8') as file:
+ IsCodePoint = False
+ for line in file:
+ if "};" in line:
+ IsCodePoint = False
+ if IsCodePoint:
+ start = line.find('0x')
+ stop = line.find(',')
+ if not (start == -1 or stop == -1):
+ hexstring = line[start:stop]
+ value = int(hexstring, 16)
+ char = chr(value)
+ codepoints.append(char)
+ if "CodePoints[]" in line:
+ IsCodePoint = True
+ return codepoints
+
+codepoints = parse_codepoints(args.codepoints)
+
def print_header(data, path, locales):
f = open(path, "w")
f.write("#ifndef APPS_I18N_H\n")
@@ -95,9 +136,10 @@ def print_header(data, path, locales):
# Languages enumeration
f.write("enum class Language : uint16_t {\n")
- f.write(" Default = 0,\n")
+ index = 0
for locale in locales:
- f.write(" " + locale.upper() + ",\n")
+ f.write(" " + locale.upper() + (" = 0" if (index < 1) else "") +",\n")
+ index = index + 1
f.write("};\n\n")
# Language names
@@ -105,6 +147,14 @@ def print_header(data, path, locales):
for locale in locales:
f.write(" Message::Language" + locale.upper() + ",\n")
f.write("};\n\n")
+
+ if generate_ISO6391():
+ # Language ISO639-1 codes
+ f.write("constexpr const Message LanguageISO6391Names[NumberOfLanguages] = {\n");
+ for locale in locales:
+ f.write(" Message::LanguageISO6391" + locale.upper() + ",\n")
+ f.write("};\n\n")
+
f.write("}\n\n")
f.write("#endif\n")
f.close()
@@ -159,32 +209,21 @@ def print_implementation(data, path, locales):
# Write the translate method
- f.write("const char * translate(Message m, Language l) {\n")
+ f.write("const char * translate(Message m) {\n")
f.write(" assert(m != Message::LocalizedMessageMarker);\n")
f.write(" int localizedMessageOffset = (int)Message::LocalizedMessageMarker+1;\n")
f.write(" if ((int)m < localizedMessageOffset) {\n")
f.write(" assert(universalMessages[(int)m] != nullptr);\n")
f.write(" return universalMessages[(int)m];\n")
f.write(" }\n")
- f.write(" int languageIndex = (int)l;\n")
- f.write(" if (l == Language::Default) {\n")
- f.write(" languageIndex = (int) GlobalPreferences::sharedGlobalPreferences()->language();\n")
- f.write(" }\n")
- f.write(" assert(languageIndex > 0);\n")
+ f.write(" int languageIndex = (int)GlobalPreferences::sharedGlobalPreferences()->language();\n")
f.write(" int messageIndex = (int)m - localizedMessageOffset;\n")
- f.write(" assert((messageIndex*NumberOfLanguages+languageIndex-1)*sizeof(char *) < sizeof(messages));\n")
- f.write(" return messages[messageIndex][languageIndex-1];\n")
+ f.write(" assert((messageIndex*NumberOfLanguages+languageIndex)*sizeof(char *) < sizeof(messages));\n")
+ f.write(" return messages[messageIndex][languageIndex];\n")
f.write("}\n\n")
f.write("}\n")
f.close()
-parser = argparse.ArgumentParser(description="Process some i18n files.")
-parser.add_argument('--header', help='the .h file to generate')
-parser.add_argument('--implementation', help='the .cpp file to generate')
-parser.add_argument('--locales', nargs='+', help='locale to actually generate')
-parser.add_argument('--files', nargs='+', help='an i18n file')
-
-args = parser.parse_args()
data = parse_files(args.files)
if args.header:
print_header(data, args.header, args.locales)
diff --git a/apps/language_de_iso6391.universal.i18n b/apps/language_de_iso6391.universal.i18n
new file mode 100644
index 000000000..8d811650d
--- /dev/null
+++ b/apps/language_de_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391DE = "de"
diff --git a/apps/language_en_iso6391.universal.i18n b/apps/language_en_iso6391.universal.i18n
new file mode 100644
index 000000000..54972ae84
--- /dev/null
+++ b/apps/language_en_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391EN = "en"
diff --git a/apps/language_es_iso6391.universal.i18n b/apps/language_es_iso6391.universal.i18n
new file mode 100644
index 000000000..615cbf978
--- /dev/null
+++ b/apps/language_es_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391ES = "es"
diff --git a/apps/language_fr_iso6391.universal.i18n b/apps/language_fr_iso6391.universal.i18n
new file mode 100644
index 000000000..905e6f179
--- /dev/null
+++ b/apps/language_fr_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391FR = "fr"
diff --git a/apps/language_hu_iso6391.universal.i18n b/apps/language_hu_iso6391.universal.i18n
new file mode 100644
index 000000000..98bc27af1
--- /dev/null
+++ b/apps/language_hu_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391HU = "hu"
diff --git a/apps/language_it.universal.i18n b/apps/language_it.universal.i18n
new file mode 100644
index 000000000..5914a13e6
--- /dev/null
+++ b/apps/language_it.universal.i18n
@@ -0,0 +1 @@
+LanguageIT = "Italiano "
diff --git a/apps/language_it_iso6391.universal.i18n b/apps/language_it_iso6391.universal.i18n
new file mode 100644
index 000000000..6a72c7216
--- /dev/null
+++ b/apps/language_it_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391IT = "it"
diff --git a/apps/language_nl.universal.i18n b/apps/language_nl.universal.i18n
new file mode 100644
index 000000000..a509bf7b0
--- /dev/null
+++ b/apps/language_nl.universal.i18n
@@ -0,0 +1 @@
+LanguageNL = "Nederlands "
diff --git a/apps/language_nl_iso6391.universal.i18n b/apps/language_nl_iso6391.universal.i18n
new file mode 100644
index 000000000..0fef88fd0
--- /dev/null
+++ b/apps/language_nl_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391NL = "nl"
diff --git a/apps/language_pt_iso6391.universal.i18n b/apps/language_pt_iso6391.universal.i18n
new file mode 100644
index 000000000..15218e760
--- /dev/null
+++ b/apps/language_pt_iso6391.universal.i18n
@@ -0,0 +1 @@
+LanguageISO6391PT = "pt"
diff --git a/apps/main.cpp b/apps/main.cpp
index 8b5a13e61..d0dcf00b7 100644
--- a/apps/main.cpp
+++ b/apps/main.cpp
@@ -31,11 +31,13 @@ void ion_main(int argc, const char * const argv[]) {
* $ ./epsilon.elf --language fr
*/
if (strcmp(argv[i], "--language") == 0 && argc > i+1) {
- const char * languageIdentifiers[] = {"none", "en", "fr", "es", "de", "pt"};
const char * requestedLanguageId = argv[i+1];
- for (int i=0; isetLanguage((I18n::Language)i);
+ if (strcmp(requestedLanguageId, "none") == 0) {
+ continue;
+ }
+ for (int j = 0; j < I18n::NumberOfLanguages; j++) {
+ if (strcmp(requestedLanguageId, I18n::translate(I18n::LanguageISO6391Names[j])) == 0) {
+ GlobalPreferences::sharedGlobalPreferences()->setLanguage((I18n::Language)j);
break;
}
}
diff --git a/apps/math_toolbox.cpp b/apps/math_toolbox.cpp
index cbc9871a3..6a282996c 100644
--- a/apps/math_toolbox.cpp
+++ b/apps/math_toolbox.cpp
@@ -23,7 +23,7 @@ const ToolboxMessageTree calculChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::DiffCommandWithArg, I18n::Message::DerivateNumber, false, I18n::Message::DiffCommand),
ToolboxMessageTree::Leaf(I18n::Message::IntCommandWithArg, I18n::Message::Integral, false, I18n::Message::IntCommand),
ToolboxMessageTree::Leaf(I18n::Message::SumCommandWithArg, I18n::Message::Sum, false, I18n::Message::SumCommand),
- ToolboxMessageTree::Leaf(I18n::Message::ProductCommandWithArg, I18n::Message::Product, false, I18n::Message::ProductCommand)
+ ToolboxMessageTree::Leaf(I18n::Message::ProductCommandWithArg, I18n::Message::Product, false, I18n::Message::ProductCommand),
};
const ToolboxMessageTree complexChildren[] = {
@@ -109,13 +109,28 @@ const ToolboxMessageTree unitDistanceMeterChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceMeterPicoSymbol, I18n::Message::UnitDistanceMeterPico),
};
+const ToolboxMessageTree unitDistanceImperialChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceInchSymbol, I18n::Message::UnitDistanceInch),
+ ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceFootSymbol, I18n::Message::UnitDistanceFoot),
+ ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceYardSymbol, I18n::Message::UnitDistanceYard),
+ ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceMileSymbol, I18n::Message::UnitDistanceMile),
+};
+
const ToolboxMessageTree unitDistanceChildren[] = {
ToolboxMessageTree::Node(I18n::Message::UnitDistanceMeterMenu, unitDistanceMeterChildren),
+ ToolboxMessageTree::Node(I18n::Message::UnitDistanceImperialMenu, unitDistanceImperialChildren),
ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceAstronomicalUnitSymbol, I18n::Message::UnitDistanceAstronomicalUnit),
ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceLightYearSymbol, I18n::Message::UnitDistanceLightYear),
ToolboxMessageTree::Leaf(I18n::Message::UnitDistanceParsecSymbol, I18n::Message::UnitDistanceParsec)};
+const ToolboxMessageTree unitMassImperialChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::UnitMassPoundSymbol, I18n::Message::UnitMassPound),
+ ToolboxMessageTree::Leaf(I18n::Message::UnitMassOunceSymbol, I18n::Message::UnitMassOunce),
+ ToolboxMessageTree::Leaf(I18n::Message::UnitMassTonSymbol, I18n::Message::UnitMassTon),
+};
+
const ToolboxMessageTree unitMassChildren[] = {
+ ToolboxMessageTree::Node(I18n::Message::UnitMassImperialMenu, unitMassImperialChildren),
ToolboxMessageTree::Leaf(I18n::Message::UnitMassTonneSymbol, I18n::Message::UnitMassTonne),
ToolboxMessageTree::Leaf(I18n::Message::UnitMassGramKiloSymbol, I18n::Message::UnitMassGramKilo),
ToolboxMessageTree::Leaf(I18n::Message::UnitMassGramSymbol, I18n::Message::UnitMassGram),
@@ -142,6 +157,19 @@ const ToolboxMessageTree unitAmountMoleChildren[] = {
const ToolboxMessageTree unitLuminousIntensityChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitLuminousIntensityCandelaSymbol, I18n::Message::UnitLuminousIntensityCandela)};
+
+const ToolboxMessageTree unitLuminousFluxChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::UnitLuminousFluxLumenSymbol, I18n::Message::UnitLuminousFluxLumen),
+};
+
+const ToolboxMessageTree unitSolidAngleChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::UnitSolidAngleSteradianSymbol, I18n::Message::UnitSolidAngleSteradian),
+};
+
+const ToolboxMessageTree unitIlluminanceChildren[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::UnitIlluminanceLuxSymbol, I18n::Message::UnitIlluminanceLux),
+};
+
const ToolboxMessageTree unitFrequencyHertzChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitFrequencyHertzGigaSymbol, I18n::Message::UnitFrequencyHertzGiga),
ToolboxMessageTree::Leaf(I18n::Message::UnitFrequencyHertzMegaSymbol, I18n::Message::UnitFrequencyHertzMega),
@@ -190,6 +218,7 @@ const ToolboxMessageTree unitElectricChargeCoulombChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitChargeCoulombSymbol, I18n::Message::UnitChargeCoulomb),
};
+
const ToolboxMessageTree unitPotentialVoltChildren[] = {
ToolboxMessageTree::Leaf(I18n::Message::UnitPotentialVoltKiloSymbol, I18n::Message::UnitPotentialVoltKilo),
ToolboxMessageTree::Leaf(I18n::Message::UnitPotentialVoltSymbol, I18n::Message::UnitPotentialVolt),
@@ -237,6 +266,9 @@ const ToolboxMessageTree unitChildren[] = {
ToolboxMessageTree::Node(I18n::Message::UnitTemperatureMenu, unitTemperatureChildren),
ToolboxMessageTree::Node(I18n::Message::UnitAmountMenu, unitAmountMoleChildren),
ToolboxMessageTree::Node(I18n::Message::UnitLuminousIntensityMenu, unitLuminousIntensityChildren),
+ ToolboxMessageTree::Node(I18n::Message::UnitLuminousFluxMenu, unitLuminousFluxChildren),
+ ToolboxMessageTree::Node(I18n::Message::UnitIlluminanceMenu, unitIlluminanceChildren),
+ ToolboxMessageTree::Node(I18n::Message::UnitSolidAngleMenu, unitSolidAngleChildren),
ToolboxMessageTree::Node(I18n::Message::UnitFrequencyMenu, unitFrequencyHertzChildren),
ToolboxMessageTree::Node(I18n::Message::UnitForceMenu, unitForceNewtonChildren),
ToolboxMessageTree::Node(I18n::Message::UnitPressureMenu, unitPressureChildren),
@@ -520,45 +552,163 @@ const ToolboxMessageTree chemistryMolarMassesByAlpha[] = {
ToolboxMessageTree::Leaf(I18n::Message::AlphaElementZr, I18n::Message::ElementZrMass, false, I18n::Message::ElementZrMass)
};
-const ToolboxMessageTree chemistry[] = {
- ToolboxMessageTree::Node(I18n::Message::MolarMassesByNumber, chemistryMolarMassesByNumber),
- ToolboxMessageTree::Node(I18n::Message::MolarMassesByAlpha, chemistryMolarMassesByAlpha)
+const ToolboxMessageTree Pka[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::Pka01, I18n::Message::Pka01Value, false, I18n::Message::Pka01Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka02, I18n::Message::Pka02Value, false, I18n::Message::Pka02Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka03, I18n::Message::Pka03Value, false, I18n::Message::Pka03Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka04, I18n::Message::Pka04Value, false, I18n::Message::Pka04Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka05, I18n::Message::Pka05Value, false, I18n::Message::Pka05Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka06, I18n::Message::Pka06Value, false, I18n::Message::Pka06Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka07, I18n::Message::Pka07Value, false, I18n::Message::Pka07Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka08, I18n::Message::Pka08Value, false, I18n::Message::Pka08Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka09, I18n::Message::Pka09Value, false, I18n::Message::Pka09Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka10, I18n::Message::Pka10Value, false, I18n::Message::Pka10Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka11, I18n::Message::Pka11Value, false, I18n::Message::Pka11Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka12, I18n::Message::Pka12Value, false, I18n::Message::Pka12Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka13, I18n::Message::Pka13Value, false, I18n::Message::Pka13Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka14, I18n::Message::Pka14Value, false, I18n::Message::Pka14Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka15, I18n::Message::Pka15Value, false, I18n::Message::Pka15Value),
+ ToolboxMessageTree::Leaf(I18n::Message::Pka16, I18n::Message::Pka16Value, false, I18n::Message::Pka16Value),
+ ToolboxMessageTree::Leaf(I18n::Message::UltimateAnswer, I18n::Message::UltimateAnswerValue, false, I18n::Message::UltimateAnswerValue),
};
-const ToolboxMessageTree UnitOfMesurement[] = {
+const ToolboxMessageTree chemistry[] = {
+ ToolboxMessageTree::Node(I18n::Message::MolarMassesByNumber, chemistryMolarMassesByNumber),
+ ToolboxMessageTree::Node(I18n::Message::MolarMassesByAlpha, chemistryMolarMassesByAlpha),
+ ToolboxMessageTree::Node(I18n::Message::Pka, Pka),
+};
+
+const ToolboxMessageTree FundamentalConstants[] = {
ToolboxMessageTree::Leaf(I18n::Message::SpeedOfLightTag, I18n::Message::SpeedOfLight, false, I18n::Message::SpeedOfLight),
- ToolboxMessageTree::Leaf(I18n::Message::YearLightTag, I18n::Message::YearLight, false, I18n::Message::YearLight)
+ ToolboxMessageTree::Leaf(I18n::Message::Vacuum_permittivityTag, I18n::Message::Vacuum_permittivity, false, I18n::Message::Vacuum_permittivity),
+ ToolboxMessageTree::Leaf(I18n::Message::Vacuum_permeabilityTag, I18n::Message::Vacuum_permeability, false, I18n::Message::Vacuum_permeability),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckTag, I18n::Message::Planck, false, I18n::Message::Planck),
+ ToolboxMessageTree::Leaf(I18n::Message::VacuumImpedanceTag, I18n::Message::VacuumImpedance, false, I18n::Message::VacuumImpedance),
+};
+
+const ToolboxMessageTree SpeedsOfSound[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::SpeedOfSound0Tag, I18n::Message::SpeedOfSound0, false, I18n::Message::SpeedOfSound0),
+ ToolboxMessageTree::Leaf(I18n::Message::SpeedOfSoundWaterTag, I18n::Message::SpeedOfSoundWater, false, I18n::Message::SpeedOfSoundWater),
+ ToolboxMessageTree::Leaf(I18n::Message::SpeedOfSoundGlassTag, I18n::Message::SpeedOfSoundGlass, false, I18n::Message::SpeedOfSoundGlass),
+ ToolboxMessageTree::Leaf(I18n::Message::SpeedOfSoundSteelTag, I18n::Message::SpeedOfSoundSteel, false, I18n::Message::SpeedOfSoundSteel)
+};
+
+const ToolboxMessageTree EscapeVelocities[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::EscapeVelocityFromMoon, I18n::Message::EscapeVelocityOfMoon, false, I18n::Message::EscapeVelocityOfMoon),
+ ToolboxMessageTree::Leaf(I18n::Message::EscapeVelocityFromEarth, I18n::Message::EscapeVelocityOfEarth, false, I18n::Message::EscapeVelocityOfEarth),
+ ToolboxMessageTree::Leaf(I18n::Message::EscapeVelocityFromSun, I18n::Message::EscapeVelocityOfSun, false, I18n::Message::EscapeVelocityOfSun),
+};
+
+const ToolboxMessageTree Speed[] = {
+ ToolboxMessageTree::Node(I18n::Message::SpeedOfSound, SpeedsOfSound),
+ ToolboxMessageTree::Node(I18n::Message::EscapeVelocity, EscapeVelocities)
};
const ToolboxMessageTree Thermodynamics[] = {
ToolboxMessageTree::Leaf(I18n::Message::BoltzmannTag,I18n::Message::Boltzmann, false, I18n::Message::Boltzmann),
ToolboxMessageTree::Leaf(I18n::Message::AvogadroTag, I18n::Message::Avogadro, false, I18n::Message::Avogadro),
ToolboxMessageTree::Leaf(I18n::Message::GasTag,I18n::Message::Gas, false, I18n::Message::Gas),
- ToolboxMessageTree::Leaf(I18n::Message::ElectronMassTag, I18n::Message::ElectronMass, false, I18n::Message::ElectronMass),
- ToolboxMessageTree::Leaf(I18n::Message::ProtonMassTag, I18n::Message::ProtonMass, false, I18n::Message::ProtonMass),
- ToolboxMessageTree::Leaf(I18n::Message::NeutronMassTag, I18n::Message::NeutronMass, false, I18n::Message::NeutronMass),
+ ToolboxMessageTree::Leaf(I18n::Message::StefanBoltzmannTag, I18n::Message::StefanBoltzmann, false, I18n::Message::StefanBoltzmann),
+ ToolboxMessageTree::Leaf(I18n::Message::WaterTriplePointTag, I18n::Message::WaterTriplePoint, false, I18n::Message::WaterTriplePoint),
+ ToolboxMessageTree::Leaf(I18n::Message::WienTag, I18n::Message::Wien, false, I18n::Message::Wien),
+ ToolboxMessageTree::Leaf(I18n::Message::AtmosphericPressureTag, I18n::Message::AtmosphericPressure, false, I18n::Message::AtmosphericPressure),
};
const ToolboxMessageTree Electromagnetism[] = {
ToolboxMessageTree::Leaf(I18n::Message::CoulombTag, I18n::Message::Coulomb, false, I18n::Message::Coulomb),
- ToolboxMessageTree::Leaf(I18n::Message::Vacuum_permittivityTag, I18n::Message::Vacuum_permittivity, false, I18n::Message::Vacuum_permittivity),
- ToolboxMessageTree::Leaf(I18n::Message::Vacuum_permeabilityTag, I18n::Message::Vacuum_permeability, false, I18n::Message::Vacuum_permeability),
- ToolboxMessageTree::Leaf(I18n::Message::PlanckTag, I18n::Message::Planck, false, I18n::Message::Planck),
- ToolboxMessageTree::Leaf(I18n::Message::ElementalChargeTag, I18n::Message::ElementalCharge, false, I18n::Message::ElementalCharge)
+ ToolboxMessageTree::Leaf(I18n::Message::ElementalChargeTag, I18n::Message::ElementalCharge, false, I18n::Message::ElementalCharge),
+ ToolboxMessageTree::Leaf(I18n::Message::FaradayConstantTag, I18n::Message::FaradayConstant, false, I18n::Message::FaradayConstant),
+
+};
+
+const ToolboxMessageTree ParticleMass[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::ElectronMassTag, I18n::Message::ElectronMass, false, I18n::Message::ElectronMass),
+ ToolboxMessageTree::Leaf(I18n::Message::MuonMassTag, I18n::Message::MuonMass, false, I18n::Message::MuonMass),
+ ToolboxMessageTree::Leaf(I18n::Message::NeutronMassTag, I18n::Message::NeutronMass, false, I18n::Message::NeutronMass),
+ ToolboxMessageTree::Leaf(I18n::Message::ProtonMassTag, I18n::Message::ProtonMass, false, I18n::Message::ProtonMass),
+ ToolboxMessageTree::Leaf(I18n::Message::TauonMassTag, I18n::Message::TauonMass, false, I18n::Message::TauonMass),
+ ToolboxMessageTree::Leaf(I18n::Message::WBosonMassTag, I18n::Message::WBosonMass, false, I18n::Message::WBosonMass),
+ ToolboxMessageTree::Leaf(I18n::Message::ZBosonMassTag, I18n::Message::ZBosonMass, false, I18n::Message::ZBosonMass),
+ ToolboxMessageTree::Leaf(I18n::Message::AtomicMassUnitTag, I18n::Message::AtomicMassUnit, false, I18n::Message::AtomicMassUnit),
+};
+
+
+const ToolboxMessageTree Nuclear[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::FineStructureTag, I18n::Message::FineStructure, false, I18n::Message::FineStructure),
+ ToolboxMessageTree::Leaf(I18n::Message::RydbergConstantTag, I18n::Message::RydbergConstant, false, I18n::Message::RydbergConstant),
+ ToolboxMessageTree::Leaf(I18n::Message::HartreeConstantTag, I18n::Message::HartreeConstant, false, I18n::Message::HartreeConstant),
+ ToolboxMessageTree::Leaf(I18n::Message::MagneticFluxQuantumTag, I18n::Message::MagneticFluxQuantum, false, I18n::Message::MagneticFluxQuantum),
+ ToolboxMessageTree::Leaf(I18n::Message::ConductanceQuantumTag, I18n::Message::ConductanceQuantum, false, I18n::Message::ConductanceQuantum),
+ ToolboxMessageTree::Leaf(I18n::Message::CirculationQuantumTag, I18n::Message::CirculationQuantum, false, I18n::Message::CirculationQuantum),
+ ToolboxMessageTree::Leaf(I18n::Message::BohrRadiusTag, I18n::Message::BohrRadius, false, I18n::Message::BohrRadius),
+ ToolboxMessageTree::Leaf(I18n::Message::BohrMagnetonTag, I18n::Message::BohrMagneton, false, I18n::Message::BohrMagneton),
+ ToolboxMessageTree::Leaf(I18n::Message::NuclearMagnetonTag, I18n::Message::NuclearMagneton, false, I18n::Message::NuclearMagneton),
+ ToolboxMessageTree::Node(I18n::Message::ParticleMass, ParticleMass),
};
const ToolboxMessageTree Gravitation[] = {
ToolboxMessageTree::Leaf(I18n::Message::GAccelerationTag, I18n::Message::GAcceleration, false, I18n::Message::GAcceleration),
- ToolboxMessageTree::Leaf(I18n::Message::GConstantTag, I18n::Message::GConstant, false, I18n::Message::GConstant)
+ ToolboxMessageTree::Leaf(I18n::Message::GConstantTag, I18n::Message::GConstant, false, I18n::Message::GConstant),
+};
+
+
+const ToolboxMessageTree Radiuses[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::SunMassTag, I18n::Message::SunRadius, false, I18n::Message::SunRadius),
+ ToolboxMessageTree::Leaf(I18n::Message::EarthMassTag, I18n::Message::EarthRadius, false, I18n::Message::EarthRadius),
+ ToolboxMessageTree::Leaf(I18n::Message::MoonMassTag, I18n::Message::MoonRadius, false, I18n::Message::MoonRadius),
+
+};
+
+const ToolboxMessageTree Distances[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::EarthMoonDistanceTag, I18n::Message::EarthMoonDistance, false, I18n::Message::EarthMoonDistance),
+ ToolboxMessageTree::Leaf(I18n::Message::EarthSunDistanceTag, I18n::Message::EarthSunDistance, false, I18n::Message::EarthSunDistance),
+};
+
+const ToolboxMessageTree Length[] = {
+ ToolboxMessageTree::Node(I18n::Message::Radiuses, Radiuses),
+ ToolboxMessageTree::Node(I18n::Message::Distances, Distances),
+};
+
+const ToolboxMessageTree Mass[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::SunMassTag, I18n::Message::SunMass, false, I18n::Message::SunMass),
+ ToolboxMessageTree::Leaf(I18n::Message::EarthMassTag, I18n::Message::EarthMass, false, I18n::Message::EarthMass),
+ ToolboxMessageTree::Leaf(I18n::Message::MoonMassTag, I18n::Message::MoonMass, false, I18n::Message::MoonMass),
+};
+
+
+const ToolboxMessageTree PlanckUnits[] = {
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckReduceTag, I18n::Message::PlanckReduce, false, I18n::Message::PlanckReduce),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckMassTag, I18n::Message::PlanckMass, false, I18n::Message::PlanckMass),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckLengthTag, I18n::Message::PlanckLength, false, I18n::Message::PlanckLength),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckTimeTag, I18n::Message::PlanckTime, false, I18n::Message::PlanckTime),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckTemperatureTag, I18n::Message::PlanckTemperature, false, I18n::Message::PlanckTemperature),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckChargeTag, I18n::Message::PlanckCharge, false, I18n::Message::PlanckCharge),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckForceTag, I18n::Message::PlanckForce, false, I18n::Message::PlanckForce),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckEnergyTag, I18n::Message::PlanckEnergy, false, I18n::Message::PlanckEnergy),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckPowerTag, I18n::Message::PlanckPower, false, I18n::Message::PlanckPower),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckDensityTag, I18n::Message::PlanckDensity, false, I18n::Message::PlanckDensity),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckQuantityMovementTag, I18n::Message::PlanckQuantityMovement, false, I18n::Message::PlanckQuantityMovement),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckLinearMassTag, I18n::Message::PlanckLinearMass, false, I18n::Message::PlanckLinearMass),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckTensionTag, I18n::Message::PlanckTension, false, I18n::Message::PlanckTension),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckCurrentTag, I18n::Message::PlanckCurrent, false, I18n::Message::PlanckCurrent),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckPressureTag, I18n::Message::PlanckPressure, false, I18n::Message::PlanckPressure),
+ ToolboxMessageTree::Leaf(I18n::Message::PlanckImpedanceTag, I18n::Message::PlanckImpedance, false, I18n::Message::PlanckImpedance),
};
const ToolboxMessageTree Physics[] = {
- ToolboxMessageTree::Node(I18n::Message::UnitOfMesurement, UnitOfMesurement),
- ToolboxMessageTree::Node(I18n::Message::Thermodynamics, Thermodynamics),
+ ToolboxMessageTree::Node(I18n::Message::FundamentalConstants, FundamentalConstants),
ToolboxMessageTree::Node(I18n::Message::Electromagnetism, Electromagnetism),
- ToolboxMessageTree::Node(I18n::Message::Gravitation, Gravitation)
+ ToolboxMessageTree::Node(I18n::Message::NuclearConstants, Nuclear),
+ ToolboxMessageTree::Node(I18n::Message::Thermodynamics, Thermodynamics),
+ ToolboxMessageTree::Node(I18n::Message::Gravitation, Gravitation),
+ ToolboxMessageTree::Node(I18n::Message::Speed, Speed),
+ ToolboxMessageTree::Node(I18n::Message::Mass, Mass),
+ ToolboxMessageTree::Node(I18n::Message::Length, Length),
+ ToolboxMessageTree::Node(I18n::Message::PlanckUnitsTag, PlanckUnits),
};
+
+
const ToolboxMessageTree menu[] = {
ToolboxMessageTree::Leaf(I18n::Message::AbsCommandWithArg, I18n::Message::AbsoluteValue),
ToolboxMessageTree::Leaf(I18n::Message::RootCommandWithArg, I18n::Message::NthRoot),
@@ -588,7 +738,7 @@ MathToolbox::MathToolbox() :
}
bool MathToolbox::selectLeaf(int selectedRow) {
- ToolboxMessageTree * messageTree = (ToolboxMessageTree *)m_messageTreeModel->children(selectedRow);
+ ToolboxMessageTree * messageTree = (ToolboxMessageTree *)m_messageTreeModel->childAtIndex(selectedRow);
m_selectableTableView.deselectTable();
// Translate the message
@@ -598,7 +748,7 @@ bool MathToolbox::selectLeaf(int selectedRow) {
// Remove the arguments if we kept one message for both inserted and displayed message
int maxTextToInsertLength = strlen(text) + 1;
assert(maxTextToInsertLength <= k_maxMessageSize);
- Shared::ToolboxHelpers::TextToInsertForCommandText(text, textToInsert, maxTextToInsertLength, true);
+ Shared::ToolboxHelpers::TextToInsertForCommandText(text, -1, textToInsert, maxTextToInsertLength, true);
text = textToInsert;
}
sender()->handleEventWithText(text);
diff --git a/apps/variable_box_controller.cpp b/apps/math_variable_box_controller.cpp
similarity index 74%
rename from apps/variable_box_controller.cpp
rename to apps/math_variable_box_controller.cpp
index 2d6d09e19..1d6c968ce 100644
--- a/apps/variable_box_controller.cpp
+++ b/apps/math_variable_box_controller.cpp
@@ -1,4 +1,4 @@
-#include "variable_box_controller.h"
+#include "math_variable_box_controller.h"
#include "shared/global_context.h"
#include "shared/continuous_function.h"
#include
@@ -8,16 +8,14 @@
#include
#include
#include
+#include
using namespace Poincare;
using namespace Shared;
using namespace Ion;
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-static inline KDCoordinate maxInt(int x, int y) { return x > y ? x : y; }
-
-VariableBoxController::VariableBoxController() :
- NestedMenuController(nullptr, I18n::Message::Variables),
+MathVariableBoxController::MathVariableBoxController() :
+ AlternateEmptyNestedMenuController(I18n::Message::Variables),
m_currentPage(Page::RootMenu),
m_lockPageDelete(Page::RootMenu),
m_firstMemoizedLayoutIndex(0)
@@ -27,24 +25,20 @@ VariableBoxController::VariableBoxController() :
}
}
-void VariableBoxController::viewWillAppear() {
+void MathVariableBoxController::viewWillAppear() {
assert(m_currentPage == Page::RootMenu);
- NestedMenuController::viewWillAppear();
+ AlternateEmptyNestedMenuController::viewWillAppear();
}
-void VariableBoxController::viewDidDisappear() {
- if (isDisplayingEmptyController()) {
- pop();
- }
-
- NestedMenuController::viewDidDisappear();
+void MathVariableBoxController::viewDidDisappear() {
+ AlternateEmptyNestedMenuController::viewDidDisappear();
/* NestedMenuController::viewDidDisappear might need cell heights, which would
- * use the VariableBoxController cell heights memoization. We thus reset the
- * VariableBoxController layouts only after calling the parent's
+ * use the MathVariableBoxController cell heights memoization. We thus reset the
+ * MathVariableBoxController layouts only after calling the parent's
* viewDidDisappear. */
- // Tidy the layouts displayed in the VariableBoxController to clean TreePool
+ // Tidy the layouts displayed in the MathVariableBoxController to clean TreePool
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_leafCells[i].setLayout(Layout());
m_leafCells[i].setAccessoryLayout(Layout());
@@ -55,7 +49,7 @@ void VariableBoxController::viewDidDisappear() {
setPage(Page::RootMenu);
}
-bool VariableBoxController::handleEvent(Ion::Events::Event event) {
+bool MathVariableBoxController::handleEvent(Ion::Events::Event event) {
/* We do not want to handle backspace event if:
* - On the root menu page
* The deletion on the current page is locked
@@ -68,13 +62,13 @@ bool VariableBoxController::handleEvent(Ion::Events::Event event) {
int newSelectedRow = rowIndex >= numberOfRows() ? numberOfRows()-1 : rowIndex;
selectCellAtLocation(selectedColumn(), newSelectedRow);
m_selectableTableView.reloadData();
- displayEmptyController();
+ displayEmptyControllerIfNeeded();
return true;
}
- return NestedMenuController::handleEvent(event);
+ return AlternateEmptyNestedMenuController::handleEvent(event);
}
-int VariableBoxController::numberOfRows() const {
+int MathVariableBoxController::numberOfRows() const {
switch (m_currentPage) {
case Page::RootMenu:
return k_numberOfMenuRows;
@@ -87,7 +81,7 @@ int VariableBoxController::numberOfRows() const {
}
}
-int VariableBoxController::reusableCellCount(int type) {
+int MathVariableBoxController::reusableCellCount(int type) {
assert(type < 2);
if (type == 0) {
return k_maxNumberOfDisplayedRows;
@@ -95,7 +89,7 @@ int VariableBoxController::reusableCellCount(int type) {
return k_numberOfMenuRows;
}
-void VariableBoxController::willDisplayCellForIndex(HighlightCell * cell, int index) {
+void MathVariableBoxController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (m_currentPage == Page::RootMenu) {
I18n::Message label = nodeLabelAtIndex(index);
MessageTableCell * myCell = (MessageTableCell *)cell;
@@ -125,64 +119,64 @@ void VariableBoxController::willDisplayCellForIndex(HighlightCell * cell, int in
myCell->reloadCell();
}
-KDCoordinate VariableBoxController::rowHeight(int index) {
+KDCoordinate MathVariableBoxController::rowHeight(int index) {
if (m_currentPage != Page::RootMenu) {
Layout layoutR = expressionLayoutForRecord(recordAtIndex(index), index);
if (!layoutR.isUninitialized()) {
- return maxCoordinate(layoutR.layoutSize().height()+k_leafMargin, Metric::ToolboxRowHeight);
+ return std::max(layoutR.layoutSize().height()+k_leafMargin, Metric::ToolboxRowHeight);
}
}
- return NestedMenuController::rowHeight(index);
+ return AlternateEmptyNestedMenuController::rowHeight(index);
}
-int VariableBoxController::typeAtLocation(int i, int j) {
+int MathVariableBoxController::typeAtLocation(int i, int j) {
if (m_currentPage == Page::RootMenu) {
return 1;
}
return 0;
}
-ExpressionTableCellWithExpression * VariableBoxController::leafCellAtIndex(int index) {
+ExpressionTableCellWithExpression * MathVariableBoxController::leafCellAtIndex(int index) {
assert(index >= 0 && index < k_maxNumberOfDisplayedRows);
return &m_leafCells[index];
}
-MessageTableCellWithChevron * VariableBoxController::nodeCellAtIndex(int index) {
+MessageTableCellWithChevron * MathVariableBoxController::nodeCellAtIndex(int index) {
assert(index >= 0 && index < k_numberOfMenuRows);
return &m_nodeCells[index];
}
-VariableBoxController::Page VariableBoxController::pageAtIndex(int index) {
+MathVariableBoxController::Page MathVariableBoxController::pageAtIndex(int index) {
Page pages[2] = {Page::Expression, Page::Function};
return pages[index];
}
-void VariableBoxController::setPage(Page page) {
+void MathVariableBoxController::setPage(Page page) {
m_currentPage = page;
resetMemoization();
}
-bool VariableBoxController::selectSubMenu(int selectedRow) {
+bool MathVariableBoxController::selectSubMenu(int selectedRow) {
m_selectableTableView.deselectTable();
setPage(pageAtIndex(selectedRow));
- bool selectSubMenu = NestedMenuController::selectSubMenu(selectedRow);
- if (displayEmptyController()) {
+ bool selectSubMenu = AlternateEmptyNestedMenuController::selectSubMenu(selectedRow);
+ if (displayEmptyControllerIfNeeded()) {
return true;
}
return selectSubMenu;
}
-bool VariableBoxController::returnToPreviousMenu() {
+bool MathVariableBoxController::returnToPreviousMenu() {
if (isDisplayingEmptyController()) {
pop();
} else {
m_selectableTableView.deselectTable();
}
setPage(Page::RootMenu);
- return NestedMenuController::returnToPreviousMenu();
+ return AlternateEmptyNestedMenuController::returnToPreviousMenu();
}
-bool VariableBoxController::selectLeaf(int selectedRow) {
+bool MathVariableBoxController::selectLeaf(int selectedRow) {
if (isDisplayingEmptyController()) {
/* We do not want to handle OK/EXE events in that case. */
return false;
@@ -216,13 +210,13 @@ bool VariableBoxController::selectLeaf(int selectedRow) {
return true;
}
-I18n::Message VariableBoxController::nodeLabelAtIndex(int index) {
+I18n::Message MathVariableBoxController::nodeLabelAtIndex(int index) {
assert(m_currentPage == Page::RootMenu);
I18n::Message labels[2] = {I18n::Message::Expressions, I18n::Message::Functions};
return labels[index];
}
-Layout VariableBoxController::expressionLayoutForRecord(Storage::Record record, int index) {
+Layout MathVariableBoxController::expressionLayoutForRecord(Storage::Record record, int index) {
assert(m_currentPage != Page::RootMenu);
assert(index >= 0);
if (index >= m_firstMemoizedLayoutIndex+k_maxNumberOfDisplayedRows || index < m_firstMemoizedLayoutIndex) {
@@ -252,36 +246,30 @@ Layout VariableBoxController::expressionLayoutForRecord(Storage::Record record,
return m_layouts[index-m_firstMemoizedLayoutIndex];
}
-const char * VariableBoxController::extension() const {
+const char * MathVariableBoxController::extension() const {
assert(m_currentPage != Page::RootMenu);
return m_currentPage == Page::Function ? Ion::Storage::funcExtension : Ion::Storage::expExtension;
}
-Storage::Record VariableBoxController::recordAtIndex(int rowIndex) {
+Storage::Record MathVariableBoxController::recordAtIndex(int rowIndex) {
assert(m_currentPage != Page::RootMenu);
assert(!Storage::sharedStorage()->recordWithExtensionAtIndex(extension(), rowIndex).isNull());
return Storage::sharedStorage()->recordWithExtensionAtIndex(extension(), rowIndex);
}
-bool VariableBoxController::displayEmptyController() {
- assert(!isDisplayingEmptyController());
- // If the content is empty, we push above an empty controller.
- if (numberOfRows() == 0) {
- m_emptyViewController.setType((VariableBoxEmptyController::Type)m_currentPage);
- push(&m_emptyViewController);
- return true;
- }
- return false;
+ViewController * MathVariableBoxController::emptyViewController() {
+ m_emptyViewController.setType((MathVariableBoxEmptyController::Type)m_currentPage);
+ return &m_emptyViewController;
}
-void VariableBoxController::resetMemoization() {
+void MathVariableBoxController::resetMemoization() {
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_layouts[i] = Layout();
}
m_firstMemoizedLayoutIndex = 0;
}
-void VariableBoxController::destroyRecordAtRowIndex(int rowIndex) {
+void MathVariableBoxController::destroyRecordAtRowIndex(int rowIndex) {
// Destroy the record
recordAtIndex(rowIndex).destroy();
// Shift the memoization if needed
@@ -289,7 +277,7 @@ void VariableBoxController::destroyRecordAtRowIndex(int rowIndex) {
// The deleted row is after the memoization
return;
}
- for (int i = maxInt(0, rowIndex - m_firstMemoizedLayoutIndex); i < k_maxNumberOfDisplayedRows - 1; i++) {
+ for (int i = std::max(0, rowIndex - m_firstMemoizedLayoutIndex); i < k_maxNumberOfDisplayedRows - 1; i++) {
m_layouts[i] = m_layouts[i+1];
}
m_layouts[k_maxNumberOfDisplayedRows - 1] = Layout();
diff --git a/apps/variable_box_controller.h b/apps/math_variable_box_controller.h
similarity index 82%
rename from apps/variable_box_controller.h
rename to apps/math_variable_box_controller.h
index 95824e29d..8e6052a11 100644
--- a/apps/variable_box_controller.h
+++ b/apps/math_variable_box_controller.h
@@ -1,15 +1,13 @@
-#ifndef APPS_VARIABLE_BOX_CONTROLLER_H
-#define APPS_VARIABLE_BOX_CONTROLLER_H
+#ifndef APPS_MATH_VARIABLE_BOX_CONTROLLER_H
+#define APPS_MATH_VARIABLE_BOX_CONTROLLER_H
-#define MATRIX_VARIABLES 1
-
-#include
-#include "variable_box_empty_controller.h"
+#include "alternate_empty_nested_menu_controller.h"
+#include "math_variable_box_empty_controller.h"
#include
-class VariableBoxController : public NestedMenuController {
+class MathVariableBoxController : public AlternateEmptyNestedMenuController {
public:
- VariableBoxController();
+ MathVariableBoxController();
// View Controller
void viewWillAppear() override;
@@ -32,6 +30,7 @@ public:
Function = 2
};
void lockDeleteEvent(Page page) { m_lockPageDelete = page; }
+
private:
constexpr static int k_maxNumberOfDisplayedRows = (Ion::Display::Height - Metric::TitleBarHeight - Metric::PopUpTopMargin - Metric::StackTitleHeight) / Metric::ToolboxRowHeight + 2; // (240 - 18 - 50 - 20) / 40 = 3.8; the 0.8 cell can be above and below so we add +2 to get 5
constexpr static int k_numberOfMenuRows = 2;
@@ -47,15 +46,14 @@ private:
Poincare::Layout expressionLayoutForRecord(Ion::Storage::Record record, int index);
const char * extension() const;
Ion::Storage::Record recordAtIndex(int rowIndex);
- bool displayEmptyController();
- bool isDisplayingEmptyController() { return StackViewController::depth() == 2; }
+ ViewController * emptyViewController() override;
void resetMemoization();
void destroyRecordAtRowIndex(int rowIndex);
Page m_currentPage;
Page m_lockPageDelete;
ExpressionTableCellWithExpression m_leafCells[k_maxNumberOfDisplayedRows];
MessageTableCellWithChevron m_nodeCells[k_numberOfMenuRows];
- VariableBoxEmptyController m_emptyViewController;
+ MathVariableBoxEmptyController m_emptyViewController;
// Layout memoization
// TODO: make a helper doing the RingMemoizationOfConsecutiveObjets to factorize this code and ExpressionModelStore code
int m_firstMemoizedLayoutIndex;
diff --git a/apps/math_variable_box_empty_controller.cpp b/apps/math_variable_box_empty_controller.cpp
new file mode 100644
index 000000000..f5ad187f7
--- /dev/null
+++ b/apps/math_variable_box_empty_controller.cpp
@@ -0,0 +1,53 @@
+#include "math_variable_box_empty_controller.h"
+#include
+#include
+#include
+
+MathVariableBoxEmptyController::MathVariableBoxEmptyView::MathVariableBoxEmptyView() :
+ ModalViewEmptyView(),
+ m_layoutExample(0.5f, 0.5f, KDColorBlack, Palette::WallScreen)
+{
+ initMessageViews();
+}
+
+void MathVariableBoxEmptyController::MathVariableBoxEmptyView::setLayout(Poincare::Layout layout) {
+ m_layoutExample.setLayout(layout);
+}
+
+void MathVariableBoxEmptyController::viewDidDisappear() {
+ m_view.setLayout(Poincare::Layout());
+}
+
+void MathVariableBoxEmptyController::setType(Type type) {
+ I18n::Message messages[MathVariableBoxEmptyView::k_numberOfMessages] = {
+ I18n::Message::Default,
+ I18n::Message::Default,
+ I18n::Message::Default,
+ I18n::Message::EnableCharacters
+ };
+ Poincare::Layout layout;
+ switch (type) {
+ case Type::Expressions:
+ {
+ messages[0] = I18n::Message::EmptyExpressionBox0;
+ messages[1] = I18n::Message::EmptyExpressionBox1;
+ messages[2] = I18n::Message::EmptyExpressionBox2;
+ const char * storeExpression = "3→A";
+ layout = Poincare::LayoutHelper::String(storeExpression, strlen(storeExpression), MathVariableBoxEmptyView::k_font);
+ break;
+ }
+ case Type::Functions:
+ {
+ messages[0] = I18n::Message::EmptyFunctionBox0;
+ messages[1] = I18n::Message::EmptyFunctionBox1;
+ messages[2] = I18n::Message::EmptyFunctionBox2;
+ const char * storeFunction = "3+x→f(x)";
+ layout = Poincare::LayoutHelper::String(storeFunction, strlen(storeFunction), MathVariableBoxEmptyView::k_font);
+ break;
+ }
+ default:
+ assert(false);
+ }
+ m_view.setMessages(messages);
+ m_view.setLayout(layout);
+}
diff --git a/apps/math_variable_box_empty_controller.h b/apps/math_variable_box_empty_controller.h
new file mode 100644
index 000000000..120ff42b6
--- /dev/null
+++ b/apps/math_variable_box_empty_controller.h
@@ -0,0 +1,41 @@
+#ifndef APPS_MATH_VARIABLE_BOX_EMPTY_CONTROLLER_H
+#define APPS_MATH_VARIABLE_BOX_EMPTY_CONTROLLER_H
+
+#include
+#include
+
+class MathVariableBoxEmptyController : public ModalViewEmptyController {
+public:
+ MathVariableBoxEmptyController() :
+ ModalViewEmptyController(),
+ m_view()
+ {}
+ enum class Type {
+ None = 0,
+ Expressions = 1,
+ Functions = 2
+ };
+ void setType(Type type);
+ // View Controller
+ View * view() override { return &m_view; }
+ void viewDidDisappear() override;
+private:
+ class MathVariableBoxEmptyView : public ModalViewEmptyController::ModalViewEmptyView {
+ public:
+ constexpr static int k_numberOfMessages = 4;
+ MathVariableBoxEmptyView();
+ void setLayout(Poincare::Layout layout);
+ private:
+ int numberOfMessageTextViews() const override { return k_numberOfMessages; }
+ MessageTextView * messageTextViewAtIndex(int index) override {
+ assert(index >= 0 && index < k_numberOfMessages);
+ return &m_messages[index];
+ }
+ ExpressionView * expressionView() override { return &m_layoutExample; }
+ MessageTextView m_messages[k_numberOfMessages];
+ ExpressionView m_layoutExample;
+ };
+ MathVariableBoxEmptyView m_view;
+};
+
+#endif
diff --git a/apps/on_boarding/Makefile b/apps/on_boarding/Makefile
index 0d546b356..42d22f508 100644
--- a/apps/on_boarding/Makefile
+++ b/apps/on_boarding/Makefile
@@ -7,15 +7,8 @@ app_on_boarding_src = $(addprefix apps/on_boarding/,\
power_on_self_test.cpp \
)
-app_src += $(app_on_boarding_src)
+apps_src += $(app_on_boarding_src)
-i18n_files += $(addprefix apps/on_boarding/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
-)
+i18n_files += $(call i18n_without_universal_for,on_boarding/base)
$(eval $(call depends_on_image,apps/on_boarding/logo_view.cpp,apps/on_boarding/logo_icon.png))
diff --git a/apps/on_boarding/base.it.i18n b/apps/on_boarding/base.it.i18n
new file mode 100644
index 000000000..33ff2bd3c
--- /dev/null
+++ b/apps/on_boarding/base.it.i18n
@@ -0,0 +1,13 @@
+UpdateAvailable = "AGGIORNAMENTO DISPONIBILE"
+UpdateMessage1 = "Esistono miglioramenti significativi"
+UpdateMessage2 = "per la vostra calcolatrice."
+UpdateMessage3 = "Connettetevi dal vostro computer"
+UpdateMessage4 = "www.numworks.com/update"
+BetaVersion = "VERSIONE BETA"
+BetaVersionMessage1 = "Il vostro dispositivo dispone"
+BetaVersionMessage2 = "di una versione beta del software."
+BetaVersionMessage3 = "Possono comparire alcuni bugs."
+BetaVersionMessage4 = "Per comunicarci un riscontro"
+BetaVersionMessage5 = "potete scriverci a"
+BetaVersionMessage6 = "contact@numworks.com"
+Skip = "Saltare"
diff --git a/apps/on_boarding/base.nl.i18n b/apps/on_boarding/base.nl.i18n
new file mode 100644
index 000000000..7d9ce05b7
--- /dev/null
+++ b/apps/on_boarding/base.nl.i18n
@@ -0,0 +1,13 @@
+UpdateAvailable = "UPDATE BESCHIKBAAR"
+UpdateMessage1 = "Er zijn belangrijke updates"
+UpdateMessage2 = "voor je rekenmachine."
+UpdateMessage3 = "Ga vanaf je computer naar onze pagina"
+UpdateMessage4 = "www.numworks.com/update"
+BetaVersion = "BÈTAVERSIE"
+BetaVersionMessage1 = ""
+BetaVersionMessage2 = "Je apparaat draait op een bèta-software."
+BetaVersionMessage3 = "Je komt mogelijk bugs of glitches tegen."
+BetaVersionMessage4 = ""
+BetaVersionMessage5 = "Feedback kun je sturen naar"
+BetaVersionMessage6 = "contact@numworks.nl"
+Skip = "Verdergaan"
diff --git a/apps/on_boarding/base.pt.i18n b/apps/on_boarding/base.pt.i18n
index db822f4b6..bd16512a0 100644
--- a/apps/on_boarding/base.pt.i18n
+++ b/apps/on_boarding/base.pt.i18n
@@ -1,13 +1,13 @@
UpdateAvailable = "ATUALIZAÇÃO DISPONÍVEL"
UpdateMessage1 = "Existem melhorias significativas"
UpdateMessage2 = "para a sua calculadora."
-UpdateMessage3 = "Navegue na nossa página do seu computador"
+UpdateMessage3 = "Navegue na nossa página no seu computador"
UpdateMessage4 = "www.numworks.com/update"
BetaVersion = "BETA VERSION"
-BetaVersionMessage1 = ""
-BetaVersionMessage2 = "Your device runs a beta software."
-BetaVersionMessage3 = "You might run into bugs or glitches."
+BetaVersionMessage1 = "O seu dispositivo está a executar"
+BetaVersionMessage2 = "um software beta."
+BetaVersionMessage3 = "Pode encontrar bugs ou falhas."
BetaVersionMessage4 = ""
-BetaVersionMessage5 = "Please send any feedback to"
+BetaVersionMessage5 = "Por favor envie-nos o seu feedback para"
BetaVersionMessage6 = "contact@numworks.com"
-Skip = "Pular"
+Skip = "Saltar"
diff --git a/apps/on_boarding/language_controller.cpp b/apps/on_boarding/language_controller.cpp
index 3319b507c..d4a3ae679 100644
--- a/apps/on_boarding/language_controller.cpp
+++ b/apps/on_boarding/language_controller.cpp
@@ -1,12 +1,20 @@
#include "language_controller.h"
#include "../global_preferences.h"
#include "../apps_container.h"
+#include
+#include
namespace OnBoarding {
LanguageController::LanguageController(Responder * parentResponder) :
- Shared::LanguageController(parentResponder, (Ion::Display::Height - I18n::NumberOfLanguages*Metric::ParameterCellHeight)/2)
+ Shared::LanguageController(
+ parentResponder,
+ std::max(static_cast(Metric::CommonLeftMargin),
+ (Ion::Display::Height - I18n::NumberOfLanguages*Metric::ParameterCellHeight)/2))
{
+ static_cast(m_selectableTableView.decorator()->indicatorAtIndex(1))->setMargin(
+ std::max(static_cast(Metric::CommonLeftMargin),
+ (Ion::Display::Height - I18n::NumberOfLanguages*Metric::ParameterCellHeight)/2));
}
bool LanguageController::handleEvent(Ion::Events::Event event) {
diff --git a/apps/probability/Makefile b/apps/probability/Makefile
index cc8e26580..d22f88d48 100644
--- a/apps/probability/Makefile
+++ b/apps/probability/Makefile
@@ -38,17 +38,9 @@ app_probability_src = $(addprefix apps/probability/,\
)
app_probability_src += $(app_probability_test_src)
-app_src += $(app_probability_src)
+apps_src += $(app_probability_src)
-i18n_files += $(addprefix apps/probability/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
- base.universal.i18n\
-)
+i18n_files += $(call i18n_with_universal_for,probability/base)
tests_src += $(addprefix apps/probability/test/,\
hypergeometric_function.cpp\
diff --git a/apps/probability/app.cpp b/apps/probability/app.cpp
index 5387997ee..56fe17b42 100644
--- a/apps/probability/app.cpp
+++ b/apps/probability/app.cpp
@@ -15,8 +15,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::ProbaAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
@@ -28,14 +28,11 @@ App::Snapshot::Snapshot() :
m_calculation{},
m_activePage(Page::Distribution)
{
- new(m_distribution) BinomialDistribution();
- new(m_calculation) LeftIntegralCalculation();
- calculation()->setDistribution(distribution());
+ initializeDistributionAndCalculation();
}
App::Snapshot::~Snapshot() {
- distribution()->~Distribution();
- calculation()->~Calculation();
+ deleteDistributionAndCalculation();
}
App * App::Snapshot::unpack(Container * container) {
@@ -48,10 +45,8 @@ App::Descriptor * App::Snapshot::descriptor() {
}
void App::Snapshot::reset() {
- distribution()->~Distribution();
- new(m_distribution) BinomialDistribution();
- calculation()->~Calculation();
- new(m_calculation) LeftIntegralCalculation();
+ deleteDistributionAndCalculation();
+ initializeDistributionAndCalculation();
m_activePage = Page::Distribution;
}
@@ -63,6 +58,16 @@ Calculation * App::Snapshot::calculation() {
return (Calculation *)m_calculation;
}
+void App::Snapshot::deleteDistributionAndCalculation() {
+ distribution()->~Distribution();
+ calculation()->~Calculation();
+}
+
+void App::Snapshot::initializeDistributionAndCalculation() {
+ new(m_distribution) BinomialDistribution();
+ new(m_calculation) LeftIntegralCalculation(distribution());
+}
+
void App::Snapshot::setActivePage(Page activePage) {
m_activePage = activePage;
}
diff --git a/apps/probability/app.h b/apps/probability/app.h
index fcae7f321..be6fb5c78 100644
--- a/apps/probability/app.h
+++ b/apps/probability/app.h
@@ -27,7 +27,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
@@ -46,9 +46,14 @@ public:
Calculation * calculation();
Page activePage();
void setActivePage(Page activePage);
+
private:
constexpr static int k_distributionSizes[] = {sizeof(BinomialDistribution),sizeof(ExponentialDistribution), sizeof(NormalDistribution), sizeof(PoissonDistribution), sizeof(UniformDistribution), 0};
constexpr static size_t k_distributionSize = max(k_distributionSizes);
+
+ void deleteDistributionAndCalculation();
+ void initializeDistributionAndCalculation();
+
char m_distribution[k_distributionSize];
constexpr static int k_calculationSizes[] = {sizeof(LeftIntegralCalculation),sizeof(FiniteIntegralCalculation), sizeof(RightIntegralCalculation), 0};
constexpr static size_t k_calculationSize = max(k_calculationSizes);
diff --git a/apps/probability/base.de.i18n b/apps/probability/base.de.i18n
index 45d52dd65..5c7c68fba 100644
--- a/apps/probability/base.de.i18n
+++ b/apps/probability/base.de.i18n
@@ -3,12 +3,12 @@ ProbaAppCapital = "WAHRSCHEINLICHKEIT"
ChooseDistribution = "Wählen Sie eine Verteilung"
Binomial = "Binomial"
Geometric = "Geometrische"
-Uniforme = "Uniform"
+Uniforme = "Gleichverteilung"
Normal = "Normal"
ChiSquared = "Chi-Quadrat"
UniformDistribution = "Uniformverteilung"
ExponentialDistribution = "Exponentialverteilung"
-GeometricDistribution = "Geometrischeverteilung"
+GeometricDistribution = "Geometrische Verteilung"
PoissonDistribution = "Poisson-Verteilung"
ChiSquaredDistribution = "Chi-Quadrat-Verteilung"
StudentDistribution = "Student-Verteilung"
diff --git a/apps/probability/base.fr.i18n b/apps/probability/base.fr.i18n
index 056fa00f6..95da17e67 100644
--- a/apps/probability/base.fr.i18n
+++ b/apps/probability/base.fr.i18n
@@ -10,13 +10,13 @@ UniformDistribution = "Loi uniforme"
ExponentialDistribution = "Loi exponentielle"
GeometricDistribution = "Loi géométrique"
PoissonDistribution = "Loi de Poisson"
-ChiSquaredDistribution = "Loi du chi2"
+ChiSquaredDistribution = "Loi du Khi-2"
StudentDistribution = "Loi de Student"
FisherDistribution = "Loi de Fisher"
ChooseParameters = "Choisir les paramètres"
RepetitionNumber = "n : Nombre de répétitions"
SuccessProbability = "p : Probabilité de succès"
-IntervalDefinition = "[a,b] : Intervalle"
+IntervalDefinition = "[a;b] : Intervalle"
LambdaExponentialDefinition = "λ : Paramètre"
MeanDefinition = "μ : Espérance ou moyenne"
DeviationDefinition = "σ : Écart type"
diff --git a/apps/probability/base.hu.i18n b/apps/probability/base.hu.i18n
index f147ff239..dc9948bb2 100644
--- a/apps/probability/base.hu.i18n
+++ b/apps/probability/base.hu.i18n
@@ -23,5 +23,5 @@ DeviationDefinition = "σ: szórás"
LambdaPoissonDefinition = "λ: Paraméter"
DegreesOfFreedomDefinition = "k: Szabadságfokok"
D1FisherDefinition = "d1: A számláló szabadsági foka"
-D2FisherDefinition = "d2: A nevező szabadsági foka"
+D2FisherDefinition = "d2: A nevezó szabadsági foka"
ComputeProbability = "Számítsa ki a valószínüségeket"
diff --git a/apps/probability/base.it.i18n b/apps/probability/base.it.i18n
new file mode 100644
index 000000000..443229b53
--- /dev/null
+++ b/apps/probability/base.it.i18n
@@ -0,0 +1,27 @@
+ProbaApp = "Probabilità"
+ProbaAppCapital = "PROBABILITA"
+ChooseDistribution = "Scegliere il tipo di distribuzione"
+Binomial = "Binomiale"
+Geometric = "Geometrica"
+Uniforme = "Uniforme"
+Normal = "Normale"
+ChiSquared = "Chi2"
+UniformDistribution = "Distribuzione uniforme"
+ExponentialDistribution = "Distribuzione esponenziale"
+GeometricDistribution = "Distribuzione geometrica"
+PoissonDistribution = "Distribuzione di Poisson"
+ChiSquaredDistribution = "Distribuzione chi2"
+StudentDistribution = "Distribuzione Student"
+FisherDistribution = "Distribuzione di Fisher"
+ChooseParameters = "Scegliere i parametri"
+RepetitionNumber = "n : Numero di prove"
+SuccessProbability = "p : Probabilità di successo"
+IntervalDefinition = "[a,b] : Intervallo"
+LambdaExponentialDefinition = "λ : Parametro"
+MeanDefinition = "μ : Media"
+DeviationDefinition = "σ : Deviazione standard"
+LambdaPoissonDefinition = "λ : Parametro"
+DegreesOfFreedomDefinition = "k : Gradi di libertà"
+D1FisherDefinition = "d1 : Gradi di libertà del numeratore"
+D2FisherDefinition = "d2 : Gradi di libertà del denominatore"
+ComputeProbability = "Calcolare le probabilità"
diff --git a/apps/probability/base.nl.i18n b/apps/probability/base.nl.i18n
new file mode 100644
index 000000000..07ace7455
--- /dev/null
+++ b/apps/probability/base.nl.i18n
@@ -0,0 +1,27 @@
+ProbaApp = "Kansrekenen"
+ProbaAppCapital = "KANSREKENEN"
+ChooseDistribution = "Kies de kansverdeling"
+Binomial = "Binomiaal"
+Geometric = "Geometrisch"
+Uniforme = "Uniform"
+Normal = "Normaal"
+ChiSquared = "Chi-kwadraat"
+UniformDistribution = "Uniforme verdeling"
+ExponentialDistribution = "Exponentiële verdeling"
+GeometricDistribution = "Geometrische verdeling"
+PoissonDistribution = "Poissonverdeling"
+ChiSquaredDistribution = "Chi-kwadraatverdeling"
+StudentDistribution = "Studentverdeling"
+FisherDistribution = "F-verdeling"
+ChooseParameters = "Bepaal de parameters"
+RepetitionNumber = "n: Aantal proeven"
+SuccessProbability = "p: Kans op succes"
+IntervalDefinition = "[a,b]: Interval"
+LambdaExponentialDefinition = "λ: Snelheidsparameter"
+MeanDefinition = "μ: Gemiddelde"
+DeviationDefinition = "σ: Standaardafwijking"
+LambdaPoissonDefinition = "λ: Parameter"
+DegreesOfFreedomDefinition = "k: Vrijheidsgraden"
+D1FisherDefinition = "d1: Vrijheidsgraden in de teller"
+D2FisherDefinition = "d2: Vrijheidsgraden in de noemer"
+ComputeProbability = "Bereken kansen"
diff --git a/apps/probability/base.pt.i18n b/apps/probability/base.pt.i18n
index 6e9071d74..f37757390 100644
--- a/apps/probability/base.pt.i18n
+++ b/apps/probability/base.pt.i18n
@@ -1,5 +1,5 @@
-ProbaApp = "Probabilidade"
-ProbaAppCapital = "PROBABILIDADE"
+ProbaApp = "Probabilidades"
+ProbaAppCapital = "PROBABILIDADES"
ChooseDistribution = "Selecionar a distribuição"
Binomial = "Binomial"
Geometric = "Geométrica"
diff --git a/apps/probability/calculation/calculation.cpp b/apps/probability/calculation/calculation.cpp
index f45f31460..a059a938e 100644
--- a/apps/probability/calculation/calculation.cpp
+++ b/apps/probability/calculation/calculation.cpp
@@ -4,11 +4,6 @@
namespace Probability {
-void Calculation::setDistribution(Distribution * distribution) {
- m_distribution = distribution;
- compute(0);
-}
-
double Calculation::lowerBound() {
return -INFINITY;
}
diff --git a/apps/probability/calculation/calculation.h b/apps/probability/calculation/calculation.h
index 215daece6..f7847dba4 100644
--- a/apps/probability/calculation/calculation.h
+++ b/apps/probability/calculation/calculation.h
@@ -13,10 +13,11 @@ public:
RightIntegral,
Discrete,
};
- Calculation() : m_distribution(nullptr) {}
+ Calculation(Distribution * distribution) : m_distribution(distribution) {
+ assert(distribution != nullptr);
+ }
virtual ~Calculation() = default;
virtual Type type() = 0;
- void setDistribution(Distribution * distribution);
virtual int numberOfParameters() = 0;
virtual I18n::Message legendForParameterAtIndex(int index) = 0;
virtual void setParameterAtIndex(double f, int index) = 0;
diff --git a/apps/probability/calculation/discrete_calculation.cpp b/apps/probability/calculation/discrete_calculation.cpp
index 075038512..798281e70 100644
--- a/apps/probability/calculation/discrete_calculation.cpp
+++ b/apps/probability/calculation/discrete_calculation.cpp
@@ -5,10 +5,9 @@
namespace Probability {
-DiscreteCalculation::DiscreteCalculation() :
- Calculation(),
- m_abscissa(0.0),
- m_result(0.0)
+DiscreteCalculation::DiscreteCalculation(Distribution * distribution) :
+ Calculation(distribution),
+ m_abscissa(distribution->defaultComputedValue())
{
compute(0);
}
diff --git a/apps/probability/calculation/discrete_calculation.h b/apps/probability/calculation/discrete_calculation.h
index 6bf1b332a..9ca655e94 100644
--- a/apps/probability/calculation/discrete_calculation.h
+++ b/apps/probability/calculation/discrete_calculation.h
@@ -7,7 +7,7 @@ namespace Probability {
class DiscreteCalculation final : public Calculation {
public:
- DiscreteCalculation();
+ DiscreteCalculation(Distribution * distribution);
Type type() override { return Type::Discrete; }
int numberOfParameters() override { return 2; }
I18n::Message legendForParameterAtIndex(int index) override;
diff --git a/apps/probability/calculation/finite_integral_calculation.cpp b/apps/probability/calculation/finite_integral_calculation.cpp
index a1ac5d9c8..2341bc15b 100644
--- a/apps/probability/calculation/finite_integral_calculation.cpp
+++ b/apps/probability/calculation/finite_integral_calculation.cpp
@@ -6,11 +6,10 @@
namespace Probability {
-FiniteIntegralCalculation::FiniteIntegralCalculation() :
- Calculation(),
- m_lowerBound(0.0),
- m_upperBound(1.0),
- m_result(0.0)
+FiniteIntegralCalculation::FiniteIntegralCalculation(Distribution * distribution) :
+ Calculation(distribution),
+ m_lowerBound(distribution->defaultComputedValue()),
+ m_upperBound(m_lowerBound + 1.0)
{
compute(0);
}
diff --git a/apps/probability/calculation/finite_integral_calculation.h b/apps/probability/calculation/finite_integral_calculation.h
index ccbd2afd0..fbbb867ac 100644
--- a/apps/probability/calculation/finite_integral_calculation.h
+++ b/apps/probability/calculation/finite_integral_calculation.h
@@ -7,7 +7,7 @@ namespace Probability {
class FiniteIntegralCalculation : public Calculation {
public:
- FiniteIntegralCalculation();
+ FiniteIntegralCalculation(Distribution * distribution);
Type type() override { return Type::FiniteIntegral; }
int numberOfParameters() override { return 3; }
I18n::Message legendForParameterAtIndex(int index) override;
diff --git a/apps/probability/calculation/left_integral_calculation.cpp b/apps/probability/calculation/left_integral_calculation.cpp
index f589ce4e8..d24430b74 100644
--- a/apps/probability/calculation/left_integral_calculation.cpp
+++ b/apps/probability/calculation/left_integral_calculation.cpp
@@ -5,10 +5,9 @@
namespace Probability {
-LeftIntegralCalculation::LeftIntegralCalculation() :
- Calculation(),
- m_upperBound(0.0),
- m_result(0.0)
+LeftIntegralCalculation::LeftIntegralCalculation(Distribution * distribution) :
+ Calculation(distribution),
+ m_upperBound(distribution->defaultComputedValue())
{
compute(0);
}
diff --git a/apps/probability/calculation/left_integral_calculation.h b/apps/probability/calculation/left_integral_calculation.h
index f998cdb12..032a52581 100644
--- a/apps/probability/calculation/left_integral_calculation.h
+++ b/apps/probability/calculation/left_integral_calculation.h
@@ -7,7 +7,7 @@ namespace Probability {
class LeftIntegralCalculation final : public Calculation {
public:
- LeftIntegralCalculation();
+ LeftIntegralCalculation(Distribution * distribution);
Type type() override { return Type::LeftIntegral; }
int numberOfParameters() override { return 2; }
I18n::Message legendForParameterAtIndex(int index) override;
diff --git a/apps/probability/calculation/right_integral_calculation.cpp b/apps/probability/calculation/right_integral_calculation.cpp
index 08903df47..068fe3552 100644
--- a/apps/probability/calculation/right_integral_calculation.cpp
+++ b/apps/probability/calculation/right_integral_calculation.cpp
@@ -5,10 +5,9 @@
namespace Probability {
-RightIntegralCalculation::RightIntegralCalculation() :
- Calculation(),
- m_lowerBound(0.0),
- m_result(0.0)
+RightIntegralCalculation::RightIntegralCalculation(Distribution * distribution) :
+ Calculation(distribution),
+ m_lowerBound(distribution->defaultComputedValue())
{
compute(0);
}
diff --git a/apps/probability/calculation/right_integral_calculation.h b/apps/probability/calculation/right_integral_calculation.h
index 71f1e5530..9cb8f001d 100644
--- a/apps/probability/calculation/right_integral_calculation.h
+++ b/apps/probability/calculation/right_integral_calculation.h
@@ -7,7 +7,7 @@ namespace Probability {
class RightIntegralCalculation final : public Calculation {
public:
- RightIntegralCalculation();
+ RightIntegralCalculation(Distribution * distribution);
Type type() override { return Type::RightIntegral; }
int numberOfParameters() override { return 2; }
I18n::Message legendForParameterAtIndex(int index) override;
diff --git a/apps/probability/calculation_cell.cpp b/apps/probability/calculation_cell.cpp
index 1a3963bd9..3c2b3dc36 100644
--- a/apps/probability/calculation_cell.cpp
+++ b/apps/probability/calculation_cell.cpp
@@ -2,12 +2,10 @@
#include "responder_image_cell.h"
#include
#include
+#include
namespace Probability {
-static inline KDCoordinate minCoordinate(KDCoordinate x, KDCoordinate y) { return x < y ? x : y; }
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-
CalculationCell::CalculationCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * textFieldDelegate) :
m_text(KDFont::LargeFont, I18n::Message::Default, 0.5f, 0.5f),
m_calculation(parentResponder, inputEventHandlerDelegate, textFieldDelegate),
@@ -78,7 +76,7 @@ KDCoordinate CalculationCell::calculationCellWidth() const {
KDCoordinate glyphWidth = KDFont::LargeFont->glyphSize().width();
KDCoordinate minTextFieldWidth = 4 * glyphWidth + TextCursorView::k_width;
KDCoordinate maxTextFieldWidth = 14 * glyphWidth + TextCursorView::k_width;
- return minCoordinate(maxTextFieldWidth, maxCoordinate(minTextFieldWidth, calculationCellWidth));
+ return std::min(maxTextFieldWidth, std::max(minTextFieldWidth, calculationCellWidth));
}
}
diff --git a/apps/probability/calculation_controller.cpp b/apps/probability/calculation_controller.cpp
index 704ed387f..2d4ddd1b6 100644
--- a/apps/probability/calculation_controller.cpp
+++ b/apps/probability/calculation_controller.cpp
@@ -15,6 +15,7 @@
#include "images/focused_calcul4_icon.h"
#include
#include
+#include
#include
using namespace Poincare;
@@ -22,7 +23,7 @@ using namespace Shared;
namespace Probability {
-static inline int minInt(int x, int y) { return x < y ? x : y; }
+constexpr int CalculationController::k_titleBufferSize;
CalculationController::ContentView::ContentView(SelectableTableView * selectableTableView, Distribution * distribution, Calculation * calculation) :
m_titleView(KDFont::SmallFont, I18n::Message::ComputeProbability, 0.5f, 0.5f, Palette::SecondaryText, Palette::BackgroundApps),
@@ -250,21 +251,20 @@ void CalculationController::setCalculationAccordingToIndex(int index, bool force
m_calculation->~Calculation();
switch (index) {
case 0:
- new(m_calculation) LeftIntegralCalculation();
- break;
+ new(m_calculation) LeftIntegralCalculation(m_distribution);
+ return;
case 1:
- new(m_calculation) FiniteIntegralCalculation();
- break;
+ new(m_calculation) FiniteIntegralCalculation(m_distribution);
+ return;
case 2:
- new(m_calculation) RightIntegralCalculation();
- break;
+ new(m_calculation) RightIntegralCalculation(m_distribution);
+ return;
case 3:
- new(m_calculation) DiscreteCalculation();
- break;
+ new(m_calculation) DiscreteCalculation(m_distribution);
+ return;
default:
return;
}
- m_calculation->setDistribution(m_distribution);
}
void CalculationController::updateTitle() {
@@ -293,7 +293,7 @@ void CalculationController::updateTitle() {
}
currentChar += UTF8Decoder::CodePointToChars(' ', m_titleBuffer + currentChar, k_titleBufferSize - currentChar);
}
- m_titleBuffer[minInt(currentChar, k_titleBufferSize) - 1] = 0;
+ m_titleBuffer[std::min(currentChar, k_titleBufferSize) - 1] = 0;
}
}
diff --git a/apps/probability/distribution/binomial_distribution.cpp b/apps/probability/distribution/binomial_distribution.cpp
index ed1e8f213..5ee9326e0 100644
--- a/apps/probability/distribution/binomial_distribution.cpp
+++ b/apps/probability/distribution/binomial_distribution.cpp
@@ -66,7 +66,7 @@ double BinomialDistribution::cumulativeDistributiveInverseForProbability(double
}
double BinomialDistribution::rightIntegralInverseForProbability(double * probability) {
- if (m_parameter1 == 0.0 && (m_parameter2 == 0.0 || m_parameter2 == 1.0)) {
+ if (m_parameter1 == 0.0f && (m_parameter2 == 0.0f || m_parameter2 == 1.0f)) {
return NAN;
}
if (*probability <= 0.0) {
diff --git a/apps/probability/distribution/chi_squared_distribution.cpp b/apps/probability/distribution/chi_squared_distribution.cpp
index 091cf30f2..9338c8c4b 100644
--- a/apps/probability/distribution/chi_squared_distribution.cpp
+++ b/apps/probability/distribution/chi_squared_distribution.cpp
@@ -1,11 +1,10 @@
#include "chi_squared_distribution.h"
#include "regularized_gamma.h"
#include
+#include
namespace Probability {
-static inline double maxDouble(double x, double y) { return x > y ? x : y; }
-
float ChiSquaredDistribution::xMin() const {
return -k_displayLeftMarginRatio * xMax();
}
@@ -44,7 +43,7 @@ double ChiSquaredDistribution::cumulativeDistributiveFunctionAtAbscissa(double x
return 0.0;
}
double result = 0.0;
- if (regularizedGamma(m_parameter1/2.0, x/2.0, k_regularizedGammaPrecision, k_maxRegularizedGammaIterations, &result)) {
+ if (regularizedGamma(m_parameter1/2.0f, x/2.0, k_regularizedGammaPrecision, k_maxRegularizedGammaIterations, &result)) {
return result;
}
return NAN;
@@ -79,7 +78,7 @@ double ChiSquaredDistribution::cumulativeDistributiveInverseForProbability(doubl
2.0 * *probability * std::exp(std::lgamma(ceilKOver2)) / (exp(-kOver2Minus1) * std::pow(kOver2Minus1, kOver2Minus1)) :
30.0; // Ad hoc value
xmax = std::isnan(xmax) ? 1000000000.0 : xmax;
- return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, FLT_EPSILON, maxDouble(xMax(), xmax));
+ return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, FLT_EPSILON, std::max(xMax(), xmax));
}
}
diff --git a/apps/probability/distribution/distribution.h b/apps/probability/distribution/distribution.h
index 95575ed43..e7e9d5332 100644
--- a/apps/probability/distribution/distribution.h
+++ b/apps/probability/distribution/distribution.h
@@ -39,6 +39,7 @@ public:
virtual double rightIntegralInverseForProbability(double * probability);
virtual double evaluateAtDiscreteAbscissa(int k) const;
constexpr static int k_maxNumberOfOperations = 1000000;
+ virtual double defaultComputedValue() const { return 0.0f; }
protected:
static_assert(Poincare::Preferences::LargeNumberOfSignificantDigits == 7, "k_maxProbability is ill-defined compared to LargeNumberOfSignificantDigits");
constexpr static double k_maxProbability = 0.9999995;
diff --git a/apps/probability/distribution/fisher_distribution.cpp b/apps/probability/distribution/fisher_distribution.cpp
index 97e145599..66201ac0c 100644
--- a/apps/probability/distribution/fisher_distribution.cpp
+++ b/apps/probability/distribution/fisher_distribution.cpp
@@ -3,11 +3,10 @@
#include
#include
#include
+#include
namespace Probability {
-static inline double maxDouble(double x, double y) { return x > y ? x : y; }
-
float FisherDistribution::xMin() const {
return -k_displayLeftMarginRatio * xMax();
}
@@ -74,7 +73,7 @@ double FisherDistribution::cumulativeDistributiveInverseForProbability(double *
if (*probability < DBL_EPSILON) {
return 0.0;
}
- return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, DBL_EPSILON, maxDouble(xMax(), 100.0)); // Ad-hoc value;
+ return cumulativeDistributiveInverseForProbabilityUsingIncreasingFunctionRoot(probability, DBL_EPSILON, std::max(xMax(), 100.0)); // Ad-hoc value;
}
float FisherDistribution::mode() const {
diff --git a/apps/probability/distribution/geometric_distribution.cpp b/apps/probability/distribution/geometric_distribution.cpp
index 1ac3559cd..ed64b23ab 100644
--- a/apps/probability/distribution/geometric_distribution.cpp
+++ b/apps/probability/distribution/geometric_distribution.cpp
@@ -10,13 +10,12 @@ float GeometricDistribution::xMin() const {
}
float GeometricDistribution::xMax() const {
- assert(m_parameter1 != 0);
+ assert(m_parameter1 != 0.0f);
return 5/m_parameter1 * (1.0f + k_displayRightMarginRatio);
}
float GeometricDistribution::yMax() const {
- int maxAbscissa = 0;
- float result = evaluateAtAbscissa(maxAbscissa);
+ float result = evaluateAtAbscissa(1.0); // Tha probability is max for x == 1
return result * (1.0f + k_displayTopMarginRatio);
}
@@ -29,16 +28,18 @@ bool GeometricDistribution::authorizedValueAtIndex(float x, int index) const {
}
template
-T GeometricDistribution::templatedApproximateAtAbscissa(T x) const {
- if (x < 0) {
- return NAN;
+T GeometricDistribution::templatedApproximateAtAbscissa(T k) const {
+ constexpr T castedOne = static_cast(1.0);
+ if (k < castedOne) {
+ return static_cast(0.0);
}
- T p = (T)m_parameter1;
- if (p == (T)1.0) {
- return (T)(x == 0 ? 1.0 : 0.0);
+ T p = static_cast(m_parameter1);
+ if (p == castedOne) {
+ return k == castedOne ? castedOne : static_cast(0.0);
}
- T lResult = x * std::log(((T)1.0) - p);
- return p*std::exp(lResult);
+ // The result is p * (1-p)^{k-1}
+ T lResult = (k - castedOne) * std::log(castedOne - p);
+ return p * std::exp(lResult);
}
}
diff --git a/apps/probability/distribution/geometric_distribution.h b/apps/probability/distribution/geometric_distribution.h
index 035d2945e..e3323abfa 100644
--- a/apps/probability/distribution/geometric_distribution.h
+++ b/apps/probability/distribution/geometric_distribution.h
@@ -7,9 +7,7 @@ namespace Probability {
/* We chose the definition:
* 0 < p <= 1 for success probability
- * k failures where k ∈ {0, 1, 2, ... }
- * The distribution follows the probability distribution of the number of failures before
- * the first success. */
+ * k number of trials needed to get one success, where k ∈ {1, 2, 3, ...}. */
class GeometricDistribution final : public OneParameterDistribution {
public:
@@ -32,6 +30,7 @@ public:
return templatedApproximateAtAbscissa(x);
}
bool authorizedValueAtIndex(float x, int index) const override;
+ double defaultComputedValue() const override { return 1.0f; }
private:
double evaluateAtDiscreteAbscissa(int k) const override {
return templatedApproximateAtAbscissa((double)k);
diff --git a/apps/probability/distribution/student_distribution.cpp b/apps/probability/distribution/student_distribution.cpp
index 4199f4467..a11640113 100644
--- a/apps/probability/distribution/student_distribution.cpp
+++ b/apps/probability/distribution/student_distribution.cpp
@@ -23,7 +23,7 @@ float StudentDistribution::evaluateAtAbscissa(float x) const {
}
bool StudentDistribution::authorizedValueAtIndex(float x, int index) const {
- return x >= FLT_EPSILON && x <= 200.0; // We cannot draw the curve for x > 200 (coefficient() is too small)
+ return x >= FLT_EPSILON && x <= 200.0f; // We cannot draw the curve for x > 200 (coefficient() is too small)
}
double StudentDistribution::cumulativeDistributiveFunctionAtAbscissa(double x) const {
@@ -55,7 +55,7 @@ double StudentDistribution::cumulativeDistributiveInverseForProbability(double *
float StudentDistribution::lnCoefficient() const {
const float k = m_parameter1;
- return std::lgamma((k+1.0f)/2.0f) - std::lgamma(k/2.0f) - (M_PI+k)/2.0f;
+ return std::lgamma((k+1.0f)/2.0f) - std::lgamma(k/2.0f) - ((float)M_PI+k)/2.0f;
}
}
diff --git a/apps/probability/distribution_controller.h b/apps/probability/distribution_controller.h
index ee5086a58..78b0c7c4a 100644
--- a/apps/probability/distribution_controller.h
+++ b/apps/probability/distribution_controller.h
@@ -26,7 +26,7 @@ private:
class ContentView : public View {
public:
ContentView(SelectableTableView * selectableTableView) :
- m_titleView(KDFont::SmallFont, I18n::Message::ChooseDistribution, 0.5f, 0.5f, Palette::GreyDark, Palette::BackgroundApps),
+ m_titleView(KDFont::SmallFont, I18n::Message::ChooseDistribution, 0.5f, 0.5f, Palette::SecondaryText, Palette::BackgroundApps),
m_selectableTableView(selectableTableView)
{}
constexpr static KDCoordinate k_titleMargin = 8;
diff --git a/apps/probability/test/distributions.cpp b/apps/probability/test/distributions.cpp
index 20bd9d9f9..f28c1c9ba 100644
--- a/apps/probability/test/distributions.cpp
+++ b/apps/probability/test/distributions.cpp
@@ -110,19 +110,19 @@ QUIZ_CASE(geometric_distribution) {
// Geometric distribution with probability of success 0.5
Probability::GeometricDistribution distribution;
distribution.setParameterAtIndex(0.5, 0);
- assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.0, 0.875);
- assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 3.0, 0.9375);
+ assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.0, 0.5);
+ assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.0, 0.75);
// Geometric distribution with probability of success 0.2
distribution.setParameterAtIndex(0.2, 0);
- assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 7.0, 0.8322278399999998299563230830244719982147216796875);
- assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 3.0, 0.5904);
+ assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 6.0, 0.737856);
+ assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.0, 0.36);
// Geometric distribution with probability of success 0.4
distribution.setParameterAtIndex(0.4, 0);
- assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.24);
+ assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.4);
assert_finite_integral_between_abscissas_is(&distribution, 2.0, 1.0, 0.0);
- assert_finite_integral_between_abscissas_is(&distribution, 1.0, 2.0, 0.384);
+ assert_finite_integral_between_abscissas_is(&distribution, 2.0, 3.0, 0.384);
}
QUIZ_CASE(fisher_distribution) {
diff --git a/apps/regression/Makefile b/apps/regression/Makefile
index 2361d3a1d..2211e3192 100644
--- a/apps/regression/Makefile
+++ b/apps/regression/Makefile
@@ -15,6 +15,7 @@ app_regression_test_src += $(addprefix apps/regression/model/,\
logistic_model.cpp \
model.cpp \
power_model.cpp \
+ proportional_model.cpp \
quadratic_model.cpp \
quartic_model.cpp \
trigonometric_model.cpp \
@@ -37,17 +38,9 @@ app_regression_src = $(addprefix apps/regression/,\
)
app_regression_src += $(app_regression_test_src)
-app_src += $(app_regression_src)
+apps_src += $(app_regression_src)
-i18n_files += $(addprefix apps/regression/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
- base.universal.i18n\
-)
+i18n_files += $(call i18n_with_universal_for,regression/base)
tests_src += $(addprefix apps/regression/test/,\
model.cpp\
diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp
index 53ecf1481..0bef9c82b 100644
--- a/apps/regression/app.cpp
+++ b/apps/regression/app.cpp
@@ -15,8 +15,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::RegressionAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
diff --git a/apps/regression/app.h b/apps/regression/app.h
index b53a16868..c00e8b4ad 100644
--- a/apps/regression/app.h
+++ b/apps/regression/app.h
@@ -17,7 +17,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot, public TabViewDataSource {
diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n
index 2e06f3bba..b49e91e7c 100644
--- a/apps/regression/base.de.i18n
+++ b/apps/regression/base.de.i18n
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht"
NumberOfDots = "Punktanzahl"
Covariance = "Kovarianz"
Linear = "Lineare"
+Proportional = "Proportional"
Quadratic = "Quadratische"
Cubic = "Kubische"
Quartic = "Biquadratische"
diff --git a/apps/regression/base.en.i18n b/apps/regression/base.en.i18n
index 891c30eb3..7b257c653 100644
--- a/apps/regression/base.en.i18n
+++ b/apps/regression/base.en.i18n
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Value not reached in this window"
NumberOfDots = "Number of points"
Covariance = "Covariance"
Linear = "Linear"
+Proportional = "Proportional"
Quadratic = "Quadratic"
Cubic = "Cubic"
Quartic = "Quartic"
diff --git a/apps/regression/base.es.i18n b/apps/regression/base.es.i18n
index 15b0fff73..147304a40 100644
--- a/apps/regression/base.es.i18n
+++ b/apps/regression/base.es.i18n
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor no alcanzado en esta ventana"
NumberOfDots = "Número de puntos"
Covariance = "Covarianza"
Linear = "Lineal"
+Proportional = "Proporcional"
Quadratic = "Cuadrática"
Cubic = "Cúbica"
Quartic = "Cuártica"
diff --git a/apps/regression/base.fr.i18n b/apps/regression/base.fr.i18n
index 0d24b1bd8..c8f80d875 100644
--- a/apps/regression/base.fr.i18n
+++ b/apps/regression/base.fr.i18n
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valeur non atteinte dans cette fenêtre"
NumberOfDots = "Nombre de points"
Covariance = "Covariance"
Linear = "Linéaire"
+Proportional = "Proportionnelle"
Quadratic = "Quadratique"
Cubic = "Cubique"
Quartic = "Quartique"
diff --git a/apps/regression/base.hu.i18n b/apps/regression/base.hu.i18n
index a3f937e5d..495bd0e17 100644
--- a/apps/regression/base.hu.i18n
+++ b/apps/regression/base.hu.i18n
@@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Az ablakban az érték még nem volt elérve"
NumberOfDots = "Pontok száma"
Covariance = "Kovariancia"
Linear = "Lineáris"
+Proportional = "Proportional"
Quadratic = "Másodfokú"
Cubic = "Kocka"
Quartic = "Kvartikus"
diff --git a/apps/regression/base.it.i18n b/apps/regression/base.it.i18n
new file mode 100644
index 000000000..06976e196
--- /dev/null
+++ b/apps/regression/base.it.i18n
@@ -0,0 +1,21 @@
+RegressionApp = "Regressione"
+RegressionAppCapital = "REGRESSIONE"
+Regression = "Regressione"
+Reg = "reg"
+MeanDot = "media"
+RegressionCurve = "Curva di regressione"
+XPrediction = "Previsione data X"
+YPrediction = "Previsione data Y"
+ValueNotReachedByRegression = "Valore non raggiunto in questa finestra"
+NumberOfDots = "Numero di punti"
+Covariance = "Covarianza"
+Linear = "Lineare"
+Proportional = "Proporzionale"
+Quadratic = "Quadratica"
+Cubic = "Cubica"
+Quartic = "Quartica"
+Logarithmic = "Logaritmica"
+Power = "Potenza"
+Trigonometrical = "Trigonometrica"
+Logistic = "Logistica"
+DataNotSuitableForRegression = "I dati non sono adeguati"
diff --git a/apps/regression/base.nl.i18n b/apps/regression/base.nl.i18n
new file mode 100644
index 000000000..f5411a0a0
--- /dev/null
+++ b/apps/regression/base.nl.i18n
@@ -0,0 +1,21 @@
+RegressionApp = "Regressie"
+RegressionAppCapital = "REGRESSIE"
+Regression = "Regressie"
+Reg = "reg"
+MeanDot = "gemiddelde"
+RegressionCurve = "Regressielijn"
+XPrediction = "Voorspelling gegeven X"
+YPrediction = "Voorspelling gegeven Y"
+ValueNotReachedByRegression = "Waarde niet gevonden in dit venster"
+NumberOfDots = "Aantal punten"
+Covariance = "Covariantie"
+Linear = "Lineair"
+Proportional = "Proportioneel"
+Quadratic = "Kwadratisch"
+Cubic = "Kubiek"
+Quartic = "Quartic"
+Logarithmic = "Logaritmisch"
+Power = "Macht"
+Trigonometrical = "Trigonometrisch"
+Logistic = "Logistiek"
+DataNotSuitableForRegression = " Data niet geschikt voor dit regressiemodel"
diff --git a/apps/regression/base.pt.i18n b/apps/regression/base.pt.i18n
index 7ec8ab7b5..e5db50f79 100644
--- a/apps/regression/base.pt.i18n
+++ b/apps/regression/base.pt.i18n
@@ -2,14 +2,15 @@ RegressionApp = "Regressão"
RegressionAppCapital = "REGRESSÃO"
Regression = "Regressão"
Reg = "reg"
-MeanDot = "media"
+MeanDot = "média"
RegressionCurve = "Curva de regressão"
XPrediction = "Predição dado X"
YPrediction = "Predição dado Y"
ValueNotReachedByRegression = "Valor não alcançado nesta janela"
NumberOfDots = "Número de pontos"
-Covariance = "Covariancia"
+Covariance = "Covariância"
Linear = "Linear"
+Proportional = "Proporcional"
Quadratic = "Quadrática"
Cubic = "Cúbica"
Quartic = "Quarto grau"
diff --git a/apps/regression/base.universal.i18n b/apps/regression/base.universal.i18n
index bb896dc36..a26602af1 100644
--- a/apps/regression/base.universal.i18n
+++ b/apps/regression/base.universal.i18n
@@ -1,3 +1,4 @@
+ProportionalRegressionFormula = " y=a·x "
QuadraticRegressionFormula = " y=a·x^2+b·x+c "
CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d "
QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e "
diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp
index 878ea2c12..34b8e70ea 100644
--- a/apps/regression/calculation_controller.cpp
+++ b/apps/regression/calculation_controller.cpp
@@ -4,7 +4,7 @@
#include
#include
#include
-
+#include
#include
using namespace Poincare;
@@ -12,8 +12,6 @@ using namespace Shared;
namespace Regression {
-static inline int maxInt(int x, int y) { return x > y ? x : y; }
-
CalculationController::CalculationController(Responder * parentResponder, ButtonRowController * header, Store * store) :
TabTableController(parentResponder),
ButtonRowDelegate(header, nullptr),
@@ -375,7 +373,7 @@ int CalculationController::maxNumberOfCoefficients() const {
int numberOfDefinedSeries = m_store->numberOfNonEmptySeries();
for (int i = 0; i < numberOfDefinedSeries; i++) {
int currentNumberOfCoefs = m_store->modelForSeries(m_store->indexOfKthNonEmptySeries(i))->numberOfCoefficients();
- maxNumberCoefficients = maxInt(maxNumberCoefficients, currentNumberOfCoefs);
+ maxNumberCoefficients = std::max(maxNumberCoefficients, currentNumberOfCoefs);
}
return maxNumberCoefficients;
}
diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp
index 93b5c5867..b207c3ed9 100644
--- a/apps/regression/graph_controller.cpp
+++ b/apps/regression/graph_controller.cpp
@@ -4,14 +4,11 @@
#include "../apps_container.h"
#include
#include
+#include
using namespace Poincare;
using namespace Shared;
-static inline float minFloat(float x, float y) { return x < y ? x : y; }
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-static inline int maxInt(int x, int y) { return x > y ? x : y; }
-
namespace Regression {
GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) :
@@ -165,7 +162,7 @@ void GraphController::reloadBannerView() {
}
if (!coefficientsAreDefined) {
// Force the "Data not suitable" message to be on the next line
- int numberOfCharToCompleteLine = maxInt(Ion::Display::Width / BannerView::Font()->glyphSize().width() - strlen(I18n::translate(formula)), 0);
+ int numberOfCharToCompleteLine = std::max(Ion::Display::Width / BannerView::Font()->glyphSize().width() - strlen(I18n::translate(formula)), 0);
numberOfChar = 0;
// Padding
Shared::TextHelpers::PadWithSpaces(buffer, bufferSize, &numberOfChar, numberOfCharToCompleteLine - 1);
@@ -191,13 +188,14 @@ void GraphController::reloadBannerView() {
}
if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear) {
+ int index = model->numberOfCoefficients();
// Set "r=..."
numberOfChar = 0;
legend = " r=";
double r = m_store->correlationCoefficient(*m_selectedSeriesIndex);
numberOfChar += strlcpy(buffer, legend, bufferSize);
numberOfChar += PoincareHelpers::ConvertFloatToText(r, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits);
- m_bannerView.subTextAtIndex(2)->setText(buffer);
+ m_bannerView.subTextAtIndex(0+index)->setText(buffer);
// Set "r2=..."
numberOfChar = 0;
@@ -205,11 +203,11 @@ void GraphController::reloadBannerView() {
double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex);
numberOfChar += strlcpy(buffer, legend, bufferSize);
numberOfChar += PoincareHelpers::ConvertFloatToText(r2, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits);
- m_bannerView.subTextAtIndex(3)->setText(buffer);
+ m_bannerView.subTextAtIndex(1+index)->setText(buffer);
// Clean the last subview
buffer[0] = 0;
- m_bannerView.subTextAtIndex(4)->setText(buffer);
+ m_bannerView.subTextAtIndex(2+index)->setText(buffer);
} else {
// Empty all non used subviews
@@ -351,7 +349,7 @@ uint32_t GraphController::modelVersion() {
return m_store->storeChecksum();
}
-uint32_t GraphController::modelVersionAtIndex(size_t i) {
+uint32_t GraphController::modelVersionAtIndex(int i) {
assert(i < numberOfMemoizedVersions());
return *(m_store->seriesChecksum() + i);
}
@@ -390,8 +388,8 @@ InteractiveCurveViewRangeDelegate::Range GraphController::computeYRange(Interact
for (int series = 0; series < Store::k_numberOfSeries; series++) {
for (int k = 0; k < m_store->numberOfPairsOfSeries(series); k++) {
if (m_store->xMin() <= m_store->get(series, 0, k) && m_store->get(series, 0, k) <= m_store->xMax()) {
- minY = minFloat(minY, m_store->get(series, 1, k));
- maxY = maxFloat(maxY, m_store->get(series, 1, k));
+ minY = std::min(minY, m_store->get(series, 1, k));
+ maxY = std::max(maxY, m_store->get(series, 1, k));
}
}
}
diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h
index 30e722552..d81680c18 100644
--- a/apps/regression/graph_controller.h
+++ b/apps/regression/graph_controller.h
@@ -43,7 +43,7 @@ private:
// InteractiveCurveViewController
void initCursorParameters() override;
uint32_t modelVersion() override;
- uint32_t modelVersionAtIndex(size_t i) override;
+ uint32_t modelVersionAtIndex(int i) override;
uint32_t rangeVersion() override;
size_t numberOfMemoizedVersions() const override { return Store::k_numberOfSeries; }
int selectedCurveIndex() const override { return *m_selectedSeriesIndex; }
diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp
index 1c5899e81..393e2120e 100644
--- a/apps/regression/model/cubic_model.cpp
+++ b/apps/regression/model/cubic_model.cpp
@@ -19,31 +19,29 @@ namespace Regression {
Layout CubicModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int size = 15;
- Layout layoutChildren[size] = {
+ m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('3', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('3', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('2', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('2', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('d', k_layoutFont),
- };
- m_layout = HorizontalLayout::Builder(layoutChildren, size);
+ });
}
return m_layout;
}
@@ -57,24 +55,21 @@ double CubicModel::evaluate(double * modelCoefficients, double x) const {
}
double CubicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
- if (derivateCoefficientIndex == 0) {
- // Derivate: x^3
- return x*x*x;
- }
- if (derivateCoefficientIndex == 1) {
- // Derivate: x^2
- return x*x;
- }
- if (derivateCoefficientIndex == 2) {
- // Derivate: x
- return x;
- }
- if (derivateCoefficientIndex == 3) {
- // Derivate: 1
- return 1;
- }
- assert(false);
- return 0.0;
+ switch (derivateCoefficientIndex) {
+ case 0:
+ // Derivate with respect to a: x^3
+ return x*x*x;
+ case 1:
+ // Derivate with respect to b: x^2
+ return x*x;
+ case 2:
+ // Derivate with respect to c: x
+ return x;
+ default:
+ // Derivate with respect to d: 1
+ assert(derivateCoefficientIndex == 3);
+ return 1.0;
+ };
}
Expression CubicModel::expression(double * modelCoefficients) {
@@ -82,25 +77,28 @@ Expression CubicModel::expression(double * modelCoefficients) {
double b = modelCoefficients[1];
double c = modelCoefficients[2];
double d = modelCoefficients[3];
- Expression addChildren[] = {
- Multiplication::Builder(
+ // a*x^3+b*x^2+c*x+d
+ return Addition::Builder({
+ Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(3.0))),
- Multiplication::Builder(
+ Decimal::Builder(3.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(b),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(2.0))),
- Multiplication::Builder(
+ Decimal::Builder(2.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(c),
- Symbol::Builder('x')),
+ Symbol::Builder('x')
+ }),
Number::DecimalNumber(d)
- };
- // a*x^3+b*x^2+c*x+d
- Expression result = Addition::Builder(addChildren, 4);
- return result;
+ });
}
}
diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp
index 608467a11..aecdc3cae 100644
--- a/apps/regression/model/exponential_model.cpp
+++ b/apps/regression/model/exponential_model.cpp
@@ -13,21 +13,19 @@ namespace Regression {
Layout ExponentialModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int size = 4;
- Layout layoutChildren[size] = {
+ m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
VerticalOffsetLayout::Builder(
- HorizontalLayout::Builder(
- CodePointLayout::Builder('b', k_layoutFont),
- CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
- CodePointLayout::Builder('X', k_layoutFont)
- ),
- VerticalOffsetLayoutNode::Position::Superscript
- )
- };
- m_layout = HorizontalLayout::Builder(layoutChildren, size);
+ HorizontalLayout::Builder({
+ CodePointLayout::Builder('b', k_layoutFont),
+ CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
+ CodePointLayout::Builder('X', k_layoutFont)
+ }),
+ VerticalOffsetLayoutNode::Position::Superscript
+ )
+ });
}
return m_layout;
}
@@ -88,18 +86,15 @@ void ExponentialModel::fit(Store * store, int series, double * modelCoefficients
}
double ExponentialModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
- double a = modelCoefficients[0];
- double b = modelCoefficients[1];
+ const double b = modelCoefficients[1];
if (derivateCoefficientIndex == 0) {
- // Derivate: exp(b*x)
+ // Derivate with respect to a: exp(b*x)
return exp(b*x);
}
- if (derivateCoefficientIndex == 1) {
- // Derivate: a*x*exp(b*x)
- return a*x*exp(b*x);
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 1);
+ // Derivate with respect to b: a*x*exp(b*x)
+ double a = modelCoefficients[0];
+ return a*x*exp(b*x);
}
}
diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp
index 3de85628f..afe159ea2 100644
--- a/apps/regression/model/linear_model.cpp
+++ b/apps/regression/model/linear_model.cpp
@@ -38,15 +38,12 @@ void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poi
double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
if (derivateCoefficientIndex == 0) {
- // Derivate: x
+ // Derivate with respect to a: x
return x;
}
- if (derivateCoefficientIndex == 1) {
- // Derivate: 1;
- return 1;
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 1);
+ // Derivate with respect to b: 1
+ return 1.0;
}
}
diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp
index f5c23a105..7beeb119c 100644
--- a/apps/regression/model/logarithmic_model.cpp
+++ b/apps/regression/model/logarithmic_model.cpp
@@ -33,16 +33,13 @@ double LogarithmicModel::levelSet(double * modelCoefficients, double xMin, doubl
double LogarithmicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
if (derivateCoefficientIndex == 0) {
- // Derivate: ln(x)
- assert(x >0);
+ // Derivate with respect to a: ln(x)
+ assert(x > 0);
return log(x);
}
- if (derivateCoefficientIndex == 1) {
- // Derivate: 1
- return 1;
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 1);
+ // Derivate with respect to b: 1
+ return 1.0;
}
bool LogarithmicModel::dataSuitableForFit(Store * store, int series) const {
diff --git a/apps/regression/model/logarithmic_model.h b/apps/regression/model/logarithmic_model.h
index a541b571d..0fd7048fa 100644
--- a/apps/regression/model/logarithmic_model.h
+++ b/apps/regression/model/logarithmic_model.h
@@ -16,7 +16,7 @@ public:
int numberOfCoefficients() const override { return 2; }
int bannerLinesCount() const override { return 2; }
protected:
- virtual bool dataSuitableForFit(Store * store, int series) const override;
+ bool dataSuitableForFit(Store * store, int series) const override;
};
}
diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp
index f74db8f3d..ec9c50a48 100644
--- a/apps/regression/model/logistic_model.cpp
+++ b/apps/regression/model/logistic_model.cpp
@@ -13,29 +13,25 @@ namespace Regression {
Layout LogisticModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int exponentSize = 4;
- Layout exponentLayoutChildren[exponentSize] = {
- CodePointLayout::Builder('-', k_layoutFont),
- CodePointLayout::Builder('b', k_layoutFont),
- CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
- CodePointLayout::Builder('X', k_layoutFont)
- };
- constexpr int denominatorSize = 6;
- Layout layoutChildren[denominatorSize] = {
- CodePointLayout::Builder('1', k_layoutFont),
- CodePointLayout::Builder('+', k_layoutFont),
- CodePointLayout::Builder('a', k_layoutFont),
- CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
- CodePointLayout::Builder('e', k_layoutFont),
- VerticalOffsetLayout::Builder(
- HorizontalLayout::Builder(exponentLayoutChildren, exponentSize),
+ m_layout = FractionLayout::Builder(
+ CodePointLayout::Builder('c', k_layoutFont),
+ HorizontalLayout::Builder({
+ CodePointLayout::Builder('1', k_layoutFont),
+ CodePointLayout::Builder('+', k_layoutFont),
+ CodePointLayout::Builder('a', k_layoutFont),
+ CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
+ CodePointLayout::Builder('e', k_layoutFont),
+ VerticalOffsetLayout::Builder(
+ HorizontalLayout::Builder({
+ CodePointLayout::Builder('-', k_layoutFont),
+ CodePointLayout::Builder('b', k_layoutFont),
+ CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
+ CodePointLayout::Builder('X', k_layoutFont)
+ }),
VerticalOffsetLayoutNode::Position::Superscript
)
- };
- m_layout = FractionLayout::Builder(
- CodePointLayout::Builder('c', k_layoutFont),
- HorizontalLayout::Builder(layoutChildren, denominatorSize)
- );
+ })
+ );
}
return m_layout;
}
@@ -65,21 +61,18 @@ double LogisticModel::partialDerivate(double * modelCoefficients, int derivateCo
double a = modelCoefficients[0];
double b = modelCoefficients[1];
double c = modelCoefficients[2];
- double denominator = 1.0+a*exp(-b*x);
+ double denominator = 1.0 + a * exp(-b * x);
if (derivateCoefficientIndex == 0) {
- // Derivate: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2)
- return -exp(-b*x) * c/(denominator * denominator);
+ // Derivate with respect to a: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2)
+ return -exp(-b * x) * c / (denominator * denominator);
}
if (derivateCoefficientIndex == 1) {
- // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
- return x*a*exp(-b*x)*c/(denominator * denominator);
+ // Derivate with respect to b: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
+ return x * a * exp(-b * x) * c / (denominator * denominator);
}
- if (derivateCoefficientIndex == 2) {
- // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
- return 1.0/denominator;
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 2);
+ // Derivate with respect to c: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2)
+ return 1.0 / denominator;
}
void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const {
@@ -90,7 +83,7 @@ void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients
* and c. Twice the standard vertical deviation is a rough estimate of c
* that is "close enough" to c to seed the coefficient, without being too
* dependent on outliers.*/
- modelCoefficients[2] = 2.0*store->standardDeviationOfColumn(series, 1);
+ modelCoefficients[2] = 2.0 * store->standardDeviationOfColumn(series, 1);
}
diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h
index c018a3528..78f14aab6 100644
--- a/apps/regression/model/model.h
+++ b/apps/regression/model/model.h
@@ -14,17 +14,18 @@ class Store;
class Model {
public:
enum class Type : uint8_t {
- Linear = 0,
- Quadratic = 1,
- Cubic = 2,
- Quartic = 3,
- Logarithmic = 4,
- Exponential = 5,
- Power = 6,
- Trigonometric = 7,
- Logistic = 8
+ Linear = 0,
+ Proportional = 1,
+ Quadratic = 2,
+ Cubic = 3,
+ Quartic = 4,
+ Logarithmic = 5,
+ Exponential = 6,
+ Power = 7,
+ Trigonometric = 8,
+ Logistic = 9
};
- static constexpr int k_numberOfModels = 9;
+ static constexpr int k_numberOfModels = 10;
static constexpr int k_maxNumberOfCoefficients = 5; // This has to verify: k_maxNumberOfCoefficients < Matrix::k_maxNumberOfCoefficients
virtual ~Model() = default;
virtual Poincare::Layout layout() = 0;
diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp
index c152c9c1d..a6b768f09 100644
--- a/apps/regression/model/power_model.cpp
+++ b/apps/regression/model/power_model.cpp
@@ -12,17 +12,15 @@ namespace Regression {
Layout PowerModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int size = 4;
- Layout layoutChildren[size] = {
+ m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('b', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
- };
- m_layout = HorizontalLayout::Builder(layoutChildren, size);
+ CodePointLayout::Builder('b', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
+ });
}
return m_layout;
}
@@ -46,19 +44,16 @@ double PowerModel::partialDerivate(double * modelCoefficients, int derivateCoeff
double a = modelCoefficients[0];
double b = modelCoefficients[1];
if (derivateCoefficientIndex == 0) {
- // Derivate: pow(x,b)
+ // Derivate with respect to a: pow(x,b)
return pow(x,b);
}
- if (derivateCoefficientIndex == 1) {
- assert(x >= 0);
- /* We assume all xi are positive.
- * For x = 0, a*pow(x,b) = 0, the partial derivate along b is 0
- * For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate along b is
- * ln(x)*a*pow(x,b) */
- return x == 0 ? 0 : log(x)*a*pow(x, b);
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 1);
+ assert(x >= 0);
+ /* We assume all xi are positive.
+ * For x = 0, a*pow(x,b) = 0, the partial derivate with respect to b is 0
+ * For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate with respect
+ * to b is ln(x)*a*pow(x,b) */
+ return x == 0.0 ? 0.0 : log(x) * a * pow(x, b);
}
void PowerModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) {
diff --git a/apps/regression/model/proportional_model.cpp b/apps/regression/model/proportional_model.cpp
new file mode 100644
index 000000000..3ae682488
--- /dev/null
+++ b/apps/regression/model/proportional_model.cpp
@@ -0,0 +1,36 @@
+#include "proportional_model.h"
+#include "../store.h"
+#include
+#include
+
+using namespace Poincare;
+
+namespace Regression {
+
+Layout ProportionalModel::layout() {
+ if (m_layout.isUninitialized()) {
+ const char * s = "a·X";
+ m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont);
+ }
+ return m_layout;
+}
+
+double ProportionalModel::evaluate(double * modelCoefficients, double x) const {
+ return modelCoefficients[0] * x;
+}
+
+double ProportionalModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) {
+ const double a = modelCoefficients[0];
+ if (a == 0.0) {
+ return NAN;
+ }
+ return y/a;
+}
+
+double ProportionalModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
+ assert(derivateCoefficientIndex == 0);
+ // Derivate: x
+ return x;
+}
+
+}
diff --git a/apps/regression/model/proportional_model.h b/apps/regression/model/proportional_model.h
new file mode 100644
index 000000000..8c2ff44d3
--- /dev/null
+++ b/apps/regression/model/proportional_model.h
@@ -0,0 +1,23 @@
+#ifndef REGRESSION_PROPORTIONAL_MODEL_H
+#define REGRESSION_PROPORTIONAL_MODEL_H
+
+#include "model.h"
+
+namespace Regression {
+
+class ProportionalModel : public Model {
+public:
+ using Model::Model;
+ Poincare::Layout layout() override;
+ I18n::Message formulaMessage() const override { return I18n::Message::ProportionalRegressionFormula; }
+ double evaluate(double * modelCoefficients, double x) const override;
+ double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override;
+ double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override;
+ int numberOfCoefficients() const override { return 1; }
+ int bannerLinesCount() const override { return 2; }
+};
+
+}
+
+
+#endif
diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp
index 67305337f..d258116fc 100644
--- a/apps/regression/model/quadratic_model.cpp
+++ b/apps/regression/model/quadratic_model.cpp
@@ -19,23 +19,21 @@ namespace Regression {
Layout QuadraticModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int size = 10;
- Layout layoutChildren[size] = {
+ m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('2', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('2', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
- };
- m_layout = HorizontalLayout::Builder(layoutChildren, size);
+ });
}
return m_layout;
}
@@ -49,19 +47,16 @@ double QuadraticModel::evaluate(double * modelCoefficients, double x) const {
double QuadraticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
if (derivateCoefficientIndex == 0) {
- // Derivate: x^2
+ // Derivate with respect to a: x^2
return x*x;
}
if (derivateCoefficientIndex == 1) {
- // Derivate: x
+ // Derivate with respect to b: x
return x;
}
- if (derivateCoefficientIndex == 2) {
- // Derivate: 1
- return 1;
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 2);
+ // Derivate with respect to c: 1
+ return 1.0;
}
Expression QuadraticModel::expression(double * modelCoefficients) {
@@ -69,19 +64,20 @@ Expression QuadraticModel::expression(double * modelCoefficients) {
double b = modelCoefficients[1];
double c = modelCoefficients[2];
// a*x^2+b*x+c
- Expression addChildren[] = {
- Multiplication::Builder(
+ return Addition::Builder({
+ Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(2.0))),
- Multiplication::Builder(
+ Decimal::Builder(2.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(b),
- Symbol::Builder('x')),
+ Symbol::Builder('x')
+ }),
Number::DecimalNumber(c)
- };
- Expression result = Addition::Builder(addChildren, 3);
- return result;
+ });
}
}
diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp
index 32a2323a8..3015b7f52 100644
--- a/apps/regression/model/quartic_model.cpp
+++ b/apps/regression/model/quartic_model.cpp
@@ -19,39 +19,37 @@ namespace Regression {
Layout QuarticModel::layout() {
if (m_layout.isUninitialized()) {
- constexpr int size = 20;
- Layout layoutChildren[size] = {
+ m_layout = HorizontalLayout::Builder({
CodePointLayout::Builder('a', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('4', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('4', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('b', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('3', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('3', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('c', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
VerticalOffsetLayout::Builder(
- CodePointLayout::Builder('2', k_layoutFont),
- VerticalOffsetLayoutNode::Position::Superscript
- ),
+ CodePointLayout::Builder('2', k_layoutFont),
+ VerticalOffsetLayoutNode::Position::Superscript
+ ),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('d', k_layoutFont),
CodePointLayout::Builder(UCodePointMiddleDot, k_layoutFont),
CodePointLayout::Builder('X', k_layoutFont),
CodePointLayout::Builder('+', k_layoutFont),
CodePointLayout::Builder('e', k_layoutFont),
- };
- m_layout = HorizontalLayout::Builder(layoutChildren, size);
+ });
}
return m_layout;
}
@@ -66,28 +64,24 @@ double QuarticModel::evaluate(double * modelCoefficients, double x) const {
}
double QuarticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
- if (derivateCoefficientIndex == 0) {
- // Derivate: x^4
- return x*x*x*x;
- }
- if (derivateCoefficientIndex == 1) {
- // Derivate: x^3
- return x*x*x;
- }
- if (derivateCoefficientIndex == 2) {
- // Derivate: x^2
- return x*x;
- }
- if (derivateCoefficientIndex == 3) {
- // Derivate: x
- return x;
- }
- if (derivateCoefficientIndex == 4) {
- // Derivate: 1
- return 1;
- }
- assert(false);
- return 0.0;
+ switch (derivateCoefficientIndex) {
+ case 0:
+ // Derivate with respect to a: x^4
+ return x*x*x*x;
+ case 1:
+ // Derivate with respect to b: x^3
+ return x*x*x;
+ case 2:
+ // Derivate with respect to c: x^2
+ return x*x;
+ case 3:
+ // Derivate with respect to d: x
+ return x;
+ default:
+ assert(derivateCoefficientIndex == 4);
+ // Derivate with respect to e: 1
+ return 1.0;
+ };
}
Expression QuarticModel::expression(double * modelCoefficients) {
@@ -96,34 +90,35 @@ Expression QuarticModel::expression(double * modelCoefficients) {
double c = modelCoefficients[2];
double d = modelCoefficients[3];
double e = modelCoefficients[4];
- Expression addChildren[] = {
- // a*x^4
- Multiplication::Builder(
+ // a*x^4+b*x^3+c*x^2+d*x+e
+ return Addition::Builder({
+ Multiplication::Builder({
Number::DecimalNumber(a),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(4.0))),
- // b*x^3
- Multiplication::Builder(
+ Decimal::Builder(4.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(b),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(3.0))),
- // c*x^2
- Multiplication::Builder(
+ Decimal::Builder(3.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(c),
Power::Builder(
Symbol::Builder('x'),
- Decimal::Builder(2.0))),
- // d*x
- Multiplication::Builder(
+ Decimal::Builder(2.0)
+ )
+ }),
+ Multiplication::Builder({
Number::DecimalNumber(d),
- Symbol::Builder('x')),
- // e
+ Symbol::Builder('x')
+ }),
Number::DecimalNumber(e)
- };
- Expression result = Addition::Builder(addChildren, 5);
- return result;
+ });
}
}
diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp
index f32f2c66b..b6ddd6359 100644
--- a/apps/regression/model/trigonometric_model.cpp
+++ b/apps/regression/model/trigonometric_model.cpp
@@ -46,28 +46,27 @@ double TrigonometricModel::evaluate(double * modelCoefficients, double x) const
}
double TrigonometricModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const {
+ if (derivateCoefficientIndex == 3) {
+ // Derivate with respect to d: 1
+ return 1.0;
+ }
+
double a = modelCoefficients[0];
double b = modelCoefficients[1];
double c = modelCoefficients[2];
double radianX = x * toRadians(Poincare::Preferences::sharedPreferences()->angleUnit());
+
if (derivateCoefficientIndex == 0) {
- // Derivate: sin(b*x+c)
- return sin(b*radianX+c);
+ // Derivate with respect to a: sin(b*x+c)
+ return sin(b * radianX + c);
}
if (derivateCoefficientIndex == 1) {
- // Derivate: x*a*cos(b*x+c);
- return radianX*a*cos(b*radianX+c);
+ // Derivate with respect to b: x*a*cos(b*x+c);
+ return radianX * a * cos(b * radianX + c);
}
- if (derivateCoefficientIndex == 2) {
- // Derivate: a*cos(b*x+c)
- return a*cos(b*radianX+c);
- }
- if (derivateCoefficientIndex == 3) {
- // Derivate: 1
- return 1.0;
- }
- assert(false);
- return 0.0;
+ assert(derivateCoefficientIndex == 2);
+ // Derivatewith respect to c: a*cos(b*x+c)
+ return a * cos(b * radianX + c);
}
void TrigonometricModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const {
diff --git a/apps/regression/regression_controller.cpp b/apps/regression/regression_controller.cpp
index 9cc70181f..188007d6b 100644
--- a/apps/regression/regression_controller.cpp
+++ b/apps/regression/regression_controller.cpp
@@ -82,7 +82,7 @@ HighlightCell * RegressionController::reusableCell(int index, int type) {
void RegressionController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
assert(i == 0);
assert(j >= 0 && j < k_numberOfRows);
- I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic};
+ I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Proportional, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic};
MessageTableCellWithExpression * castedCell = static_cast(cell);
castedCell->setMessage(messages[j]);
castedCell->setLayout(m_store->regressionModel((Model::Type) j)->layout());
diff --git a/apps/regression/regression_controller.h b/apps/regression/regression_controller.h
index f0875cc28..bb32f5dca 100644
--- a/apps/regression/regression_controller.h
+++ b/apps/regression/regression_controller.h
@@ -31,7 +31,7 @@ public:
int numberOfRows() const override { return k_numberOfRows; }
void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override;
private:
- constexpr static int k_numberOfRows = 9;
+ constexpr static int k_numberOfRows = 10;
constexpr static int k_numberOfCells = 6; // (240 - 70) / 35
MessageTableCellWithExpression m_regressionCells[k_numberOfCells];
SelectableTableView m_selectableTableView;
diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp
index 98f703bd5..cfba25903 100644
--- a/apps/regression/store.cpp
+++ b/apps/regression/store.cpp
@@ -5,15 +5,13 @@
#include
#include
#include
+#include
using namespace Shared;
namespace Regression {
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-static inline float minFloat(float x, float y) { return x < y ? x : y; }
-
-static_assert(Model::k_numberOfModels == 9, "Number of models changed, Regression::Store() needs to adapt");
+static_assert(Model::k_numberOfModels == 10, "Number of models changed, Regression::Store() needs to adapt");
static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regression::Store() needs to adapt (m_seriesChecksum)");
Store::Store() :
@@ -146,8 +144,8 @@ void Store::setDefault() {
float maxX = -FLT_MAX;
for (int series = 0; series < k_numberOfSeries; series++) {
if (!seriesIsEmpty(series)) {
- minX = minFloat(minX, minValueOfColumn(series, 0));
- maxX = maxFloat(maxX, maxValueOfColumn(series, 0));
+ minX = std::min(minX, minValueOfColumn(series, 0));
+ maxX = std::max(maxX, maxValueOfColumn(series, 0));
}
}
float range = maxX - minX;
@@ -199,7 +197,7 @@ void Store::resetMemoization() {
float Store::maxValueOfColumn(int series, int i) const {
float maxColumn = -FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
- maxColumn = maxFloat(maxColumn, m_data[series][i][k]);
+ maxColumn = std::max(maxColumn, m_data[series][i][k]);
}
return maxColumn;
}
@@ -207,7 +205,7 @@ float Store::maxValueOfColumn(int series, int i) const {
float Store::minValueOfColumn(int series, int i) const {
float minColumn = FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {
- minColumn = minFloat(minColumn, m_data[series][i][k]);
+ minColumn = std::min(minColumn, m_data[series][i][k]);
}
return minColumn;
}
@@ -287,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const {
}
Model * Store::regressionModel(int index) {
- Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel};
+ Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_proportionalModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel};
return models[index];
}
diff --git a/apps/regression/store.h b/apps/regression/store.h
index 2cc6f600f..92acd0f8f 100644
--- a/apps/regression/store.h
+++ b/apps/regression/store.h
@@ -8,6 +8,7 @@
#include "model/logarithmic_model.h"
#include "model/logistic_model.h"
#include "model/power_model.h"
+#include "model/proportional_model.h"
#include "model/quadratic_model.h"
#include "model/quartic_model.h"
#include "model/trigonometric_model.h"
@@ -79,6 +80,7 @@ private:
uint32_t m_seriesChecksum[k_numberOfSeries];
Model::Type m_regressionTypes[k_numberOfSeries];
LinearModel m_linearModel;
+ ProportionalModel m_proportionalModel;
QuadraticModel m_quadraticModel;
CubicModel m_cubicModel;
QuarticModel m_quarticModel;
diff --git a/apps/regression/test/model.cpp b/apps/regression/test/model.cpp
index b38d9936d..0abbad61a 100644
--- a/apps/regression/test/model.cpp
+++ b/apps/regression/test/model.cpp
@@ -43,6 +43,21 @@ QUIZ_CASE(linear_regression) {
assert_regression_is(x, y, 4, Model::Type::Linear, coefficients);
}
+QUIZ_CASE(proportional_regression) {
+ double x[] = {7.0, 5.0, 1.0, 9.0, 3.0};
+ double y[] = {-41.4851, -29.62186, -6.454245, -53.4976, -18.03325};
+ double coefficients[] = {-5.89};
+ assert_regression_is(x, y, 5, Model::Type::Proportional, coefficients);
+}
+
+QUIZ_CASE(proportional_regression2) {
+ constexpr int numberOfPoints = 4;
+ double x[numberOfPoints] = {5.0, 2.0, 3.0, 4.0};
+ double y[numberOfPoints] = {10.0, 6.0, 7.0, 8.0};
+ double coefficients[] = {2.12963963};
+ assert_regression_is(x, y, numberOfPoints, Model::Type::Proportional, coefficients);
+}
+
QUIZ_CASE(quadratic_regression) {
double x[] = {-34.0, -12.0, 5.0, 86.0, -2.0};
double y[] = {-8241.389, -1194.734, -59.163, - 46245.39, -71.774};
diff --git a/apps/rpn b/apps/rpn
index 8df287f39..279ec6333 160000
--- a/apps/rpn
+++ b/apps/rpn
@@ -1 +1 @@
-Subproject commit 8df287f39ac2ca7ce927078fc4d998a692caf283
+Subproject commit 279ec6333ab15e08988dff66eea01c6fdaee4ea0
diff --git a/apps/sequence/Makefile b/apps/sequence/Makefile
index f3373c41a..f36277bca 100644
--- a/apps/sequence/Makefile
+++ b/apps/sequence/Makefile
@@ -26,16 +26,9 @@ app_sequence_src = $(addprefix apps/sequence/,\
)
app_sequence_src += $(app_sequence_test_src)
-app_src += $(app_sequence_src)
+apps_src += $(app_sequence_src)
-i18n_files += $(addprefix apps/sequence/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
-)
+i18n_files += $(call i18n_without_universal_for,sequence/base)
tests_src += $(addprefix apps/sequence/test/,\
sequence.cpp\
diff --git a/apps/sequence/app.cpp b/apps/sequence/app.cpp
index fc68fad5d..d9d20f78c 100644
--- a/apps/sequence/app.cpp
+++ b/apps/sequence/app.cpp
@@ -14,8 +14,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::SequenceAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
diff --git a/apps/sequence/app.h b/apps/sequence/app.h
index 379494ee8..12139933b 100644
--- a/apps/sequence/app.h
+++ b/apps/sequence/app.h
@@ -19,7 +19,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public Shared::FunctionApp::Snapshot {
diff --git a/apps/sequence/base.it.i18n b/apps/sequence/base.it.i18n
new file mode 100644
index 000000000..d9ddac4a1
--- /dev/null
+++ b/apps/sequence/base.it.i18n
@@ -0,0 +1,22 @@
+SequenceApp = "Successioni"
+SequenceAppCapital = "SUCCESSIONI"
+SequenceTab = "Successioni"
+AddSequence = "Aggiungi successione"
+ChooseSequenceType = "Scegliere il tipo di successione"
+SequenceType = "Tipo di successione"
+Explicit = "Esplicita"
+SingleRecurrence = "Ricorrente d'ordine 1"
+DoubleRecurrence = "Ricorrente d'ordine 2"
+SequenceOptions = "Opzioni della successione"
+SequenceColor = "Colore della successione"
+DeleteSequence = "Cancella la successione"
+NoSequence = "Nessuna successione"
+NoActivatedSequence = "Nessuna successione attiva"
+NStart = "N iniziale"
+NEnd = "N finale"
+TermSum = "Somma dei termini"
+SelectFirstTerm = "Selezionare il primo termine "
+SelectLastTerm = "Selezionare l'ultimo termine "
+ValueNotReachedBySequence = "Valore non raggiunto dalla successione"
+NColumn = "Colonna n"
+FirstTermIndex = "Indice del primo termine"
diff --git a/apps/sequence/base.nl.i18n b/apps/sequence/base.nl.i18n
new file mode 100644
index 000000000..507923183
--- /dev/null
+++ b/apps/sequence/base.nl.i18n
@@ -0,0 +1,22 @@
+SequenceApp = "Rijen"
+SequenceAppCapital = "RIJEN"
+SequenceTab = "Rijen"
+AddSequence = "Rij toevoegen"
+ChooseSequenceType = "Kies type rij"
+SequenceType = "Rij-type"
+Explicit = "Expliciete uitdrukking"
+SingleRecurrence = "Recursief eerste orde"
+DoubleRecurrence = "Recursief tweede orde"
+SequenceOptions = "Rij-opties"
+SequenceColor = "Rij-kleur"
+DeleteSequence = "Rij verwijderen"
+NoSequence = "Geen rij ingevoerd"
+NoActivatedSequence = "Geen rij is ingeschakelt"
+NStart = "N begin"
+NEnd = "N einde"
+TermSum = "Som van termen"
+SelectFirstTerm = "Selecteer eerste term "
+SelectLastTerm = "Selecteer laatste term "
+ValueNotReachedBySequence = "Waarde niet bereikt door de rij"
+NColumn = "n-kolom"
+FirstTermIndex = "Eerste termindex"
diff --git a/apps/sequence/base.pt.i18n b/apps/sequence/base.pt.i18n
index cb0d9caec..97f6580f8 100644
--- a/apps/sequence/base.pt.i18n
+++ b/apps/sequence/base.pt.i18n
@@ -1,5 +1,5 @@
-SequenceApp = "Sequência"
-SequenceAppCapital = "SEQUÊNCIA"
+SequenceApp = "Sequências"
+SequenceAppCapital = "SEQUÊNCIAS"
SequenceTab = "Sequências"
AddSequence = "Adicionar uma sequência"
ChooseSequenceType = "Escolha o tipo de sequência"
@@ -11,7 +11,7 @@ SequenceOptions = "Opções de sequência"
SequenceColor = "Cor da sequência"
DeleteSequence = "Eliminar a sequência"
NoSequence = "Sem sequência"
-NoActivatedSequence = "Sem sequência activada"
+NoActivatedSequence = "Sem sequência ativada"
NStart = "N início"
NEnd = "N fim"
TermSum = "Soma dos termos"
diff --git a/apps/sequence/graph/curve_view_range.cpp b/apps/sequence/graph/curve_view_range.cpp
index 85c828d86..a4374164d 100644
--- a/apps/sequence/graph/curve_view_range.cpp
+++ b/apps/sequence/graph/curve_view_range.cpp
@@ -2,14 +2,13 @@
#include
#include
#include
+#include
using namespace Shared;
using namespace Poincare;
namespace Sequence {
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-
CurveViewRange::CurveViewRange(InteractiveCurveViewRangeDelegate * delegate) :
InteractiveCurveViewRange(delegate)
{
@@ -37,7 +36,7 @@ void CurveViewRange::normalize() {
float xMean = xCenter();
float yMean = yCenter();
- const float unit = maxFloat(xGridUnit(), yGridUnit());
+ const float unit = std::max(xGridUnit(), yGridUnit());
// Compute the X
const float newXHalfRange = NormalizedXHalfRange(unit);
diff --git a/apps/sequence/graph/graph_controller.cpp b/apps/sequence/graph/graph_controller.cpp
index 8e14c64c8..d35077101 100644
--- a/apps/sequence/graph/graph_controller.cpp
+++ b/apps/sequence/graph/graph_controller.cpp
@@ -2,15 +2,13 @@
#include
#include
#include "../app.h"
+#include
using namespace Shared;
using namespace Poincare;
namespace Sequence {
-static inline int minInt(int x, int y) { return (x < y ? x : y); }
-static inline int maxInt(int x, int y) { return (x > y ? x : y); }
-
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -39,7 +37,7 @@ float GraphController::interestingXMin() const {
int nbOfActiveModels = functionStore()->numberOfActiveFunctions();
for (int i = 0; i < nbOfActiveModels; i++) {
Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
- nmin = minInt(nmin, s->initialRank());
+ nmin = std::min(nmin, s->initialRank());
}
assert(nmin < INT_MAX);
return nmin;
@@ -53,8 +51,8 @@ float GraphController::interestingXHalfRange() const {
for (int i = 0; i < nbOfActiveModels; i++) {
Sequence * s = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i));
int firstInterestingIndex = s->initialRank();
- nmin = minInt(nmin, firstInterestingIndex);
- nmax = maxInt(nmax, firstInterestingIndex + standardRange);
+ nmin = std::min(nmin, firstInterestingIndex);
+ nmax = std::max(nmax, firstInterestingIndex + static_cast(standardRange));
}
assert(nmax - nmin >= standardRange);
return nmax - nmin;
diff --git a/apps/sequence/list/list_controller.cpp b/apps/sequence/list/list_controller.cpp
index 8b97f791b..48e59d38a 100644
--- a/apps/sequence/list/list_controller.cpp
+++ b/apps/sequence/list/list_controller.cpp
@@ -1,12 +1,11 @@
#include "list_controller.h"
#include "../app.h"
#include
+#include
using namespace Shared;
using namespace Poincare;
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-
namespace Sequence {
ListController::ListController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, ButtonRowController * footer) :
@@ -55,7 +54,7 @@ KDCoordinate ListController::expressionRowHeight(int j) {
return defaultHeight;
}
KDCoordinate sequenceHeight = layout.layoutSize().height();
- return maxCoordinate(defaultHeight, sequenceHeight + 2*k_expressionCellVerticalMargin);
+ return std::max(defaultHeight, sequenceHeight + 2*k_expressionCellVerticalMargin);
}
void ListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) {
diff --git a/apps/sequence/list/list_parameter_controller.cpp b/apps/sequence/list/list_parameter_controller.cpp
index b40cab658..ebdd85905 100644
--- a/apps/sequence/list/list_parameter_controller.cpp
+++ b/apps/sequence/list/list_parameter_controller.cpp
@@ -87,7 +87,7 @@ bool ListParameterController::textFieldDidFinishEditing(TextField * textField, c
return true;
}
-void ListParameterController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
+void ListParameterController::tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) {
if (withinTemporarySelection || (previousSelectedCellX == t->selectedColumn() && previousSelectedCellY == t->selectedRow())) {
return;
}
diff --git a/apps/sequence/list/list_parameter_controller.h b/apps/sequence/list/list_parameter_controller.h
index 6aaddb713..cc5ddae78 100644
--- a/apps/sequence/list/list_parameter_controller.h
+++ b/apps/sequence/list/list_parameter_controller.h
@@ -19,7 +19,7 @@ public:
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override;
- void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
+ void tableViewDidChangeSelectionAndDidScroll(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
// ListViewDataSource
HighlightCell * reusableCell(int index, int type) override;
diff --git a/apps/settings/Makefile b/apps/settings/Makefile
index 89731dd29..e5580265a 100644
--- a/apps/settings/Makefile
+++ b/apps/settings/Makefile
@@ -5,8 +5,16 @@ app_settings_src = $(addprefix apps/settings/,\
app.cpp \
cell_with_separator.cpp \
main_controller.cpp \
+ main_controller_prompt_beta.cpp:+beta \
+ main_controller_prompt_none.cpp:-beta \
+ main_controller_prompt_none.cpp:-update \
+ main_controller_prompt_update.cpp:+update \
sub_menu/about_controller.cpp \
sub_menu/accessibility_controller.cpp \
+ sub_menu/about_controller_official.cpp:+official \
+ sub_menu/about_controller_non_official.cpp:-official \
+ sub_menu/exam_mode_controller_official.cpp:+official \
+ sub_menu/exam_mode_controller_non_official.cpp:-official \
sub_menu/display_mode_controller.cpp \
sub_menu/exam_mode_controller.cpp \
sub_menu/generic_sub_controller.cpp \
@@ -18,32 +26,8 @@ app_settings_src = $(addprefix apps/settings/,\
)
app_settings_src += $(app_settings_test_src)
-app_src += $(app_settings_src)
+apps_src += $(app_settings_src)
-apps_prompt_none_src += apps/settings/main_controller_prompt_none.cpp
-apps_prompt_beta_src += apps/settings/main_controller_prompt_beta.cpp
-apps_prompt_update_src += apps/settings/main_controller_prompt_update.cpp
-
-apps_settings_official += $(addprefix apps/settings/,\
- sub_menu/about_controller_official.cpp \
- sub_menu/exam_mode_controller_official.cpp \
-)
-
-apps_settings_non_official += $(addprefix apps/settings/,\
- sub_menu/about_controller_non_official.cpp \
- sub_menu/exam_mode_controller_non_official.cpp \
-)
-
-apps_official += $(apps_settings_official)
-apps_non_official += $(apps_settings_non_official)
-
-i18n_files += $(addprefix apps/settings/,\
- base.de.i18n\
- base.en.i18n\
- base.es.i18n\
- base.fr.i18n\
- base.pt.i18n\
- base.hu.i18n\
-)
+i18n_files += $(call i18n_without_universal_for,settings/base)
$(eval $(call depends_on_image,apps/settings/app.cpp,apps/settings/settings_icon.png))
diff --git a/apps/settings/app.cpp b/apps/settings/app.cpp
index a1c298eba..7e341c3f5 100644
--- a/apps/settings/app.cpp
+++ b/apps/settings/app.cpp
@@ -12,8 +12,8 @@ I18n::Message App::Descriptor::upperName() {
return I18n::Message::SettingsAppCapital;
}
-int App::Descriptor::examinationLevel() {
- return App::Descriptor::StrictExaminationLevel;
+App::Descriptor::ExaminationLevel App::Descriptor::examinationLevel() {
+ return App::Descriptor::ExaminationLevel::Strict;
}
const Image * App::Descriptor::icon() {
diff --git a/apps/settings/app.h b/apps/settings/app.h
index eb6b17cf1..f67e75896 100644
--- a/apps/settings/app.h
+++ b/apps/settings/app.h
@@ -12,7 +12,7 @@ public:
public:
I18n::Message name() override;
I18n::Message upperName() override;
- int examinationLevel() override;
+ App::Descriptor::ExaminationLevel examinationLevel() override;
const Image * icon() override;
};
class Snapshot : public ::App::Snapshot {
diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n
index bc10bd120..c813ffe83 100644
--- a/apps/settings/base.de.i18n
+++ b/apps/settings/base.de.i18n
@@ -5,10 +5,10 @@ DisplayMode = "Zahlenformat"
EditionMode = "Eingabe"
EditionLinear = "Linear "
Edition2D = "Natürlich "
-ComplexFormat = "Komplex"
-ExamMode = "Testmodus"
-ExamModeActive = "Testmodus neustarten"
-ToDeactivateExamMode1 = "Um den Testmodus auszuschalten,"
+ComplexFormat = "Komplexe Zahlen"
+ExamMode = "Prüfungsmodus"
+ExamModeActive = "Modus erneut starten"
+ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten,"
ToDeactivateExamMode2 = "schließen Sie den Rechner an einen"
ToDeactivateExamMode3 = "Computer oder eine Steckdose an."
# --------------------- Please do not edit these messages ---------------------
@@ -22,26 +22,23 @@ AboutWarning4 = "for any resulting damage."
# -----------------------------------------------------------------------------
About = "Über"
Degrees = "Grad "
-Gradians = "Gone "
+Gradians = "Gon "
Radian = "Bogenmaß "
Decimal = "Dezimal "
Engineering = "Technisch "
Scientific = "Wissenschaftlich "
SignificantFigures = "Signifikante Stellen "
-Real = "Reel "
-Cartesian = "Algebraische "
+Real = "Reell "
+Cartesian = "Kartesisch "
Polar = "Polar "
Brightness = "Helligkeit"
SoftwareVersion = "Epsilon version"
-CustomSoftwareVersion = "Omega version"
+OmegaVersion = "Omega version"
Username = "Name"
MicroPythonVersion = "µPythonversion"
-ResultDisplay = "Ergebniswiedergabe"
-DefaultResult = "Standard "
-CompactResult = "Compact "
-FontSizes = "Python Schriftgröße"
-LargeFont = "Groß "
-SmallFont = "Klein "
+FontSizes = "Python-Schriftgröße"
+LargeFont = "Große "
+SmallFont = "Kleine "
SerialNumber = "Seriennummer"
UpdatePopUp = "Erinnerung: Update"
BetaPopUp = "Beta pop-up"
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "Kreuz "
SymbolMultiplicationMiddleDot = "Mittelpunkt "
SymbolMultiplicationStar = "Stern "
SymbolMultiplicationAutoSymbol = "automatisch "
+SymbolFunction = "Ausdrucksformat "
+SymbolDefaultFunction = "Standardl "
+SymbolArgFunction = "Leer "
+SymbolArgDefaultFunction = "Argument "
PythonFont = "Python Schriftart"
-Large = "Groß "
-Small = "Klein "
MemUse = "Speicher"
diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n
index e09b048fa..5990f4c9d 100644
--- a/apps/settings/base.en.i18n
+++ b/apps/settings/base.en.i18n
@@ -33,12 +33,9 @@ Cartesian = "Cartesian "
Polar = "Polar "
Brightness = "Brightness"
SoftwareVersion = "Epsilon version"
-CustomSoftwareVersion = "Omega version"
+OmegaVersion = "Omega version"
Username = "Name"
MicroPythonVersion = "µPython version"
-ResultDisplay = "Result display"
-DefaultResult = "Default "
-CompactResult = "Compact "
FontSizes = "Python font size"
LargeFont = "Large "
SmallFont = "Small "
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "Cross "
SymbolMultiplicationMiddleDot = "Dot "
SymbolMultiplicationStar = "Star "
SymbolMultiplicationAutoSymbol = "Auto "
+SymbolFunction = "Expression format "
+SymbolDefaultFunction = "Default "
+SymbolArgFunction = "Empty "
+SymbolArgDefaultFunction = "Argument "
PythonFont = "Python Font"
-Large = "Large "
-Small = "Small "
MemUse = "Memory"
diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n
index c8d4a88a1..49aa842f5 100644
--- a/apps/settings/base.es.i18n
+++ b/apps/settings/base.es.i18n
@@ -33,12 +33,9 @@ Cartesian = "Binómica "
Polar = "Polar "
Brightness = "Brillo"
SoftwareVersion = "Versión de Epsilon"
-CustomSoftwareVersion = "Versión de Omega"
+OmegaVersion = "Versión de Omega"
Username = "Apellido"
MicroPythonVersion = "Version de µPython"
-ResultDisplay = "Visualización resultados"
-DefaultResult = "Defecto "
-CompactResult = "Compacto "
FontSizes = "Tipografía Python"
LargeFont = "Grande "
SmallFont = "Pequeño "
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "Contrariar "
SymbolMultiplicationMiddleDot = "Punto "
SymbolMultiplicationStar = "Estrella "
SymbolMultiplicationAutoSymbol = "Auto "
+SymbolFunction = "Formato expresión "
+SymbolDefaultFunction = "Defecto "
+SymbolArgFunction = "Vacío "
+SymbolArgDefaultFunction = "Argumento "
PythonFont = "Fuente Python"
-Large = "Grande "
-Small = "Pequeña "
MemUse = "Memoria"
diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n
index a23f8a0b9..9e9db0495 100644
--- a/apps/settings/base.fr.i18n
+++ b/apps/settings/base.fr.i18n
@@ -33,18 +33,15 @@ Cartesian = "Algébrique "
Polar = "Exponentielle "
Brightness = "Luminosité"
SoftwareVersion = "Version d'Epsilon"
-CustomSoftwareVersion = "Version d'Omega"
+OmegaVersion = "Version d'Omega"
Username = "Nom"
MicroPythonVersion = "Version de µPython"
-ResultDisplay = "Mode d'affichage"
-DefaultResult = "Défaut "
-CompactResult = "Compact "
FontSizes = "Police Python"
-LargeFont = "Grand "
-SmallFont = "Petit "
-SerialNumber = "Numéro série"
-UpdatePopUp = "Rappel mise à jour"
-BetaPopUp = "Rappel version bêta"
+LargeFont = "Grande "
+SmallFont = "Petite "
+SerialNumber = "Numéro de série"
+UpdatePopUp = "Rappel de mise à jour"
+BetaPopUp = "Rappel de version bêta"
Contributors = "Contributeurs"
Accessibility = "Accessibilité"
AccessibilityInvertColors = "Inverser couleurs"
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "Croix "
SymbolMultiplicationMiddleDot = "Point "
SymbolMultiplicationStar = "Etoile "
SymbolMultiplicationAutoSymbol = "Automatique "
+SymbolFunction = "Format expression "
+SymbolDefaultFunction = "Défaut "
+SymbolArgFunction = "Vide "
+SymbolArgDefaultFunction = "Arguments "
PythonFont = "Police Python"
-Large = "Grand "
-Small = "Petit "
MemUse = "Mémoire"
diff --git a/apps/settings/base.hu.i18n b/apps/settings/base.hu.i18n
index b3828759e..79e0650c7 100644
--- a/apps/settings/base.hu.i18n
+++ b/apps/settings/base.hu.i18n
@@ -33,12 +33,9 @@ Cartesian = "Kartéziánus "
Polar = "Poláris "
Brightness = "Fényerö"
SoftwareVersion = "Epsilon verzió"
-CustomSoftwareVersion = "Omega verzió"
+OmegaVersion = "Omega verzió"
Username = "Felhasználónév"
MicroPythonVersion = "µPython verzió"
-ResultDisplay = "Eredménykijelzö"
-DefaultResult = "Alapértelmezett "
-CompactResult = "Kompakt "
FontSizes = "Python betü méret"
LargeFont = "Nagy "
SmallFont = "Kicsi "
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "Kereszt"
SymbolMultiplicationMiddleDot = "Pont "
SymbolMultiplicationStar = "Csillag "
SymbolMultiplicationAutoSymbol = "Automata "
+SymbolFunction = "Kifejezési formátum "
+SymbolDefaultFunction = "alapértelmezett "
+SymbolArgFunction = "Üres "
+SymbolArgDefaultFunction = "Argumentummal "
PythonFont = "Python Betütipus"
-Large = "Nagy "
-Small = "Kicsi "
MemUse = "Memória"
diff --git a/apps/settings/base.it.i18n b/apps/settings/base.it.i18n
new file mode 100644
index 000000000..2daf43950
--- /dev/null
+++ b/apps/settings/base.it.i18n
@@ -0,0 +1,64 @@
+SettingsApp = "Impostazioni"
+SettingsAppCapital = "IMPOSTAZIONI"
+AngleUnit = "Misura angolo"
+DisplayMode = "Forma risultato"
+EditionMode = "Forma scrittura"
+EditionLinear = "In linea "
+Edition2D = "Naturale "
+ComplexFormat = "Forma complessa"
+ExamMode = "Modalità Esame"
+ExamModeActive = "Riattivare modalità Esame"
+ToDeactivateExamMode1 = "Per disattivare la modalità esame,"
+ToDeactivateExamMode2 = "collegare la calcolatrice a un"
+ToDeactivateExamMode3 = "computer o a una presa di corrente."
+# --------------------- Please do not edit these messages ---------------------
+ExamModeWarning1 = "Attenzione, la conformità della modalità"
+ExamModeWarning2 = "esame di questo software non ufficiale"
+ExamModeWarning3 = "non è garantita da NumWorks."
+AboutWarning1 = "Attenzione, voi utilizzate una versione"
+AboutWarning2 = "non ufficiale del software. NumWorks"
+AboutWarning3 = "non potrà essere ritenuto responsabile dei"
+AboutWarning4 = "problemi che questo potrebbe comportare."
+# -----------------------------------------------------------------------------
+About = "Informazioni su"
+Degrees = "Gradi "
+Gradians = "Gradienti "
+Radian = "Radianti "
+Decimal = "Decimale "
+Scientific = "Scientifico "
+Engineering = "Ingegneria "
+SignificantFigures = "Cifre significative "
+Real = "Reale "
+Cartesian = "Algebrico "
+Polar = "Esponenziale "
+Brightness = "Luminosità"
+SoftwareVersion = "Epsilon version"
+OmegaVersion = "Omega version"
+Username = "Name"
+MicroPythonVersion = "µPython version"
+FontSizes = "Carattere Python"
+LargeFont = "Grande "
+SmallFont = "Piccolo "
+SerialNumber = "Numero di serie"
+UpdatePopUp = "Promemoria aggiornamento"
+BetaPopUp = "Promemoria beta"
+Contributors = "Contributors"
+Accessibility = "Accessibility"
+AccessibilityInvertColors = "Invert colors"
+AccessibilityMagnify = "Magnify"
+AccessibilityGamma = "Gamma correction"
+AccessibilityGammaRed = "Red gamma"
+AccessibilityGammaGreen = "Green gamma"
+AccessibilityGammaBlue = "Blue gamma"
+MathOptions = "Math options"
+SymbolMultiplication = "Multiply"
+SymbolMultiplicationCross = "Cross "
+SymbolMultiplicationMiddleDot = "Dot "
+SymbolMultiplicationStar = "Star "
+SymbolMultiplicationAutoSymbol = "Auto "
+SymbolFunction = "Expression format "
+SymbolDefaultFunction = "Default "
+SymbolArgFunction = "Empty "
+SymbolArgDefaultFunction = "Argument "
+PythonFont = "Python Font"
+MemUse = "Memory"
diff --git a/apps/settings/base.nl.i18n b/apps/settings/base.nl.i18n
new file mode 100644
index 000000000..b6d9ef1c1
--- /dev/null
+++ b/apps/settings/base.nl.i18n
@@ -0,0 +1,64 @@
+SettingsApp = "Instellingen"
+SettingsAppCapital = "INSTELLINGEN"
+AngleUnit = "Hoekmaat"
+DisplayMode = "Resultaat formaat"
+EditionMode = "Schrijfformaat"
+EditionLinear = "Lineair "
+Edition2D = "Natuurlijk "
+ComplexFormat = "Complex formaat"
+ExamMode = "Examenstand"
+ExamModeActive = "Herstart examenstand"
+ToDeactivateExamMode1 = "Om de examenstand te verlaten,"
+ToDeactivateExamMode2 = "sluit de rekenmachine aan op"
+ToDeactivateExamMode3 = "een computer of stopcontact."
+# --------------------- Please do not edit these messages - Discuss with Leo (remove this later) ---------------------
+ExamModeWarning1 = "Let op: naleving van de examenstand"
+ExamModeWarning2 = "op deze onofficiële software wordt"
+ExamModeWarning3 = "door NumWorks niet gegarandeerd."
+AboutWarning1 = "Let op: je gebruikt een"
+AboutWarning2 = "onofficiële softwareversie."
+AboutWarning3 = "NumWorks is niet verantwoordelijk"
+AboutWarning4 = "voor mogelijke resulterende schade"
+# -----------------------------------------------------------------------------
+About = "Over deze rekenmachine"
+Degrees = "Graden "
+Gradians = "Decimale graden "
+Radian = "Radialen "
+Decimal = "Decimaal "
+Scientific = "Wetenschappelijk "
+Engineering = "Engineering "
+SignificantFigures = "Significante cijfers "
+Real = "Reëel "
+Cartesian = "Cartesisch "
+Polar = "Polair "
+Brightness = "Helderheid"
+SoftwareVersion = "Epsilon version"
+OmegaVersion = "Omega version"
+Username = "Name"
+MicroPythonVersion = "µPython version"
+FontSizes = "Python lettergrootte"
+LargeFont = "Groot "
+SmallFont = "Klein "
+SerialNumber = "Serienummer"
+UpdatePopUp = "Update pop-up"
+BetaPopUp = "Bèta pop-up"
+Contributors = "Contributors"
+Accessibility = "Accessibility"
+AccessibilityInvertColors = "Invert colors"
+AccessibilityMagnify = "Magnify"
+AccessibilityGamma = "Gamma correction"
+AccessibilityGammaRed = "Red gamma"
+AccessibilityGammaGreen = "Green gamma"
+AccessibilityGammaBlue = "Blue gamma"
+MathOptions = "Math options"
+SymbolMultiplication = "Multiply"
+SymbolMultiplicationCross = "Cross "
+SymbolMultiplicationMiddleDot = "Dot "
+SymbolMultiplicationStar = "Star "
+SymbolMultiplicationAutoSymbol = "Auto "
+SymbolFunction = "Expression format "
+SymbolDefaultFunction = "Default "
+SymbolArgFunction = "Empty "
+SymbolArgDefaultFunction = "Argument "
+PythonFont = "Python Font"
+MemUse = "Memory"
diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n
index c3fb57e31..5c36527fc 100644
--- a/apps/settings/base.pt.i18n
+++ b/apps/settings/base.pt.i18n
@@ -1,24 +1,24 @@
-SettingsApp = "Configuração"
-SettingsAppCapital = "CONFIGURAÇÃO"
-AngleUnit = "Valor do angulo"
+SettingsApp = "Definições"
+SettingsAppCapital = "DEFINIÇÕES"
+AngleUnit = "Valor do ângulo"
DisplayMode = "Formato numérico"
EditionMode = "Formato escrita "
EditionLinear = "Em linha "
Edition2D = "Natural "
ComplexFormat = "Complexos"
ExamMode = "Modo de exame"
-ExamModeActive = "Reactivar o modo de exame"
-ToDeactivateExamMode1 = "Para desactivar o modo de exame,"
+ExamModeActive = "Reativar o modo de exame"
+ToDeactivateExamMode1 = "Para desativar o modo de exame,"
ToDeactivateExamMode2 = "ligue a calculadora a um computador"
-ToDeactivateExamMode3 = "ou a uma tomada eléctrica."
+ToDeactivateExamMode3 = "ou a uma tomada elétrica."
# --------------------- Please do not edit these messages ---------------------
-ExamModeWarning1 = "Caution: compliance of this"
-ExamModeWarning2 = "unofficial software's exam mode"
-ExamModeWarning3 = "is not guaranteed by NumWorks."
-AboutWarning1 = "Caution: you're using an"
-AboutWarning2 = "unofficial software version."
-AboutWarning3 = "NumWorks can't be held responsible"
-AboutWarning4 = "for any resulting damage."
+ExamModeWarning1 = "Cuidado: o software que está a utilizar"
+ExamModeWarning2 = "não é oficial. A sua conformidade com o"
+ExamModeWarning3 = "Modo de Exame não é garantida pela NumWorks."
+AboutWarning1 = "Cuidado: está a usar uma"
+AboutWarning2 = "versão não oficial do software."
+AboutWarning3 = "A NumWorks não pode ser responsável"
+AboutWarning4 = "por qualquer dano resultante."
# -----------------------------------------------------------------------------
About = "Acerca"
Degrees = "Graus "
@@ -27,18 +27,15 @@ Radian = "Radianos "
Decimal = "Decimal "
Scientific = "Científico "
Engineering = "Engenharia "
-SignificantFigures = "Algarismo significativo "
+SignificantFigures = "Alg. significativos "
Real = "Real "
Cartesian = "Cartesiana "
Polar = "Polar "
Brightness = "Brilho"
SoftwareVersion = "Versão do Epsilon"
-CustomSoftwareVersion = "Versão do Omega"
+OmegaVersion = "Versão do Omega"
Username = "Nome"
MicroPythonVersion = "Versao do µPython"
-ResultDisplay = "Exibição resultado"
-DefaultResult = "Padrão "
-CompactResult = "Compactar "
FontSizes = "Tipografia Python"
LargeFont = "Grande "
SmallFont = "Pequeno "
@@ -59,7 +56,9 @@ SymbolMultiplicationCross = "crómio "
SymbolMultiplicationMiddleDot = "ponto médio "
SymbolMultiplicationStar = "estrela "
SymbolMultiplicationAutoSymbol = "automático "
+SymbolFunction = "Formato expressão "
+SymbolDefaultFunction = "Padrão "
+SymbolArgFunction = "Vazio "
+SymbolArgDefaultFunction = "Argumento "
PythonFont = "Fonte Python"
-Large = "Ampla "
-Small = "Pequeno "
MemUse = "Memória"
diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp
index bf86296d0..02f17f1dd 100644
--- a/apps/settings/main_controller.cpp
+++ b/apps/settings/main_controller.cpp
@@ -14,15 +14,15 @@ constexpr SettingsMessageTree s_modelEditionModeChildren[2] = {SettingsMessageTr
constexpr SettingsMessageTree s_modelFloatDisplayModeChildren[4] = {SettingsMessageTree(I18n::Message::Decimal), SettingsMessageTree(I18n::Message::Scientific), SettingsMessageTree(I18n::Message::Engineering), SettingsMessageTree(I18n::Message::SignificantFigures)};
constexpr SettingsMessageTree s_modelComplexFormatChildren[3] = {SettingsMessageTree(I18n::Message::Real), SettingsMessageTree(I18n::Message::Cartesian), SettingsMessageTree(I18n::Message::Polar)};
constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::Message::SymbolMultiplicationCross),SettingsMessageTree(I18n::Message::SymbolMultiplicationMiddleDot),SettingsMessageTree(I18n::Message::SymbolMultiplicationStar),SettingsMessageTree(I18n::Message::SymbolMultiplicationAutoSymbol)};
-constexpr SettingsMessageTree s_modelMathOptionsChildren[5] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren)};
-constexpr SettingsMessageTree s_modelResultDisplayChildren[2] = {SettingsMessageTree(I18n::Message::DefaultResult), SettingsMessageTree(I18n::Message::CompactResult)};
+constexpr SettingsMessageTree s_symbolFunctionChildren[3] = {SettingsMessageTree(I18n::Message::SymbolDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgFunction)};
+constexpr SettingsMessageTree s_modelMathOptionsChildren[6] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren), SettingsMessageTree(I18n::Message::SymbolFunction, s_symbolFunctionChildren), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren)};
constexpr SettingsMessageTree s_modelFontChildren[2] = {SettingsMessageTree(I18n::Message::LargeFont), SettingsMessageTree(I18n::Message::SmallFont)};
constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)};
-constexpr SettingsMessageTree s_contributorsChildren[16] = {SettingsMessageTree(I18n::Message::Developers), SettingsMessageTree(I18n::Message::QuentinGuidee), SettingsMessageTree(I18n::Message::DannySimmons), SettingsMessageTree(I18n::Message::JoachimLeFournis), SettingsMessageTree(I18n::Message::JeanBaptisteBoric), SettingsMessageTree(I18n::Message::MaximeFriess), SettingsMessageTree(I18n::Message::David), SettingsMessageTree(I18n::Message::DamienNicolet), SettingsMessageTree(I18n::Message::EvannDreumont), SettingsMessageTree(I18n::Message::SzaboLevente), SettingsMessageTree(I18n::Message::VenceslasDuet), SettingsMessageTree(I18n::Message::BetaTesters), SettingsMessageTree(I18n::Message::CyprienMejat), SettingsMessageTree(I18n::Message::TimeoArnouts), SettingsMessageTree(I18n::Message::LouisC), SettingsMessageTree(I18n::Message::LelahelHideux)};
+constexpr SettingsMessageTree s_contributorsChildren[20] = {SettingsMessageTree(I18n::Message::Developers), SettingsMessageTree(I18n::Message::QuentinGuidee), SettingsMessageTree(I18n::Message::SandraSimmons), SettingsMessageTree(I18n::Message::JoachimLeFournis), SettingsMessageTree(I18n::Message::JeanBaptisteBoric), SettingsMessageTree(I18n::Message::MaximeFriess), SettingsMessageTree(I18n::Message::David), SettingsMessageTree(I18n::Message::DamienNicolet), SettingsMessageTree(I18n::Message::EvannDreumont), SettingsMessageTree(I18n::Message::SzaboLevente), SettingsMessageTree(I18n::Message::VenceslasDuet), SettingsMessageTree(I18n::Message::CharlotteThomas), SettingsMessageTree(I18n::Message::AntoninLoubiere), SettingsMessageTree(I18n::Message::BetaTesters), SettingsMessageTree(I18n::Message::CyprienMejat), SettingsMessageTree(I18n::Message::TimeoArnouts), SettingsMessageTree(I18n::Message::JulieC), SettingsMessageTree(I18n::Message::LelahelHideux), SettingsMessageTree(I18n::Message::Madil), SettingsMessageTree(I18n::Message::HilaireLeRoux)};
#ifdef USERNAME
-constexpr SettingsMessageTree s_modelAboutChildren[8] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::CustomSoftwareVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)};
+constexpr SettingsMessageTree s_modelAboutChildren[8] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::OmegaVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)};
#else
-constexpr SettingsMessageTree s_modelAboutChildren[7] = {SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::CustomSoftwareVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)};
+constexpr SettingsMessageTree s_modelAboutChildren[7] = {SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::OmegaVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)};
#endif
MainController::MainController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate) :
@@ -31,7 +31,7 @@ MainController::MainController(Responder * parentResponder, InputEventHandlerDel
m_popUpCell(I18n::Message::Default, KDFont::LargeFont),
m_selectableTableView(this),
m_mathOptionsController(this, inputEventHandlerDelegate),
- m_languageController(this, 13),
+ m_languageController(this, Metric::CommonTopMargin),
m_accessibilityController(this),
m_examModeController(this),
m_aboutController(this),
@@ -62,8 +62,8 @@ bool MainController::handleEvent(Ion::Events::Event event) {
m_selectableTableView.reloadCellAtLocation(m_selectableTableView.selectedColumn(), 1);
return true;
}
- if (model()->children(selectedRow())->numberOfChildren() == 0) {
- if (model()->children(selectedRow())->label() == promptMessage()) {
+ if (model()->childAtIndex(selectedRow())->numberOfChildren() == 0) {
+ if (model()->childAtIndex(selectedRow())->label() == promptMessage()) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
globalPreferences->setShowPopUp(!globalPreferences->showPopUp());
m_selectableTableView.reloadCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow());
@@ -71,7 +71,7 @@ bool MainController::handleEvent(Ion::Events::Event event) {
}
return false;
}
- if (model()->children(selectedRow())->label() == I18n::Message::Brightness) {
+ if (model()->childAtIndex(selectedRow())->label() == I18n::Message::Brightness) {
if (event == Ion::Events::Right || event == Ion::Events::Left || event == Ion::Events::Plus || event == Ion::Events::Minus) {
int delta = Ion::Backlight::MaxBrightness/GlobalPreferences::NumberOfBrightnessStates;
int direction = (event == Ion::Events::Right || event == Ion::Events::Plus) ? delta : -delta;
@@ -81,7 +81,7 @@ bool MainController::handleEvent(Ion::Events::Event event) {
}
return false;
}
- if (model()->children(selectedRow())->label() == I18n::Message::Language) {
+ if (model()->childAtIndex(selectedRow())->label() == I18n::Message::Language) {
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
stackController()->push(&m_languageController);
return true;
@@ -91,7 +91,7 @@ bool MainController::handleEvent(Ion::Events::Event event) {
}
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
GenericSubController * subController = nullptr;
- I18n::Message title = model()->children(selectedRow())->label();
+ I18n::Message title = model()->childAtIndex(selectedRow())->label();
if (title == I18n::Message::Brightness || title == I18n::Message::Language) {
assert(false);
} else if (title == I18n::Message::ExamMode) {
@@ -105,7 +105,7 @@ bool MainController::handleEvent(Ion::Events::Event event) {
} else {
subController = &m_preferencesController;
}
- subController->setMessageTreeModel(model()->children(selectedRow()));
+ subController->setMessageTreeModel(model()->childAtIndex(selectedRow()));
StackViewController * stack = stackController();
stack->push(subController);
return true;
@@ -118,7 +118,7 @@ int MainController::numberOfRows() const {
};
KDCoordinate MainController::rowHeight(int j) {
- if (model()->children(j)->label() == I18n::Message::Brightness) {
+ if (model()->childAtIndex(j)->label() == I18n::Message::Brightness) {
return Metric::ParameterCellHeight + CellWithSeparator::k_margin;
}
return Metric::ParameterCellHeight;
@@ -161,10 +161,10 @@ int MainController::reusableCellCount(int type) {
}
int MainController::typeAtLocation(int i, int j) {
- if (model()->children(j)->label() == I18n::Message::Brightness) {
+ if (model()->childAtIndex(j)->label() == I18n::Message::Brightness) {
return 1;
}
- if (model()->children(j)->label() == I18n::Message::UpdatePopUp || model()->children(j)->label() == I18n::Message::BetaPopUp) {
+ if (model()->childAtIndex(j)->label() == I18n::Message::UpdatePopUp || model()->childAtIndex(j)->label() == I18n::Message::BetaPopUp) {
return 2;
}
return 0;
@@ -172,8 +172,8 @@ int MainController::typeAtLocation(int i, int j) {
void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) {
GlobalPreferences * globalPreferences = GlobalPreferences::sharedGlobalPreferences();
- I18n::Message title = model()->children(index)->label();
- if (model()->children(index)->label() == I18n::Message::Brightness) {
+ I18n::Message title = model()->childAtIndex(index)->label();
+ if (model()->childAtIndex(index)->label() == I18n::Message::Brightness) {
MessageTableCellWithGaugeWithSeparator * myGaugeCell = (MessageTableCellWithGaugeWithSeparator *)cell;
myGaugeCell->setMessage(title);
GaugeView * myGauge = (GaugeView *)myGaugeCell->accessoryView();
@@ -182,12 +182,12 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) {
}
MessageTableCell * myCell = (MessageTableCell *)cell;
myCell->setMessage(title);
- if (model()->children(index)->label() == I18n::Message::Language) {
- int index = (int)globalPreferences->language()-1;
+ if (model()->childAtIndex(index)->label() == I18n::Message::Language) {
+ int index = (int)(globalPreferences->language());
static_cast(cell)->setSubtitle(I18n::LanguageNames[index]);
return;
}
- if (model()->children(index)->label() == I18n::Message::UpdatePopUp || model()->children(index)->label() == I18n::Message::BetaPopUp) {
+ if (model()->childAtIndex(index)->label() == I18n::Message::UpdatePopUp || model()->childAtIndex(index)->label() == I18n::Message::BetaPopUp) {
MessageTableCellWithSwitch * mySwitchCell = (MessageTableCellWithSwitch *)cell;
SwitchView * mySwitch = (SwitchView *)mySwitchCell->accessoryView();
mySwitch->setState(globalPreferences->showPopUp());
@@ -195,14 +195,14 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) {
}
MessageTableCellWithChevronAndMessage * myTextCell = (MessageTableCellWithChevronAndMessage *)cell;
int childIndex = -1;
- switch (model()->children(index)->label()) {
+ switch (model()->childAtIndex(index)->label()) {
case I18n::Message::FontSizes:
childIndex = GlobalPreferences::sharedGlobalPreferences()->font() == KDFont::LargeFont ? 0 : 1;
break;
default:
break;
}
- I18n::Message message = childIndex >= 0 ? model()->children(index)->children(childIndex)->label() : I18n::Message::Default;
+ I18n::Message message = childIndex >= 0 ? model()->childAtIndex(index)->childAtIndex(childIndex)->label() : I18n::Message::Default;
myTextCell->setSubtitle(message);
}
diff --git a/apps/settings/main_controller.h b/apps/settings/main_controller.h
index e0e2066ef..62f1d3c36 100644
--- a/apps/settings/main_controller.h
+++ b/apps/settings/main_controller.h
@@ -18,11 +18,11 @@ extern const Shared::SettingsMessageTree s_modelEditionModeChildren[2];
extern const Shared::SettingsMessageTree s_modelFloatDisplayModeChildren[4];
extern const Shared::SettingsMessageTree s_modelComplexFormatChildren[3];
extern const Shared::SettingsMessageTree s_symbolChildren[4];
-extern const Shared::SettingsMessageTree s_modelMathOptionsChildren[5];
-extern const Shared::SettingsMessageTree s_modelResultDisplayChildren[2];
+extern const Shared::SettingsMessageTree s_symbolFunctionChildren[3];
+extern const Shared::SettingsMessageTree s_modelMathOptionsChildren[6];
extern const Shared::SettingsMessageTree s_modelFontChildren[2];
extern const Shared::SettingsMessageTree s_accessibilityChildren[6];
-extern const Shared::SettingsMessageTree s_contributorsChildren[16];
+extern const Shared::SettingsMessageTree s_contributorsChildren[20];
#ifdef USERNAME
extern const Shared::SettingsMessageTree s_modelAboutChildren[8];
#else
@@ -51,8 +51,7 @@ private:
constexpr static int k_indexOfBrightnessCell = k_indexOfMathOptionsChildren + 1;
constexpr static int k_indexOfLanguageCell = k_indexOfBrightnessCell + 1;
constexpr static int k_indexOfExamModeCell = k_indexOfLanguageCell + 1;
- constexpr static int k_indexOfResultDisplayCell = k_indexOfExamModeCell + 1;
- constexpr static int k_indexOfFontCell = k_indexOfResultDisplayCell + 1;
+ constexpr static int k_indexOfFontCell = k_indexOfExamModeCell + 1;
/* Pop-up cell and About cell are located at the same index because pop-up
* cell is optional. We must always correct k_indexOfAboutCell with
* hasPrompt() (TODO: make hasPrompt() constexpr and correct
diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp
index c5eb3fef6..518460f33 100644
--- a/apps/settings/main_controller_prompt_beta.cpp
+++ b/apps/settings/main_controller_prompt_beta.cpp
@@ -9,7 +9,6 @@ namespace Settings {
constexpr SettingsMessageTree s_modelMenu[] =
{SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren),
SettingsMessageTree(I18n::Message::Brightness),
- SettingsMessageTree(I18n::Message::ResultDisplay, s_modelResultDisplayChildren),
SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren),
SettingsMessageTree(I18n::Message::Language),
SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren),
diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp
index 80c61b2c1..058a61c08 100644
--- a/apps/settings/main_controller_prompt_none.cpp
+++ b/apps/settings/main_controller_prompt_none.cpp
@@ -11,7 +11,6 @@ constexpr SettingsMessageTree s_modelMenu[] =
SettingsMessageTree(I18n::Message::Brightness),
SettingsMessageTree(I18n::Message::Language),
SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren),
- SettingsMessageTree(I18n::Message::ResultDisplay, s_modelResultDisplayChildren),
SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren),
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren),
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)};
diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp
index 77619e6f2..f9b8af791 100644
--- a/apps/settings/main_controller_prompt_update.cpp
+++ b/apps/settings/main_controller_prompt_update.cpp
@@ -9,7 +9,6 @@ using namespace Shared;
constexpr SettingsMessageTree s_modelMenu[] =
{SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren),
SettingsMessageTree(I18n::Message::Brightness),
- SettingsMessageTree(I18n::Message::ResultDisplay, s_modelResultDisplayChildren),
SettingsMessageTree(I18n::Message::FontSizes, s_modelFontChildren),
SettingsMessageTree(I18n::Message::Language),
SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren),
diff --git a/apps/settings/sub_menu/about_controller.cpp b/apps/settings/sub_menu/about_controller.cpp
index 8b0ab99ed..fe64d7453 100644
--- a/apps/settings/sub_menu/about_controller.cpp
+++ b/apps/settings/sub_menu/about_controller.cpp
@@ -25,7 +25,7 @@ AboutController::AboutController(Responder * parentResponder) :
}
bool AboutController::handleEvent(Ion::Events::Event event) {
- I18n::Message childLabel = m_messageTreeModel->children(selectedRow())->label();
+ I18n::Message childLabel = m_messageTreeModel->childAtIndex(selectedRow())->label();
/* We hide here the activation hardware test app: in the menu "about", by
* clicking on '6' on the last row. */
if ((event == Ion::Events::Six || event == Ion::Events::LowerT || event == Ion::Events::UpperT) && childLabel == I18n::Message::FccId) {
@@ -35,7 +35,7 @@ bool AboutController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
if (childLabel == I18n::Message::Contributors) {
GenericSubController * subController = &m_contributorsController;
- subController->setMessageTreeModel(m_messageTreeModel->children(selectedRow()));
+ subController->setMessageTreeModel(m_messageTreeModel->childAtIndex(selectedRow()));
StackViewController * stack = stackController();
stack->push(subController);
return true;
@@ -50,13 +50,13 @@ bool AboutController::handleEvent(Ion::Events::Event event) {
myCell->setAccessoryText(Ion::patchLevel());
return true;
}
- if (childLabel == I18n::Message::CustomSoftwareVersion) {
+ if (childLabel == I18n::Message::OmegaVersion) {
MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)m_selectableTableView.selectedCell();
- if (strcmp(myCell->accessoryText(), Ion::customSoftwareVersion()) == 0) {
- myCell->setAccessoryText("Public"); //Change for public/dev
+ if (strcmp(myCell->accessoryText(), Ion::omegaVersion()) == 0) {
+ myCell->setAccessoryText("Dev"); //Change for public/dev
return true;
}
- myCell->setAccessoryText(Ion::customSoftwareVersion());
+ myCell->setAccessoryText(Ion::omegaVersion());
return true;
}
if (childLabel == I18n::Message::MemUse) {
@@ -121,10 +121,10 @@ int AboutController::reusableCellCount(int type) {
void AboutController::willDisplayCellForIndex(HighlightCell * cell, int index) {
GenericSubController::willDisplayCellForIndex(cell, index);
assert(index >= 0 && index < k_totalNumberOfCell);
- if (m_messageTreeModel->children(index)->label() == I18n::Message::Contributors) {
+ if (m_messageTreeModel->childAtIndex(index)->label() == I18n::Message::Contributors) {
MessageTableCellWithChevronAndMessage * myTextCell = (MessageTableCellWithChevronAndMessage *)cell;
myTextCell->setSubtitle(I18n::Message::Default);
- } else if (m_messageTreeModel->children(index)->label() == I18n::Message::MemUse) {
+ } else if (m_messageTreeModel->childAtIndex(index)->label() == I18n::Message::MemUse) {
char memUseBuffer[15];
int len = Poincare::Integer((int)((float) (Ion::Storage::k_storageSize - Ion::Storage::sharedStorage()->availableSize()) / 1024.f)).serialize(memUseBuffer, 4);
memUseBuffer[len] = 'k';
@@ -146,7 +146,7 @@ void AboutController::willDisplayCellForIndex(HighlightCell * cell, int index) {
Ion::username(),
#endif
Ion::softwareVersion(),
- Ion::customSoftwareVersion(),
+ Ion::omegaVersion(),
mpVersion,
"",
Ion::serialNumber(),
diff --git a/apps/settings/sub_menu/contributors_controller.cpp b/apps/settings/sub_menu/contributors_controller.cpp
index 687ffc412..19fc88895 100644
--- a/apps/settings/sub_menu/contributors_controller.cpp
+++ b/apps/settings/sub_menu/contributors_controller.cpp
@@ -28,11 +28,10 @@ int ContributorsController::reusableCellCount(int type) {
return k_totalNumberOfCell;
}
-constexpr static int s_numberOfDevelopers = 10;
-
+constexpr static int s_numberOfDevelopers = 12;
constexpr static I18n::Message s_developersUsernames[s_numberOfDevelopers] = {
I18n::Message::PQuentinGuidee,
- I18n::Message::PDannySimmons,
+ I18n::Message::PSandraSimmons,
I18n::Message::PJoachimLeFournis,
I18n::Message::PJeanBaptisteBoric,
I18n::Message::PMaximeFriess,
@@ -41,15 +40,18 @@ constexpr static I18n::Message s_developersUsernames[s_numberOfDevelopers] = {
I18n::Message::PEvannDreumont,
I18n::Message::PSzaboLevente,
I18n::Message::PVenceslasDuet,
+ I18n::Message::PCharlotteThomas,
+ I18n::Message::PAntoninLoubiere,
};
-constexpr static int s_numberOfBetaTesters = 4;
-
+constexpr static int s_numberOfBetaTesters = 6;
constexpr static I18n::Message s_betaTestersUsernames[s_numberOfBetaTesters] = {
I18n::Message::PCyprienMejat,
I18n::Message::PTimeoArnouts,
- I18n::Message::PLouisC,
+ I18n::Message::PJulieC,
I18n::Message::PLelahelHideux,
+ I18n::Message::PMadil,
+ I18n::Message::PHilaireLeRoux,
};
void ContributorsController::willDisplayCellForIndex(HighlightCell * cell, int index) {
diff --git a/apps/settings/sub_menu/exam_mode_controller.cpp b/apps/settings/sub_menu/exam_mode_controller.cpp
index e47766266..31a40c36a 100644
--- a/apps/settings/sub_menu/exam_mode_controller.cpp
+++ b/apps/settings/sub_menu/exam_mode_controller.cpp
@@ -32,15 +32,15 @@ ExamModeController::ExamModeController(Responder * parentResponder) :
bool ExamModeController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
- if (m_messageTreeModel->children(selectedRow())->label() == I18n::Message::ExamModeMode) {
- (&m_examModeModeController)->setMessageTreeModel(m_messageTreeModel->children(selectedRow()));
+ if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::ExamModeMode) {
+ (&m_examModeModeController)->setMessageTreeModel(m_messageTreeModel->childAtIndex(selectedRow()));
StackViewController * stack = stackController();
stack->push(&m_examModeModeController);
return true;
}
#if LEDS_CHOICE
- else if (m_messageTreeModel->children(selectedRow())->label() == I18n::Message::LEDColor) {
- (&m_ledController)->setMessageTreeModel(m_messageTreeModel->children(selectedRow()));
+ else if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::LEDColor) {
+ (&m_ledController)->setMessageTreeModel(m_messageTreeModel->childAtIndex(selectedRow()));
StackViewController * stack = stackController();
stack->push(&m_ledController);
return true;
@@ -80,11 +80,11 @@ HighlightCell * ExamModeController::reusableCell(int index, int type) {
assert(type == 0);
assert(index >= 0 && index < 3);
#if LEDS_CHOICE
- if (m_messageTreeModel->children(index)->label() == I18n::Message::LEDColor) {
+ if (m_messageTreeModel->childAtIndex(index)->label() == I18n::Message::LEDColor) {
return &m_ledColorCell;
}
#endif
- if (m_messageTreeModel->children(index)->label() == I18n::Message::ExamModeMode) {
+ if (m_messageTreeModel->childAtIndex(index)->label() == I18n::Message::ExamModeMode) {
return &m_examModeCell;
}
return &m_cell[index];
@@ -101,7 +101,7 @@ void ExamModeController::willDisplayCellForIndex(HighlightCell * cell, int index
}
Preferences * preferences = Preferences::sharedPreferences();
GenericSubController::willDisplayCellForIndex(cell, index);
- I18n::Message thisLabel = m_messageTreeModel->children(index)->label();
+ I18n::Message thisLabel = m_messageTreeModel->childAtIndex(index)->label();
if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode() && (thisLabel == I18n::Message::ActivateExamMode || thisLabel == I18n::Message::ExamModeActive)) {
MessageTableCell * myCell = (MessageTableCell *)cell;
@@ -110,13 +110,13 @@ void ExamModeController::willDisplayCellForIndex(HighlightCell * cell, int index
#if LEDS_CHOICE
if (thisLabel == I18n::Message::LEDColor) {
MessageTableCellWithChevronAndMessage * myTextCell = (MessageTableCellWithChevronAndMessage *)cell;
- I18n::Message message = (I18n::Message) m_messageTreeModel->children(index)->children((int)preferences->colorOfLED())->label();
+ I18n::Message message = (I18n::Message) m_messageTreeModel->childAtIndex(index)->childAtIndex((int)preferences->colorOfLED())->label();
myTextCell->setSubtitle(message);
}
#endif
if (thisLabel == I18n::Message::ExamModeMode) {
MessageTableCellWithChevronAndMessage * myTextCell = (MessageTableCellWithChevronAndMessage *)cell;
- I18n::Message message = (I18n::Message) m_messageTreeModel->children(index)->children((uint8_t)GlobalPreferences::sharedGlobalPreferences()->tempExamMode() - 1)->label();
+ I18n::Message message = (I18n::Message) m_messageTreeModel->childAtIndex(index)->childAtIndex((uint8_t)GlobalPreferences::sharedGlobalPreferences()->tempExamMode() - 1)->label();
myTextCell->setSubtitle(message);
}
}
diff --git a/apps/settings/sub_menu/generic_sub_controller.cpp b/apps/settings/sub_menu/generic_sub_controller.cpp
index 40f658955..5286743d1 100644
--- a/apps/settings/sub_menu/generic_sub_controller.cpp
+++ b/apps/settings/sub_menu/generic_sub_controller.cpp
@@ -76,7 +76,7 @@ int GenericSubController::typeAtLocation(int i, int j) {
void GenericSubController::willDisplayCellForIndex(HighlightCell * cell, int index) {
MessageTableCell * myCell = (MessageTableCell *)cell;
- myCell->setMessage(m_messageTreeModel->children(index)->label());
+ myCell->setMessage(m_messageTreeModel->childAtIndex(index)->label());
}
void GenericSubController::setMessageTreeModel(const MessageTree * messageTreeModel) {
diff --git a/apps/settings/sub_menu/math_options_controller.cpp b/apps/settings/sub_menu/math_options_controller.cpp
index 5b4b42b7b..b1a239506 100644
--- a/apps/settings/sub_menu/math_options_controller.cpp
+++ b/apps/settings/sub_menu/math_options_controller.cpp
@@ -19,11 +19,11 @@ MathOptionsController::MathOptionsController(Responder * parentResponder, InputE
bool MathOptionsController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
GenericSubController * subController = nullptr;
- if (m_messageTreeModel->children(selectedRow())->label() == I18n::Message::DisplayMode)
+ if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::DisplayMode)
subController = &m_displayModeController;
else
subController = &m_preferencesController;
- subController->setMessageTreeModel(m_messageTreeModel->children(selectedRow()));
+ subController->setMessageTreeModel(m_messageTreeModel->childAtIndex(selectedRow()));
StackViewController * stack = stackController();
stack->push(subController);
return true;
@@ -45,7 +45,7 @@ int MathOptionsController::reusableCellCount(int type) {
void MathOptionsController::willDisplayCellForIndex(HighlightCell * cell, int index) {
MessageTableCellWithChevronAndMessage * myTextCell = (MessageTableCellWithChevronAndMessage *)cell;
Preferences * preferences = Preferences::sharedPreferences();
- I18n::Message thisLabel = m_messageTreeModel->children(index)->label();
+ I18n::Message thisLabel = m_messageTreeModel->childAtIndex(index)->label();
myTextCell->setMessage(thisLabel);
//add text for preferences
@@ -64,12 +64,15 @@ void MathOptionsController::willDisplayCellForIndex(HighlightCell * cell, int in
childIndex = (int)preferences->complexFormat();
break;
case I18n::Message::SymbolMultiplication:
- childIndex = (int)preferences->symbolofMultiplication();
+ childIndex = (int)preferences->symbolOfMultiplication();
break;
+ case I18n::Message::SymbolFunction:
+ childIndex = (int)preferences->symbolOfFunction();
+ break;
default:
break;
}
- I18n::Message message = childIndex >= 0 ? m_messageTreeModel->children(index)->children(childIndex)->label() : I18n::Message::Default;
+ I18n::Message message = childIndex >= 0 ? m_messageTreeModel->childAtIndex(index)->childAtIndex(childIndex)->label() : I18n::Message::Default;
myTextCell->setSubtitle(message);
}
diff --git a/apps/settings/sub_menu/math_options_controller.h b/apps/settings/sub_menu/math_options_controller.h
index 1db4c3104..47a59758d 100644
--- a/apps/settings/sub_menu/math_options_controller.h
+++ b/apps/settings/sub_menu/math_options_controller.h
@@ -16,7 +16,7 @@ public:
int reusableCellCount(int type) override;
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
private:
- constexpr static int k_totalNumberOfCell = 5;
+ constexpr static int k_totalNumberOfCell = 7;
MessageTableCellWithChevronAndMessage m_cells[k_totalNumberOfCell];
PreferencesController m_preferencesController;
DisplayModeController m_displayModeController;
diff --git a/apps/settings/sub_menu/preferences_controller.cpp b/apps/settings/sub_menu/preferences_controller.cpp
index c0b4b973d..187782c3c 100644
--- a/apps/settings/sub_menu/preferences_controller.cpp
+++ b/apps/settings/sub_menu/preferences_controller.cpp
@@ -7,13 +7,13 @@
#include
#include
#include
+#include
+#include
using namespace Poincare;
namespace Settings {
-static inline int maxInt(int x, int y) { return x > y ? x : y; }
-
PreferencesController::PreferencesController(Responder * parentResponder) :
GenericSubController(parentResponder)
{
@@ -182,17 +182,20 @@ Layout PreferencesController::layoutForPreferences(I18n::Message message) {
return CodePointLayout::Builder('*', k_layoutFont);
case I18n::Message::SymbolMultiplicationAutoSymbol:
return CodePointLayout::Builder(' ', k_layoutFont);
-
- // Result display
- case I18n::Message::DefaultResult:
+
+
+ // Symbol function
+ case I18n::Message::SymbolDefaultFunction:
{
- const char * text = " ";
- return LayoutHelper::String(text, strlen(text), k_layoutFont);
+ return NthRootLayout::Builder(CodePointLayout::Builder('x'));
}
- case I18n::Message::CompactResult:
+ case I18n::Message::SymbolArgDefaultFunction:
{
- const char * text = "Beta";
- return LayoutHelper::String(text, strlen(text), k_layoutFont);
+ return NthRootLayout::Builder(CodePointLayout::Builder('x'), CodePointLayout::Builder('2'));
+ }
+ case I18n::Message::SymbolArgFunction:
+ {
+ return NthRootLayout::Builder(CodePointLayout::Builder('x'), CodePointLayout::Builder('y'));
}
// Font size
@@ -213,7 +216,7 @@ Layout PreferencesController::layoutForPreferences(I18n::Message message) {
void PreferencesController::willDisplayCellForIndex(HighlightCell * cell, int index) {
GenericSubController::willDisplayCellForIndex(cell, index);
MessageTableCellWithExpression * myCell = (MessageTableCellWithExpression *)cell;
- myCell->setLayout(layoutForPreferences(m_messageTreeModel->children(index)->label()));
+ myCell->setLayout(layoutForPreferences(m_messageTreeModel->childAtIndex(index)->label()));
}
KDCoordinate PreferencesController::rowHeight(int j) {
@@ -233,7 +236,7 @@ void PreferencesController::setPreferenceWithValueIndex(I18n::Message message, i
/* In Engineering mode, the number of significant digits cannot be lower
* than 3, because we need to be able to display 100 for instance. */
// TODO: Add warning about signifiant digits change ?
- preferences->setNumberOfSignificantDigits(maxInt(preferences->numberOfSignificantDigits(), 3));
+ preferences->setNumberOfSignificantDigits(std::max(preferences->numberOfSignificantDigits(), 3));
}
} else if (message == I18n::Message::EditionMode) {
preferences->setEditionMode((Preferences::EditionMode)valueIndex);
@@ -245,12 +248,11 @@ void PreferencesController::setPreferenceWithValueIndex(I18n::Message message, i
GlobalPreferences::sharedGlobalPreferences()->setTempExamMode((GlobalPreferences::ExamMode)((uint8_t)valueIndex + 1));
} else if (message == I18n::Message::SymbolMultiplication) {
preferences->setSymbolMultiplication((Preferences::SymbolMultiplication)valueIndex);
- } else if (message == I18n::Message::ResultDisplay) {
- preferences->setResultDisplay((Preferences::ResultDisplay)valueIndex);
+ } else if (message == I18n::Message::SymbolFunction) {
+ preferences->setSymbolOfFunction((Preferences::SymbolFunction)valueIndex);
} else if (message == I18n::Message::FontSizes) {
GlobalPreferences::sharedGlobalPreferences()->setFont(valueIndex == 0 ? KDFont::LargeFont : KDFont::SmallFont);
}
-
}
int PreferencesController::valueIndexForPreference(I18n::Message message) const {
@@ -273,10 +275,10 @@ int PreferencesController::valueIndexForPreference(I18n::Message message) const
}
#endif
if (message == I18n::Message::SymbolMultiplication) {
- return (int)preferences->symbolofMultiplication();
+ return (int)preferences->symbolOfMultiplication();
}
- if (message == I18n::Message::ResultDisplay) {
- return (int)preferences->resultDisplay();
+ if (message == I18n::Message::SymbolFunction) {
+ return (int)preferences->symbolOfFunction();
}
if (message == I18n::Message::FontSizes) {
return GlobalPreferences::sharedGlobalPreferences()->font() == KDFont::LargeFont ? 0 : 1;
diff --git a/apps/settings/sub_menu/selectable_view_with_messages.cpp b/apps/settings/sub_menu/selectable_view_with_messages.cpp
index 5bef3ecc9..8e64acce4 100644
--- a/apps/settings/sub_menu/selectable_view_with_messages.cpp
+++ b/apps/settings/sub_menu/selectable_view_with_messages.cpp
@@ -1,13 +1,12 @@
#include "selectable_view_with_messages.h"
#include
#include
+#include
using namespace Shared;
namespace Settings {
-static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; }
-
SelectableViewWithMessages::SelectableViewWithMessages(SelectableTableView * selectableTableView) :
m_selectableTableView(selectableTableView),
m_numberOfMessages(0)
@@ -52,7 +51,7 @@ void SelectableViewWithMessages::layoutSubviews(bool force) {
// Layout the text
KDCoordinate textHeight = KDFont::SmallFont->glyphSize().height();
- KDCoordinate defOrigin = maxCoordinate(bounds().height() - Metric::CommonBottomMargin - m_numberOfMessages*textHeight, tableHeight);
+ KDCoordinate defOrigin = std::max(bounds().height() - Metric::CommonBottomMargin - m_numberOfMessages*textHeight, tableHeight);
for (int i = 0; i < m_numberOfMessages; i++) {
m_messageLines[i].setFrame(KDRect(0, defOrigin, bounds().width(), textHeight), force);
diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n
index 353bbb86f..866e69355 100644
--- a/apps/shared.de.i18n
+++ b/apps/shared.de.i18n
@@ -1,13 +1,13 @@
ActivateDeactivate = "Aktivieren/Deaktivieren"
ActivateDutchExamMode = "Activate Dutch exam mode"
-ActivateExamMode = "Starten Testmodus"
+ActivateExamMode = "Prüfungsmodus starten"
ActiveExamModeMessage1 = "Alle Ihre Daten werden "
ActiveExamModeMessage2 = "gelöscht, wenn Sie den "
-ActiveExamModeMessage3 = "Testmodus einschalten."
+ActiveExamModeMessage3 = "Prüfungsmodus einschalten."
ActiveDutchExamModeMessage1 = "All your data will be deleted when"
ActiveDutchExamModeMessage2 = "you activate the exam mode. Python"
ActiveDutchExamModeMessage3 = "application will be unavailable."
-Axis = "Achsen"
+Axis = "Achse"
Cancel = "Abbrechen"
ClearColumn = "Spalte löschen"
ColumnOptions = "Optionen der Spalte"
@@ -20,8 +20,8 @@ Deviation = "Varianz"
DisplayValues = "Werte anzeigen"
Empty = "Leer"
Eng = "tech"
-ExitExamMode1 = "Möchten Sie den Testmodus "
-ExitExamMode2 = "verlassen?"
+ExitExamMode1 = "Möchten Sie den"
+ExitExamMode2 = "Prüfungsmodus verlassen?"
Exponential = "Exponentielle"
FillWithFormula = "Mit einer Formel füllen"
ForbiddenValue = "Verbotener Wert"
@@ -63,7 +63,7 @@ StatTab = "Stats"
StandardDeviation = "Standardabweichung"
Step = "Schrittwert"
StorageMemoryFull1 = "Der Speicher ist voll. Löschen Sie"
-StorageMemoryFull2 = "von Daten und versuchen Sie es erneut."
+StorageMemoryFull2 = "einige Daten und versuchen Sie es erneut."
StoreExpressionNotAllowed = "'store' ist verboten"
SyntaxError = "Syntaxfehler"
Sym = "sym"
@@ -74,7 +74,6 @@ TStart = "T Startwert"
ToZoom = "Zoom: "
Trigonometric = "Trigonometrisch"
UndefinedValue = "Nicht definierter Wert"
-ValueNotReachedByFunction = "Der Wert wird von der Funktion nicht erreicht"
ValuesTab = "Tabelle"
Warning = "Achtung"
XEnd = "X Endwert"
diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n
index bbef03c83..55f57a25c 100644
--- a/apps/shared.en.i18n
+++ b/apps/shared.en.i18n
@@ -74,7 +74,6 @@ TStart = "T start"
ToZoom = "Zoom: "
Trigonometric = "Trigonometrical"
UndefinedValue = "Undefined value"
-ValueNotReachedByFunction = "Value not reached by function"
ValuesTab = "Table"
Warning = "Warning"
XEnd = "X end"
diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n
index 2ac9ff491..8257c8691 100644
--- a/apps/shared.es.i18n
+++ b/apps/shared.es.i18n
@@ -74,7 +74,6 @@ TStart = "T inicio"
ToZoom = "Zoom : "
Trigonometric = "Trigonométrico"
UndefinedValue = "Valor indefinido"
-ValueNotReachedByFunction = "No se alcanza este valor"
ValuesTab = "Tabla"
Warning = "Cuidado"
XEnd = "X fin"
diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n
index f711cbbec..32bb6aa3b 100644
--- a/apps/shared.fr.i18n
+++ b/apps/shared.fr.i18n
@@ -74,7 +74,6 @@ TStart = "T début"
ToZoom = "Zoomer : "
Trigonometric = "Trigonométrique"
UndefinedValue = "Valeur non définie"
-ValueNotReachedByFunction = "Valeur non atteinte par la fonction"
ValuesTab = "Tableau"
Warning = "Attention"
XEnd = "X fin"
diff --git a/apps/shared.hu.i18n b/apps/shared.hu.i18n
index 4862d64f7..da5ec2157 100644
--- a/apps/shared.hu.i18n
+++ b/apps/shared.hu.i18n
@@ -74,7 +74,6 @@ TStart = "T kezdete"
ToZoom = "Zoom:"
Trigonometric = "Trigonometrikus"
UndefinedValue = "Nincs meghatározva érték"
-ValueNotReachedByFunction = "Az értéket a funkció nem érte el"
ValuesTab = "Táblázat"
Warning = "Figyelem"
XEnd = "X vége"
diff --git a/apps/shared.it.i18n b/apps/shared.it.i18n
new file mode 100644
index 000000000..e480eed23
--- /dev/null
+++ b/apps/shared.it.i18n
@@ -0,0 +1,96 @@
+ActivateDeactivate = "Attivare/Disattivare"
+ActivateExamMode = "Attivare modalità d'esame"
+ActivateDutchExamMode = "Activate Dutch exam mode"
+ActiveExamModeMessage1 = "Tutti i vostri dati saranno "
+ActiveExamModeMessage2 = "cancellati se attivate "
+ActiveExamModeMessage3 = "la modalità d'esame."
+ActiveDutchExamModeMessage1 = "All your data will be deleted when"
+ActiveDutchExamModeMessage2 = "you activate the exam mode. Python"
+ActiveDutchExamModeMessage3 = "application will be unavailable."
+Axis = "Assi"
+Cancel = "Annullare"
+ClearColumn = "Cancella la colonna"
+ColumnOptions = "Opzioni colonna"
+CopyColumnInList = "Copia colonna in una lista"
+DataNotSuitable = "I dati non sono adeguati"
+DataTab = "Dati"
+DefaultSetting = "Impostazioni di base"
+Deg = "deg"
+Deviation = "Varianza"
+DisplayValues = "Mostra valori"
+Empty = "Vuoto"
+Eng = "eng"
+ExitExamMode1 = "Volete uscire "
+ExitExamMode2 = "dalla modalità d'esame ?"
+Exponential = "Esponenziale"
+FillWithFormula = "Compilare con una formula"
+ForbiddenValue = "Valore non consentito"
+FunctionColumn = "Colonna 0(0)"
+FunctionOptions = "Opzioni della funzione"
+Goto = "Andare a"
+GraphTab = "Grafico"
+HardwareTestLaunch1 = "Farete il test hardware."
+HardwareTestLaunch2 = "Per uscire dovrete"
+HardwareTestLaunch3 = "premere il tasto reset"
+HardwareTestLaunch4 = "che cancellerà i vostri dati."
+Initialization = "Pre-regolazione"
+IntervalSet = "Imposta l'intervallo"
+Language = "Lingua"
+LowBattery = "Batteria bassa"
+Mean = "Media"
+Move = " Spostare : "
+NameCannotStartWithNumber = "Un nome non può cominciare con un numero"
+NameTaken = "Questo nome è già utilizzato"
+NameTooLong = "Questo nome è troppo lungo"
+Next = "Successivo"
+NEnd = "N finale"
+NoDataToPlot = "Nessun dato da tracciare"
+NoFunctionToDelete = "Nessuna funzione da cancellare"
+NoValueToCompute = "Nessun valore da calcolare"
+NStart = "N iniziale"
+Ok = "Conferma"
+Or = " o "
+Orthonormal = "Ortogonale"
+Plot = "Traccia grafico"
+PoolMemoryFull1 = "La memoria di lavoro è piena."
+PoolMemoryFull2 = "Riprovare."
+Rad = "rad"
+Rename = "Rinominare"
+RoundAbscissa = "Ascisse intere"
+Sci = "sci"
+SquareSum = "Somma dei quadrati"
+StandardDeviation = "Deviazione standard"
+StatTab = "Stats"
+Step = "Passo"
+StorageMemoryFull1 = "La memoria è piena."
+StorageMemoryFull2 = "Cancellate i dati e riprovate."
+StoreExpressionNotAllowed = "'store' non è consentito"
+SyntaxError = "Sintassi errata"
+Sym = "sym"
+TEnd = "T finale"
+ThetaEnd = "θ finale"
+ThetaStart = "θ iniziale"
+TStart = "T iniziale"
+ToZoom = "Ingrandire : "
+Trigonometric = "Trigonometrica"
+UndefinedValue = "Valore non definito"
+ValuesTab = "Tabella"
+Warning = "Attenzione"
+XEnd = "X finale"
+XStart = "X iniziale"
+Zoom = "Zoom"
+Developers = "Developers"
+BetaTesters = "Beta testers"
+LEDColor = "LED color"
+ExamModeMode = "Mode"
+ExamModeModeStandard = "Standard "
+ExamModeModeNoSym = "No sym "
+ExamModeModeNoSymNoText = "No sym no text "
+ExamModeModeDutch = "Dutch "
+ColorRed = "Red "
+ColorWhite = "White "
+ColorBlue = "Blue "
+ColorGreen = "Green "
+ColorYellow = "Yellow "
+ColorPurple = "Purple "
+ColorOrange = "Orange "
diff --git a/apps/shared.nl.i18n b/apps/shared.nl.i18n
new file mode 100644
index 000000000..75d0e5cd7
--- /dev/null
+++ b/apps/shared.nl.i18n
@@ -0,0 +1,96 @@
+ActivateDeactivate = "Zet aan/uit"
+ActivateExamMode = "Internationale examenst."
+ActivateDutchExamMode = "Nederlandse examenstand"
+ActiveExamModeMessage1 = "Al je gegevens worden "
+ActiveExamModeMessage2 = "gewist wanneer je de "
+ActiveExamModeMessage3 = "examenstand activeert."
+ActiveDutchExamModeMessage1 = "Al je gegevens worden gewist wanneer"
+ActiveDutchExamModeMessage2 = "je de examenstand activeert. De Python"
+ActiveDutchExamModeMessage3 = "applicatie wordt uitgeschakelt."
+Axis = "Assen"
+Cancel = "Annuleer"
+ClearColumn = "Wis kolom"
+ColumnOptions = "Kolomopties"
+CopyColumnInList = "Exporteer de kolom naar een lijst"
+DataNotSuitable = "Gegevens niet geschikt"
+DataTab = "Gegevens"
+DefaultSetting = "Standaardinstelling"
+Deg = "deg"
+Deviation = "Variantie"
+DisplayValues = "Waarden weergeven"
+Empty = "Leeg"
+Eng = "eng"
+ExitExamMode1 = "Verlaat de "
+ExitExamMode2 = "examenstand?"
+Exponential = "Exponentieel"
+FillWithFormula = "Vul met een formule"
+ForbiddenValue = "Verboden waarde"
+FunctionColumn = "0(0) kolom"
+FunctionOptions = "Functie-opties"
+Goto = "Ga naar"
+GraphTab = "Grafiek"
+HardwareTestLaunch1 = "Je start de hardware test. "
+HardwareTestLaunch2 = "Aan het eind van de test moet "
+HardwareTestLaunch3 = "je de rekenmachine resetten en "
+HardwareTestLaunch4 = "worden al je gegevens verwijderd."
+Initialization = "Voorgedefinieerd"
+IntervalSet = "Stel het interval in"
+Language = "Taal"
+LowBattery = "Batterij bijna leeg"
+Mean = "Gemiddelde"
+Move = " Verplaats: "
+NameCannotStartWithNumber = "Een naam kan niet beginnen met een nummer"
+NameTaken = "Deze naam is al in gebruik"
+NameTooLong = "Deze naam is te lang"
+Next = "Volgende"
+NoDataToPlot = "Geen gegevens om te plotten"
+NoFunctionToDelete = "Geen functie om te verwijderen"
+NoValueToCompute = "Geen waarden om te berekenen"
+NEnd = "N einde"
+NStart = "N begin"
+Ok = "Bevestig"
+Or = " of "
+Orthonormal = "Orthonormaal"
+Plot = "Grafiek plotten"
+PoolMemoryFull1 = "Het werkgeheugen is vol."
+PoolMemoryFull2 = "Opnieuw proberen."
+Rad = "rad"
+Rename = "Hernoem"
+RoundAbscissa = "Geheel getal"
+Sci = "sci"
+SquareSum = "Som van kwadraten"
+StandardDeviation = "Standaardafwijking"
+StoreExpressionNotAllowed = "'opslaan' is niet toegestaan"
+StatTab = "Stats"
+Step = "Stap"
+StorageMemoryFull1 = "Het geheugen is vol."
+StorageMemoryFull2 = "Wis de gegevens en probeer opnieuw."
+SyntaxError = "Syntax error"
+Sym = "sym"
+TEnd = "T einde"
+ThetaEnd = "θ einde"
+ThetaStart = "θ begin"
+TStart = "T begin"
+ToZoom = "Zoom: "
+Trigonometric = "Goniometrisch"
+UndefinedValue = "Ongedefinieerde waarde"
+ValuesTab = "Tabel"
+Warning = "Waarschuwing"
+XEnd = "X einde"
+XStart = "X begin"
+Zoom = "Zoom"
+Developers = "Developers"
+BetaTesters = "Beta testers"
+LEDColor = "LED color"
+ExamModeMode = "Mode"
+ExamModeModeStandard = "Standard "
+ExamModeModeNoSym = "No sym "
+ExamModeModeNoSymNoText = "No sym no text "
+ExamModeModeDutch = "Dutch "
+ColorRed = "Red "
+ColorWhite = "White "
+ColorBlue = "Blue "
+ColorGreen = "Green "
+ColorYellow = "Yellow "
+ColorPurple = "Purple "
+ColorOrange = "Orange "
diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n
index 7ffa4a8b0..84f6967d4 100644
--- a/apps/shared.pt.i18n
+++ b/apps/shared.pt.i18n
@@ -1,8 +1,8 @@
-ActivateDeactivate = "Activar/Desactivar"
-ActivateExamMode = "Activar o modo de exame"
+ActivateDeactivate = "Ativar/Desativar"
+ActivateExamMode = "Ativar o modo de exame"
ActivateDutchExamMode = "Activate Dutch exam mode"
ActiveExamModeMessage1 = "Todos os seus dados serão "
-ActiveExamModeMessage2 = "apagados se você ligar "
+ActiveExamModeMessage2 = "apagados se ativar "
ActiveExamModeMessage3 = "o modo de exame."
ActiveDutchExamModeMessage1 = "All your data will be deleted when"
ActiveDutchExamModeMessage2 = "you activate the exam mode. Python"
@@ -11,43 +11,43 @@ Axis = "Eixos"
Cancel = "Cancelar"
ClearColumn = "Excluir coluna"
ColumnOptions = "Opções de coluna"
-CopyColumnInList = "Copie a coluna em uma lista"
+CopyColumnInList = "Copiar a coluna para uma lista"
DataNotSuitable = "Dados não adequados"
DataTab = "Dados"
-DefaultSetting = "Configurações basicas"
+DefaultSetting = "Configurações básicas"
Deg = "gra"
-Deviation = "Variancia"
+Deviation = "Variância"
DisplayValues = "Exibir os valores"
-Empty = "Vácuo"
+Empty = "Vazio"
Eng = "eng"
-ExitExamMode1 = "Você quer sair do modo de "
+ExitExamMode1 = "Deseja sair do modo de "
ExitExamMode2 = "exame ?"
Exponential = "Exponencial"
FillWithFormula = "Preencher com uma fórmula"
ForbiddenValue = "Valor proibido"
FunctionColumn = "Coluna 0(0)"
FunctionOptions = "Opções de função"
-Goto = "Ir a"
+Goto = "Ir para"
GraphTab = "Gráfico"
-HardwareTestLaunch1 = "Você vai executar o teste da planta."
-HardwareTestLaunch2 = "Para sair você tem que executar"
-HardwareTestLaunch3 = "uma redefinição, que ira apagar"
-HardwareTestLaunch4 = "seus dados."
+HardwareTestLaunch1 = "Vai executar o teste da planta."
+HardwareTestLaunch2 = "Para sair tem que executar"
+HardwareTestLaunch3 = "uma redefinição, que irá apagar"
+HardwareTestLaunch4 = "os seus dados."
Initialization = "Inicialização"
IntervalSet = "Ajustar o intervalo"
Language = "Idioma"
LowBattery = "Bateria fraca"
Mean = "Média"
Move = " Mover : "
-NameCannotStartWithNumber = "Um nome não pode começar com um número"
-NameTaken = "Este nome é já usado"
+NameCannotStartWithNumber = "O nome não pode começar com um número"
+NameTaken = "Este nome já está a ser usado"
NameTooLong = "Este nome é muito longo"
NEnd = "N fim"
Next = "Seguinte"
-NoDataToPlot = "Não ha dados para desenhar"
+NoDataToPlot = "Não há dados para desenhar"
NoFunctionToDelete = "Sem função para eliminar"
-NoValueToCompute = "Nenhuma quantidade para calcular"
-NStart = "N inicio"
+NoValueToCompute = "Não há dados para calcular"
+NStart = "N início"
Ok = "Confirmar"
Or = " ou "
Orthonormal = "Ortonormado"
@@ -62,23 +62,22 @@ SquareSum = "Soma dos quadrados"
StandardDeviation = "Desvio padrão"
StatTab = "Estat"
Step = "Passo"
-StorageMemoryFull1 = "A memoria esta cheia."
+StorageMemoryFull1 = "A memória esta cheia."
StorageMemoryFull2 = "Apage dados e tente novamente."
StoreExpressionNotAllowed = "'store' não está permitido"
SyntaxError = "Erro de sintaxe"
Sym = "sim"
TEnd = "T fim"
ThetaEnd = "θ fim"
-ThetaStart = "θ inicio"
-TStart = "T inicio"
+ThetaStart = "θ início"
+TStart = "T início"
ToZoom = "Zoom : "
Trigonometric = "Trigonométrico"
UndefinedValue = "Valor indefinido"
-ValueNotReachedByFunction = "O valor não é alcançado pela função"
ValuesTab = "Tabela"
Warning = "Atenção"
XEnd = "X fim"
-XStart = "X inicio"
+XStart = "X início"
Zoom = "Zoom"
Developers = "Desenvolvedores"
BetaTesters = "Testadores beta"
diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n
index 8c7f6b28d..c2226b9d0 100644
--- a/apps/shared.universal.i18n
+++ b/apps/shared.universal.i18n
@@ -14,9 +14,16 @@ UnitDistanceMeterMilliSymbol = "_mm"
UnitDistanceMeterMicroSymbol = "_μm"
UnitDistanceMeterNanoSymbol = "_nm"
UnitDistanceMeterPicoSymbol = "_pm"
+UnitDistanceInchSymbol = "_in"
+UnitDistanceFootSymbol = "_ft"
+UnitDistanceYardSymbol = "_yd"
+UnitDistanceMileSymbol = "_mi"
UnitDistanceAstronomicalUnitSymbol = "_au"
UnitDistanceLightYearSymbol = "_ly"
UnitDistanceParsecSymbol = "_pc"
+UnitMassPoundSymbol = "_lb"
+UnitMassOunceSymbol = "_oz"
+UnitMassTonSymbol = "_ton"
UnitMassGramKiloSymbol = "_kg"
UnitMassGramSymbol = "_g"
UnitMassGramMilliSymbol = "_mg"
@@ -31,6 +38,9 @@ UnitAmountMoleSymbol = "_mol"
UnitAmountMoleMilliSymbol = "_mmol"
UnitAmountMoleMicroSymbol = "_μmol"
UnitLuminousIntensityCandelaSymbol = "_cd"
+UnitLuminousFluxLumenSymbol = "_lm"
+UnitIlluminanceLuxSymbol = "_lx"
+UnitSolidAngleSteradianSymbol = "_sr"
UnitFrequencyHertzGigaSymbol = "_GHz"
UnitFrequencyHertzMegaSymbol = "_MHz"
UnitFrequencyHertzKiloSymbol = "_kHz"
@@ -119,7 +129,7 @@ InvSortCommandWithArg = "sort>(L)"
K = "k"
Lambda = "λ"
LcmCommandWithArg = "lcm(p,q)"
-LinearRegressionFormula = " y=a·x+b "
+LinearRegressionFormula = " y=a·x+b "
LogCommandWithArg = "log(x,a)"
MatrixCommand = "[[\x11]]"
MatrixCommandWithArg = "[[1,2][3,4]]"
@@ -287,46 +297,140 @@ ElementTsMass = "294"
ElementOgMass = "294"
ElementUueMass = "295"
ElementUbnMass = "297"
-QuentinGuidee = "Quentin Guidée "
+QuentinGuidee = "Quentin Guidée"
PQuentinGuidee = "@quentinguidee"
-DannySimmons = "Danny Simmons "
-PDannySimmons = "@MixedMatched"
-JoachimLeFournis = "Joachim Le Fournis "
+SandraSimmons = "Sandra Simmons"
+PSandraSimmons = "@MixedMatched"
+JoachimLeFournis = "Joachim Le Fournis"
PJoachimLeFournis = "@RedGl0w"
-JeanBaptisteBoric = "Jean-Baptiste Boric "
+JeanBaptisteBoric = "Jean-Baptiste Boric"
PJeanBaptisteBoric = "@boricj"
-MaximeFriess = "Maxime Friess "
+MaximeFriess = "Maxime Friess"
PMaximeFriess = "@M4x1m3"
-David = "David "
+David = "David"
PDavid = "@0b101"
-DamienNicolet = "Damien Nicolet "
+DamienNicolet = "Damien Nicolet"
PDamienNicolet = "@zardam"
EvannDreumont = "Evann Dreumont"
PEvannDreumont = "@LeGmask"
SzaboLevente = "Szabó Levente"
PSzaboLevente = "@Gegenter"
+CharlotteThomas = "Charlotte Thomas"
+PCharlotteThomas = "@coco33920"
+AntoninLoubiere = "Antonin L."
+PAntoninLoubiere = "@AntoninLoubiere"
VenceslasDuet = "Venceslas Duet"
PVenceslasDuet = "@Citorva"
CyprienMejat = "Cyprien Méjat"
PCyprienMejat = "@A2drien"
TimeoArnouts = "Timéo Arnouts"
PTimeoArnouts = "@Dogm"
-LouisC = "Louis C"
-PLouisC = "@windows9x95"
+JulieC = "Julie C"
+PJulieC = "@windows9x95"
LelahelHideux = "Lélahel Hideux"
PLelahelHideux = "@Lelahelry"
-SpeedOfLight = "2.998·10^8"
-YearLight = "9.461·10^12"
-Boltzmann = "1.38064852·10^-23"
-Avogadro = "6.02214076·10^23"
-Gas = "8.31446261815324"
-Coulomb = "8.9875517887·10^9"
-Vacuum_permittivity = "8.8541878128·10^-12"
-Vacuum_permeability = "4π·10^-7"
-Planck = "6.62607015·10^-34"
-ElectronMass = "9.109383·10^-31"
-ProtonMass = "1.672649·10^-27"
-NeutronMass = "1.67493·10^-27"
-ElementalCharge = "1.60217662·10^-19"
-GAcceleration = "9.80665"
-GConstant = "6.674·10^-11"
+Madil = "Madil"
+PMadil = "@le-grand-mannitout"
+HilaireLeRoux = "Hilaire Le Roux"
+PHilaireLeRoux = "@0Babass2"
+SpeedOfLight = "2.99792458·10^8_m_s^-1"
+YearLight = "9.461·10^15_m"
+Boltzmann = "1.380649·10^-23_J_K^-1"
+StefanBoltzmann = "5.670374·10^-8_W_m^-2_K^-4"
+VacuumImpedance = "376.730313461_Ω"
+WaterTriplePoint = "273.16_K"
+AtmosphericPressure = "101325_Pa"
+AtomicMassUnit = "1.660539040·10^-27_kg"
+Wien = "2.8977729·10^-3_K_m"
+BohrRadius = "5.2917721067·10^-11_m"
+BohrMagneton = "9.274009994·10^-24_A_m^2"
+NuclearMagneton = "5.050783699·10^-27_A_m^2"
+MuonMass = "1.883531594·10^-28_kg"
+Avogadro = "6.02214076·10^23_mol^-1"
+Gas = "8.31446261815324_J_mol^-1_K^-1"
+Coulomb = "8.9875517887·10^9_N_m^2_C^-2"
+Vacuum_permittivity = "8.8541878128·10^-12_F_m^-1"
+Vacuum_permeability = "4π·10^-7_H_m^-1"
+Planck = "6.62607015·10^-34_J_s"
+ElectronMass = "9.109383·10^-31_kg"
+ProtonMass = "1.672649·10^-27_kg"
+NeutronMass = "1.67493·10^-27_kg"
+ElementalCharge = "1.60217662·10^-19_C"
+GAcceleration = "9.80665_m_s^-2"
+GConstant = "6.674·10^-11_m^3_kg^-1_s^-2"
+SpeedOfSound0 = "343.4_m_s^-1"
+SpeedOfSoundWater = "1480_m_s^-1"
+SpeedOfSoundSteel = "5600_m_s^-1"
+SpeedOfSoundGlass = "5300_m_s^-1"
+SunMass = "1.989·10^30_kg"
+EarthMass = "5.972·10^24_kg"
+MoonMass = "7.342·10^22_kg"
+SunRadius = "696000000_m"
+EarthRadius = "6378140_m"
+MoonRadius = "3474600_m"
+EarthMoonDistance = "384400000_m"
+EarthSunDistance = "149597870000_m"
+FaradayConstant = "96484_C_mol^-1"
+EscapeVelocityOfEarth = "11186_m_s^-1"
+EscapeVelocityOfMoon = "2380_m_s^-1"
+EscapeVelocityOfSun = "42100_m_s^-1"
+UltimateAnswer = "Ultimate Answer"
+UltimateAnswerValue = "42"
+Pka01 = "H20/OH-"
+Pka02 = "HS-/S(2-)"
+Pka03 = "HPO4(2-)/PO4(3-)"
+Pka04 = "HCO3-/CO3(2-)"
+Pka05 = "C6H5OH/C6H5O-"
+Pka06 = "NH4+/NH3"
+Pka07 = "HBrO/BrO-"
+Pka08 = "H2PO4-/HP04(2-)"
+Pka09 = "H2S/HS-"
+Pka10 = "H2C03/HC03-"
+Pka11 = "CH3COOH/CH3COO-"
+Pka12 = "C6H5COOH/C6H5COO-"
+Pka13 = "HN02/NO2-"
+Pka14 = "H3PO4/H2PO4-"
+Pka15 = "HS04-/S04(2-)"
+Pka16 = "H30+/H20"
+Pka01Value = "14"
+Pka02Value = "13.0"
+Pka03Value = "12.7"
+Pka04Value = "10.2"
+Pka05Value = "9.9"
+Pka06Value = "9.2"
+Pka07Value = "8.7"
+Pka08Value = "7.2"
+Pka09Value = "7.0"
+Pka10Value = "6.4"
+Pka11Value = "4.7"
+Pka12Value = "4.2"
+Pka13Value = "3.4"
+Pka14Value = "2.1"
+Pka15Value = "1.9"
+Pka16Value = "0"
+Pka = "pKa"
+PlanckReduce = "1.504571800·10^-34_J_s"
+PlanckMass = "2.176470·10^-8_kg"
+PlanckLength = "1.616229·10^-35_m"
+PlanckTime = "5.39116·10^-44_s"
+PlanckTemperature = "1.416808·10^32_K"
+PlanckCharge = "1.875·10^-18_C"
+PlanckForce = "1.210·10^44_N"
+PlanckEnergy = "1.956·10^9_J"
+PlanckPower = "3.629·10^52_W"
+PlanckDensity = "5.1·10^96_kg_m^-3"
+PlanckQuantityMovement = "6.5_N_s"
+PlanckLinearMass = "1.34664·10^27_kg_m^-1"
+PlanckTension = "1.0432·10^27_V"
+PlanckCurrent = "3.479·10^25_A"
+PlanckPressure = "4.635·10^113_Pa"
+PlanckImpedance = "29.986_Ω"
+TauonMass = "3.16747·10^-27_kg"
+WBosonMass = "1.4334·10^-25_kg"
+ZBosonMass = "1.62556·10^-25_kg"
+FineStructure = "7.2973525664·10^-3"
+RydbergConstant = "1.0973731568508·10^7_m^-1"
+HartreeConstant = "4.359744650·10^-18_J"
+MagneticFluxQuantum = "2.067833831·10^-15_Wb"
+ConductanceQuantum = "7.7480917310·10^-5_S"
+CirculationQuantum = "3.6369475486·10^-4_m^2_s^-1"
\ No newline at end of file
diff --git a/apps/shared/Makefile b/apps/shared/Makefile
index b406d41cb..4ffcd50df 100644
--- a/apps/shared/Makefile
+++ b/apps/shared/Makefile
@@ -14,6 +14,7 @@ app_shared_test_src = $(addprefix apps/shared/,\
labeled_curve_view.cpp \
memoized_curve_view_range.cpp \
range_1D.cpp \
+ toolbox_helpers.cpp \
zoom_and_pan_curve_view_controller.cpp \
zoom_curve_view_controller.cpp \
)
@@ -74,7 +75,6 @@ app_shared_src = $(addprefix apps/shared/,\
text_field_delegate_app.cpp \
text_field_with_extension.cpp \
text_helpers.cpp \
- toolbox_helpers.cpp \
values_controller.cpp \
values_function_parameter_controller.cpp \
values_parameter_controller.cpp \
@@ -84,4 +84,4 @@ app_shared_src = $(addprefix apps/shared/,\
)
app_shared_src += $(app_shared_test_src)
-app_src += $(app_shared_src)
+apps_src += $(app_shared_src)
diff --git a/apps/shared/continuous_function.cpp b/apps/shared/continuous_function.cpp
index fdb4fd2cc..ebac4e160 100644
--- a/apps/shared/continuous_function.cpp
+++ b/apps/shared/continuous_function.cpp
@@ -14,14 +14,12 @@
#include
#include
#include
+#include
using namespace Poincare;
namespace Shared {
-static inline double maxDouble(double x, double y) { return x > y ? x : y; }
-static inline double minDouble(double x, double y) { return x < y ? x : y; }
-
void ContinuousFunction::DefaultName(char buffer[], size_t bufferSize) {
constexpr int k_maxNumberOfDefaultLetterNames = 4;
static constexpr const char k_defaultLetterNames[k_maxNumberOfDefaultLetterNames] = {
@@ -312,14 +310,14 @@ Coordinate2D ContinuousFunction::nextIntersectionFrom(double start, doub
constexpr int bufferSize = CodePoint::MaxCodePointCharLength + 1;
char unknownX[bufferSize];
SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknown);
- double domainMin = maxDouble(tMin(), eDomainMin);
- double domainMax = minDouble(tMax(), eDomainMax);
+ double domainMin = std::max(tMin(), eDomainMin);
+ double domainMax = std::min(tMax(), eDomainMax);
if (step > 0.0f) {
- start = maxDouble(start, domainMin);
- max = minDouble(max, domainMax);
+ start = std::max(start, domainMin);
+ max = std::min(max, domainMax);
} else {
- start = minDouble(start, domainMax);
- max = maxDouble(max, domainMin);
+ start = std::min(start, domainMax);
+ max = std::max(max, domainMin);
}
return PoincareHelpers::NextIntersection(expressionReduced(context), unknownX, start, step, max, context, e);
}
@@ -330,19 +328,19 @@ Coordinate2D ContinuousFunction::nextPointOfInterestFrom(double start, d
char unknownX[bufferSize];
SerializationHelper::CodePoint(unknownX, bufferSize, UCodePointUnknown);
if (step > 0.0f) {
- start = maxDouble(start, tMin());
- max = minDouble(max, tMax());
+ start = std::max(start, tMin());
+ max = std::min(max, tMax());
} else {
- start = minDouble(start, tMax());
- max = maxDouble(max, tMin());
+ start = std::min(start, tMax());
+ max = std::max(max, tMin());
}
return compute(expressionReduced(context), unknownX, start, step, max, context);
}
Poincare::Expression ContinuousFunction::sumBetweenBounds(double start, double end, Poincare::Context * context) const {
assert(plotType() == PlotType::Cartesian);
- start = maxDouble(start, tMin());
- end = minDouble(end, tMax());
+ start = std::max(start, tMin());
+ end = std::min(end, tMax());
return Poincare::Integral::Builder(expressionReduced(context).clone(), Poincare::Symbol::Builder(UCodePointUnknown), Poincare::Float::Builder(start), Poincare::Float::Builder(end)); // Integral takes ownership of args
/* TODO: when we approximate integral, we might want to simplify the integral
* here. However, we might want to do it once for all x (to avoid lagging in
diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp
index bf3451813..4d0e17b12 100644
--- a/apps/shared/curve_view.cpp
+++ b/apps/shared/curve_view.cpp
@@ -4,18 +4,15 @@
#include
#include
#include
+#include
#include
#include
+#include
using namespace Poincare;
namespace Shared {
-static inline int minInt(int x, int y) { return x < y ? x : y; }
-
-static inline float minFloat(float x, float y) { return x < y ? x : y; }
-static inline float maxFloat(float x, float y) { return x > y ? x : y; }
-
CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView,
CursorView * cursorView, View * okView, bool displayBanner) :
View(),
@@ -137,6 +134,22 @@ float CurveView::floatToPixel(Axis axis, float f) const {
}
}
+float CurveView::floatLengthToPixelLength(Axis axis, float f) const {
+ float dist = floatToPixel(axis, f) - floatToPixel(axis, 0.0f);
+ return axis == Axis::Vertical ? - dist : dist;
+}
+
+float CurveView::floatLengthToPixelLength(float dx, float dy) const {
+ float dxPixel = floatLengthToPixelLength(Axis::Horizontal, dx);
+ float dyPixel = floatLengthToPixelLength(Axis::Vertical, dy);
+ return std::sqrt(dxPixel*dxPixel+dyPixel*dyPixel);
+}
+
+float CurveView::pixelLengthToFloatLength(Axis axis, float f) const {
+ f = axis == Axis::Vertical ? -f : f;
+ return pixelToFloat(axis, floatToPixel(axis, 0.0f) + f);
+}
+
void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor boldColor, KDColor lightColor) const {
Axis otherAxis = (axis == Axis::Horizontal) ? Axis::Vertical : Axis::Horizontal;
/* We translate the pixel coordinates into floats, adding/subtracting 1 to
@@ -182,8 +195,8 @@ void CurveView::computeLabels(Axis axis) {
* them from overprinting one another.*/
int labelMaxGlyphLength = labelMaxGlyphLengthSize();
if (axis == Axis::Horizontal) {
- float pixelsPerLabel = maxFloat(0.0f, ((float)Ion::Display::Width)/((float)axisLabelsCount) - k_labelMargin);
- labelMaxGlyphLength = minInt(labelMaxGlyphLengthSize(), pixelsPerLabel/k_font->glyphSize().width());
+ float pixelsPerLabel = std::max(0.0f, ((float)Ion::Display::Width)/((float)axisLabelsCount) - k_labelMargin);
+ labelMaxGlyphLength = std::min(labelMaxGlyphLengthSize(), pixelsPerLabel/k_font->glyphSize().width());
}
if (labelValue < step && labelValue > -step) {
@@ -256,7 +269,7 @@ void CurveView::drawLabel(KDContext * ctx, KDRect rect, float xPosition, float y
KDPoint position = positionLabel(xCoordinate, yCoordinate, labelSize, horizontalPosition, verticalPosition);
if (rect.intersects(KDRect(position, labelSize))) {
// TODO: should we blend?
- ctx->drawString(label, position, k_font, color, KDColorWhite);
+ ctx->drawString(label, position, k_font, color, Palette::BackgroundApps);
}
}
@@ -453,44 +466,66 @@ void CurveView::drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor
}
-void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, KDCoordinate pixelArrowLength, float angle) const {
+void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWidth, float tanAngle) const {
+ assert(tanAngle >= 0.0f);
+ if (std::fabs(dx) < FLT_EPSILON && std::fabs(dy) < FLT_EPSILON) {
+ // We can't draw an arrow without any orientation
+ return;
+ }
+
+ // Translate arrowWidth in pixel length
+ float pixelArrowWidth = 8.0f; // default value in pixels
+ if (arrowWidth > 0.0f) {
+ float dxdyFloat = std::sqrt(dx * dx + dy * dy);
+ float dxArrowFloat = arrowWidth * std::fabs(dy) / dxdyFloat;
+ float dyArrowFloat = arrowWidth * std::fabs(dx) / dxdyFloat;
+ pixelArrowWidth = floatLengthToPixelLength(dxArrowFloat, dyArrowFloat);
+ assert(pixelArrowWidth > 0.0f);
+ }
+
/* Let's call the following variables L and l:
*
- * / |
+ * /arrow2 |
* / |
* / l
* / |
- * / |
- * <--------------------------------------------------
+ * / B |
+ * <---------+----------------------------------------
* \
* \
* \
* \
- * \
+ * \arrow1
*
* ----- L -----
*
- **/
- assert(angle >= 0.0f);
- /* We compute the arrow segments in pixels in order to correctly size the
- * arrow without depending on the displayed range.
- * Warning: the computed values are relative so we need to add/subtract the
- * pixel position of 0s. */
- float x0Pixel = floatToPixel(Axis::Horizontal, 0.0f);
- float y0Pixel = floatToPixel(Axis::Vertical, 0.0f);
- float dxPixel = floatToPixel(Axis::Horizontal, dx) - x0Pixel;
- float dyPixel = y0Pixel - floatToPixel(Axis::Vertical, dy);
- float dx2dy2 = std::sqrt(dxPixel*dxPixel+dyPixel*dyPixel);
- float L = pixelArrowLength;
- float l = angle*L;
+ */
- float arrow1dx = pixelToFloat(Axis::Horizontal, x0Pixel + L*dxPixel/dx2dy2 + l*dyPixel/dx2dy2);
- float arrow1dy = pixelToFloat(Axis::Vertical, y0Pixel - (L*dyPixel/dx2dy2 - l*dxPixel/dx2dy2));
- drawSegment(ctx, rect, x, y, x - arrow1dx, y - arrow1dy, color, false);
+ float lPixel = pixelArrowWidth / 2.0;
+ float LPixel = lPixel / tanAngle;
- float arrow2dx = pixelToFloat(Axis::Horizontal, x0Pixel + L*dxPixel/dx2dy2 - l*dyPixel/dx2dy2);
- float arrow2dy = pixelToFloat(Axis::Vertical, y0Pixel - (L*dyPixel/dx2dy2 + l*dxPixel/dx2dy2));
- drawSegment(ctx, rect, x, y, x - arrow2dx, y - arrow2dy, color, false);
+ float xPixel = floatToPixel(Axis::Horizontal, x);
+ float yPixel = floatToPixel(Axis::Vertical, y);
+
+ // We compute the arrow segments in pixels
+ float dxPixel = floatLengthToPixelLength(Axis::Horizontal, dx);
+ float dyPixel = floatLengthToPixelLength(Axis::Vertical, dy);
+ float dx2dy2Pixel = floatLengthToPixelLength(dx, dy);
+
+ // Point B is the orthogonal projection of the arrow tips on the arrow body
+ float bxPixel = xPixel - LPixel * dxPixel / dx2dy2Pixel;
+ float byPixel = yPixel + LPixel * dyPixel / dx2dy2Pixel;
+
+ float dxArrowPixel = - lPixel * dyPixel / dx2dy2Pixel;
+ float dyArrowPixel = lPixel * dxPixel / dx2dy2Pixel;
+
+ float arrow1xPixel = bxPixel + dxArrowPixel;
+ float arrow1yPixel = byPixel - dyArrowPixel;
+ float arrow2xPixel = bxPixel - dxArrowPixel;
+ float arrow2yPixel = byPixel + dyArrowPixel;
+
+ straightJoinDots(ctx, rect, xPixel, yPixel, arrow1xPixel, arrow1yPixel, color, true);
+ straightJoinDots(ctx, rect, xPixel, yPixel, arrow2xPixel, arrow2yPixel, color, true);
}
void CurveView::drawGrid(KDContext * ctx, KDRect rect) const {
@@ -586,7 +621,7 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd
x = xy.x1();
y = xy.x2();
if (colorUnderCurve && !std::isnan(x) && colorLowerBound < x && x < colorUpperBound && !(std::isnan(y) || std::isinf(y))) {
- drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, x, minFloat(0.0f, y), maxFloat(0.0f, y), color, 1);
+ drawHorizontalOrVerticalSegment(ctx, rect, Axis::Vertical, x, std::min(0.0f, y), std::max(0.0f, y), color, 1);
}
joinDots(ctx, rect, xyEvaluation, model, context, drawStraightLinesEarly, previousT, previousX, previousY, t, x, y, color, thick, k_maxNumberOfIterations);
} while (true);
@@ -595,8 +630,8 @@ void CurveView::drawCurve(KDContext * ctx, KDRect rect, float tStart, float tEnd
void CurveView::drawCartesianCurve(KDContext * ctx, KDRect rect, float xMin, float xMax, EvaluateXYForParameter xyEvaluation, void * model, void * context, KDColor color, bool thick, bool colorUnderCurve, float colorLowerBound, float colorUpperBound) const {
float rectLeft = pixelToFloat(Axis::Horizontal, rect.left() - k_externRectMargin);
float rectRight = pixelToFloat(Axis::Horizontal, rect.right() + k_externRectMargin);
- float tStart = std::isnan(rectLeft) ? xMin : maxFloat(xMin, rectLeft);
- float tEnd = std::isnan(rectRight) ? xMax : minFloat(xMax, rectRight);
+ float tStart = std::isnan(rectLeft) ? xMin : std::max(xMin, rectLeft);
+ float tEnd = std::isnan(rectRight) ? xMax : std::min(xMax, rectRight);
assert(!std::isnan(tStart) && !std::isnan(tEnd));
if (std::isinf(tStart) || std::isinf(tEnd) || tStart > tEnd) {
return;
@@ -697,8 +732,8 @@ static void clipBarycentricCoordinatesBetweenBounds(float & start, float & end,
end = 0;
}
} else {
- start = maxFloat(start, (bounds[(p1f > p2f) ? lower : upper] - p2f)/(p1f-p2f));
- end = minFloat( end , (bounds[(p1f > p2f) ? upper : lower] - p2f)/(p1f-p2f));
+ start = std::max(start, (bounds[(p1f > p2f) ? lower : upper] - p2f)/(p1f-p2f));
+ end = std::min( end , (bounds[(p1f > p2f) ? upper : lower] - p2f)/(p1f-p2f));
}
}
diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h
index 434b2ec1e..18985cb54 100644
--- a/apps/shared/curve_view.h
+++ b/apps/shared/curve_view.h
@@ -57,6 +57,9 @@ protected:
constexpr static int k_externRectMargin = 2;
float pixelToFloat(Axis axis, KDCoordinate p) const;
float floatToPixel(Axis axis, float f) const;
+ float floatLengthToPixelLength(Axis axis, float f) const;
+ float pixelLengthToFloatLength(Axis axis, float f) const;
+ float floatLengthToPixelLength(float dx, float dy) const;
void drawLine(KDContext * ctx, KDRect rect, Axis axis,
float coordinate, KDColor color, KDCoordinate thickness = 1, KDCoordinate dashSize = -1) const {
return drawHorizontalOrVerticalSegment(ctx, rect, axis, coordinate, -INFINITY, INFINITY, color,
@@ -77,29 +80,29 @@ protected:
void drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor color, Size size = Size::Small) const;
/* 'drawArrow' draws the edge of an arrow pointing to (x,y) with the
* orientation (dx,dy).
- * The parameters defining the shape of the arrow are the length in pixel of
- * the projection of the arrow on the segment -'pixelArrowLength'- and the
- * tangent of the angle between the segment and each wing of the arrow called
- * 'angle'.
+ * The parameters defining the shape of the arrow are the length of
+ * the base of the arrow triangle - 'pixelArrowWith' - and the tangent of the
+ * angle between the segment and each wing of the arrow called 'tanAngle'.
*
* / |
* / |
- * / L
- * / |
- * / |
- * <--------------------------------------------------
- * \
- * \
- * \
- * \
- * \
+ * / |
+ * / \ |
+ * / \ angle |
+ * <----------------------------l---------------------
+ * \ |
+ * \ |
+ * \ |
+ * \ |
+ * \ |
*
- * <--- pl --->
+ * <--- L --->
*
- * pl = pixelArrowLength
- * tan(angle) = L/pl
+ * l = pixelArrowWith
+ * tanAngle = tan(angle) = l/2L
*/
- void drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, KDCoordinate pixelArrowLength = 10, float angle = 0.4f) const;
+
+ void drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWidth, float tanAngle = 1.0f/3.0f) const;
void drawGrid(KDContext * ctx, KDRect rect) const;
void drawAxes(KDContext * ctx, KDRect rect) const;
void drawAxis(KDContext * ctx, KDRect rect, Axis axis) const;
diff --git a/apps/shared/editable_cell_table_view_controller.cpp b/apps/shared/editable_cell_table_view_controller.cpp
index 6c00a76f8..48365a3f5 100644
--- a/apps/shared/editable_cell_table_view_controller.cpp
+++ b/apps/shared/editable_cell_table_view_controller.cpp
@@ -3,11 +3,10 @@
#include "../constant.h"
#include
#include
+#include
using namespace Poincare;
-static inline int maxInt(int x, int y) { return x > y ? x : y; }
-
namespace Shared {
EditableCellTableViewController::EditableCellTableViewController(Responder * parentResponder) :
@@ -60,7 +59,7 @@ bool EditableCellTableViewController::textFieldDidFinishEditing(TextField * text
int EditableCellTableViewController::numberOfRows() const {
int numberOfModelElements = 0;
for (int i = 0; i < numberOfColumns(); i++) {
- numberOfModelElements = maxInt(numberOfModelElements, numberOfElementsInColumn(i));
+ numberOfModelElements = std::max(numberOfModelElements, numberOfElementsInColumn(i));
}
return 1 + numberOfModelElements + (numberOfModelElements < maxNumberOfElements());
}
diff --git a/apps/shared/expiring_pointer.h b/apps/shared/expiring_pointer.h
index 7184aab50..4dd7dd06b 100644
--- a/apps/shared/expiring_pointer.h
+++ b/apps/shared/expiring_pointer.h
@@ -11,31 +11,31 @@ class ExpiringPointer {
friend class ExpiringPointer;
public:
ExpiringPointer(T * rawPointer) : m_rawPointer(rawPointer) {
-#if DEBUG
+#ifndef NDEBUG
s_global = rawPointer;
#endif
}
T * pointer() { return m_rawPointer; }
T *operator->() {
-#if DEBUG
+#ifndef NDEBUG
assert(m_rawPointer != nullptr && m_rawPointer == s_global);
#endif
return m_rawPointer;
}
T &operator*() {
-#if DEBUG
+#ifndef NDEBUG
assert(m_rawPointer != nullptr && m_rawPointer == s_global);
#endif
return *m_rawPointer;
}
private:
-#if DEBUG
+#ifndef NDEBUG
static T * s_global;
#endif
T * m_rawPointer;
};
-#if DEBUG
+#ifndef NDEBUG
template
T * ExpiringPointer::s_global = nullptr;
#endif
diff --git a/apps/shared/expression_field_delegate_app.h b/apps/shared/expression_field_delegate_app.h
index 1c73f1c7e..0e1752813 100644
--- a/apps/shared/expression_field_delegate_app.h
+++ b/apps/shared/expression_field_delegate_app.h
@@ -10,7 +10,7 @@ class ExpressionFieldDelegateApp : public TextFieldDelegateApp, public LayoutFie
public:
virtual ~ExpressionFieldDelegateApp() = default;
bool layoutFieldShouldFinishEditing(LayoutField * layoutField, Ion::Events::Event event) override;
- virtual bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
+ bool layoutFieldDidReceiveEvent(LayoutField * layoutField, Ion::Events::Event event) override;
protected:
ExpressionFieldDelegateApp(Snapshot * snapshot, ViewController * rootViewController);
};
diff --git a/apps/shared/expression_model.cpp b/apps/shared/expression_model.cpp
index c67d399b7..307956e02 100644
--- a/apps/shared/expression_model.cpp
+++ b/apps/shared/expression_model.cpp
@@ -7,14 +7,13 @@
#include
#include
#include
+#include