[apps] Code: use the FileSystem to store the scripts instead of an

accordion
This commit is contained in:
Émilie Feral
2018-02-26 17:01:48 +01:00
committed by EmilieNumworks
parent c37696133d
commit 19ca0f0640
18 changed files with 149 additions and 540 deletions

View File

@@ -3,7 +3,6 @@ snapshot_headers += apps/code/app.h
app_objs += $(addprefix apps/code/,\
app.o\
accordion.o\
console_controller.o\
console_edit_cell.o\
console_line_cell.o\

View File

@@ -1,227 +0,0 @@
#include "accordion.h"
#include <assert.h>
#include <string.h>
Accordion::Accordion(char * buffer, int bufferSize) :
m_historySize(bufferSize),
m_history(buffer),
m_numberOfBuffers(0),
m_startOfLastEditedBuffer(0)
{
for (int i = 0; i < m_historySize; i ++) {
m_history[i] = k_freeSpaceMarker;
}
}
const char * Accordion::bufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
char * Accordion::editableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int startOfBuffer = startOfBufferAtIndex(index);
return &m_history[startOfBuffer];
}
int Accordion::sizeOfEditableBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int length = 0;
for (int i = startOfBufferAtIndex(index); i < m_historySize-1; i++) {
if (m_history[i] == k_freeSpaceMarker && m_history[i+1] != k_freeSpaceMarker) {
break;
}
length++;
// We do not count one Free Space Marker, in order to always have at a Free
// Space of size at least one.
}
return length;
}
bool Accordion::appendBuffer(const char * buffer) {
cleanFreeSpace();
moveFreeSpaceAtEndOfHistory();
int len = strlen(buffer);
if (len + 1 > freeSpaceSize() - 1) { // We keep at keast one Free char.
return false;
}
m_startOfLastEditedBuffer = startOfFreeSpace();
memcpy(&m_history[m_startOfLastEditedBuffer], buffer, len+1);
m_numberOfBuffers++;
return true;
}
bool Accordion::replaceBufferAtIndex(int index, const char * buffer) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int len = strlen(buffer);
if (len < sizeOfEditableBufferAtIndex(index)) {
int startOfOldBuffer = startOfBufferAtIndex(index);
memcpy(&m_history[startOfOldBuffer], buffer, len+1);
m_startOfLastEditedBuffer = startOfOldBuffer;
return true;
}
return false;
}
void Accordion::deleteBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
moveFreeSpaceAtEndOfBufferAtIndex(index);
int i = startOfBufferAtIndex(index);
while (i < m_historySize && m_history[i] != k_freeSpaceMarker) {
m_history[i] = k_freeSpaceMarker;
i++;
}
m_numberOfBuffers--;
}
void Accordion::deleteLastBuffer() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
deleteBufferAtIndex(m_numberOfBuffers-1);
}
}
void Accordion::deleteAll() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++){
m_history[i] = k_freeSpaceMarker;
}
m_numberOfBuffers = 0;
}
int Accordion::freeSpaceSize() {
cleanFreeSpace();
int sizeOfFreeSpace = 0;
int freeSpaceStart = startOfFreeSpace();
for (int i = freeSpaceStart; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
sizeOfFreeSpace++;
} else {
return sizeOfFreeSpace;
}
}
return sizeOfFreeSpace;
}
int Accordion::startOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int bufferCount = 0;
int startOfBuffer = 0;
while (m_history[startOfBuffer] == k_freeSpaceMarker && startOfBuffer < m_historySize) {
startOfBuffer++;
}
for (int i = startOfBuffer; i < m_historySize; i++) {
if (bufferCount == index) {
while (m_history[i] == k_freeSpaceMarker && i < m_historySize) {
i++;
}
return i;
}
if (m_history[i] == 0) {
bufferCount++;
}
}
assert(false);
return 0;
}
int Accordion::endOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int startOfBuffer = startOfBufferAtIndex(index);
for (int i = startOfBuffer; i < m_historySize; i++) {
if (m_history[i] == 0) {
return i;
}
}
assert(false);
return 0;
}
int Accordion::startOfFreeSpace() {
cleanFreeSpace();
for (int i = 0; i < m_historySize; i++) {
if (m_history[i] == k_freeSpaceMarker) {
return i;
}
}
assert(false);
return 0;
}
void Accordion::cleanFreeSpace() {
if (m_history[m_startOfLastEditedBuffer] == k_freeSpaceMarker) {
return;
}
int indexOfCharToChangeIntoFreeSpaceMarker = m_startOfLastEditedBuffer
+ strlen(&m_history[m_startOfLastEditedBuffer]) + 1;
while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != k_freeSpaceMarker
&& indexOfCharToChangeIntoFreeSpaceMarker < m_historySize)
{
m_history[indexOfCharToChangeIntoFreeSpaceMarker] = k_freeSpaceMarker;
indexOfCharToChangeIntoFreeSpaceMarker++;
}
}
void Accordion::moveFreeSpaceAtPosition(int i) {
assert(i >= 0 && i <= m_historySize);
cleanFreeSpace();
int freeSpaceStart = startOfFreeSpace();
int newFreeSpaceStart = freeSpaceStart;
if (freeSpaceStart != i){
// First, move the chars that would be overriden by the free space.
// The indexes depend on the relative positions of the free space and the
// new destination.
int sizeFreeSpace = freeSpaceSize();
int len = 0;
int src = 0;
int dst = 0;
if (freeSpaceStart > i) {
len = freeSpaceStart - i;
src = i;
dst = i + sizeFreeSpace;
newFreeSpaceStart = i;
} else {
src = freeSpaceStart + sizeFreeSpace;
len = i - src;
dst = freeSpaceStart;
newFreeSpaceStart = i-sizeFreeSpace;
}
memmove(&m_history[dst], &m_history[src], len);
// Then move the free space.
for (int j = newFreeSpaceStart ; j < newFreeSpaceStart+sizeFreeSpace; j++) {
m_history[j] = k_freeSpaceMarker;
}
}
m_startOfLastEditedBuffer = newFreeSpaceStart-1;
while (m_startOfLastEditedBuffer > 0 && m_history[m_startOfLastEditedBuffer-1] != 0 ) {
m_startOfLastEditedBuffer--;
}
}
void Accordion::moveFreeSpaceAtEndOfBufferAtIndex(int index) {
assert(index >= 0 && index < numberOfBuffers());
cleanFreeSpace();
int endOfBuffer = endOfBufferAtIndex(index);
moveFreeSpaceAtPosition(endOfBuffer+1);
}
void Accordion::moveFreeSpaceAtEndOfHistory() {
cleanFreeSpace();
if (m_numberOfBuffers > 0) {
moveFreeSpaceAtEndOfBufferAtIndex(m_numberOfBuffers-1);
}
}

View File

@@ -1,51 +0,0 @@
#ifndef ESCHER_ACCORDION_H
#define ESCHER_ACCORDION_H
/* Accordion sequentially stores null-terminated char buffers. It moves the free
* space at the end of a buffer if it will be edited. */
class Accordion {
public:
Accordion(char * buffer, int bufferSize);
int numberOfBuffers() { return m_numberOfBuffers; }
const char * bufferAtIndex(int index);
char * editableBufferAtIndex(int index);
int sizeOfEditableBufferAtIndex(int index);
/* sizeOfEditableBufferAtIndex appends the free space at the end of the buffer
* and returns the length of the buffer plus the free space, minus one free
* space char that we never edit in order to keep track of the position of the
* last edited buffer. */
bool appendBuffer(const char * buffer);
bool replaceBufferAtIndex(int index, const char * buffer);
void deleteBufferAtIndex(int index);
void deleteLastBuffer();
void deleteAll();
int freeSpaceSize();
private:
static constexpr char k_freeSpaceMarker = 0x01;
int startOfBufferAtIndex(int index);
int endOfBufferAtIndex(int index);
int startOfFreeSpace();
void cleanFreeSpace();
/* When a buffer is edited, there is garbage after the first null char of the
* buffer. cleanFreeSpace() declares the space after this null char as free,
* by marking it with the FreeSpaceMarker. Because we always keep at least one
* Free Space char out of the editable zone, cleanFreeSpace() just needs to
* put FreeSpaceMarkers after the first null char of the last edited Buffer,
* until the first FreeSpaceMarker char.
* WARNING: We have to call cleanFreeSpace() before any operation on m_history,
* otherwise m_history might contain garbage chars. */
void moveFreeSpaceAtPosition(int i);
void moveFreeSpaceAtEndOfBufferAtIndex(int index);
void moveFreeSpaceAtEndOfHistory();
int m_historySize;
char * m_history;
/* The m_history variable sequentially stores null-terminated char buffers.
* It also contains free space, which is marked with the FreeSpaceMarker. By
* construction, there is always at least one free byte, and the free space is
* always continuous. */
int m_numberOfBuffers;
int m_startOfLastEditedBuffer;
};
#endif

View File

@@ -77,7 +77,7 @@ bool ConsoleController::pythonEnvironmentIsLoaded() {
void ConsoleController::autoImport() {
for (int i = 0; i < m_scriptStore->numberOfScripts(); i++) {
autoImportScriptAtIndex(i);
autoImportScript(m_scriptStore->scriptAtIndex(i));
}
}
@@ -341,13 +341,12 @@ void ConsoleController::printText(const char * text, size_t length) {
}
}
void ConsoleController::autoImportScriptAtIndex(int index, bool force) {
void ConsoleController::autoImportScript(Script script, bool force) {
const char * importCommand1 = "from ";
const char * importCommand2 = " import *";
int lenImportCommand1 = strlen(importCommand1);
int lenImportCommand2 = strlen(importCommand2);
Script script = m_scriptStore->scriptAtIndex(index);
if (script.autoImport() || force) {
if (script.importationStatus() || force) {
// Remove the name extension ".py" if there is one.
int scriptOriginalNameLength = strlen(script.name());
char scriptNewName[scriptOriginalNameLength];

View File

@@ -32,7 +32,7 @@ public:
bool pythonEnvironmentIsLoaded();
void autoImport();
void autoImportScriptAtIndex(int index, bool force = false);
void autoImportScript(Script script, bool force = false);
void runAndPrintForCommand(const char * command);
void removeExtensionIfAny(char * name);

View File

@@ -10,18 +10,38 @@ namespace Code {
EditorController::EditorController(MenuController * menuController) :
ViewController(nullptr),
m_textArea(this),
m_areaBuffer(nullptr),
m_script(File()),
m_menuController(menuController)
{
m_textArea.setDelegate(this);
}
void EditorController::setScript(Script script){
m_textArea.setText(script.editableContent(), script.contentBufferSize());
EditorController::~EditorController() {
delete m_areaBuffer;
m_areaBuffer = nullptr;
}
void EditorController::setScript(Script script) {
m_script = script;
const char * scriptBody = m_script.readContent();
size_t scriptBodySize = strlen(scriptBody)+1;
size_t availableScriptSize = scriptBodySize + FileSystem::sharedFileSystem()->availableSize();
assert(m_areaBuffer == nullptr);
m_areaBuffer = new char[availableScriptSize];
strlcpy(m_areaBuffer, scriptBody, scriptBodySize);
m_textArea.setText(m_areaBuffer, availableScriptSize);
}
// TODO: this should be done in textAreaDidFinishEditing maybe??
bool EditorController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::Back) {
stackController()->pop();
File::ErrorStatus err = m_script.writeContent(m_areaBuffer, strlen(m_areaBuffer)+1);
if (err == File::ErrorStatus::NoEnoughSpaceAvailable) {
assert(false); // This should not happen as we set the text area according to the available space in the File System
} else {
stackController()->pop();
}
return true;
}
return false;
@@ -37,10 +57,8 @@ void EditorController::viewWillAppear() {
void EditorController::viewDidDisappear() {
m_menuController->scriptContentEditionDidFinish();
}
bool EditorController::textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) {
return false;
delete[] m_areaBuffer;
m_areaBuffer = nullptr;
}
bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) {
@@ -114,7 +132,6 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events:
return true;
}
}
return false;
}

View File

@@ -12,6 +12,7 @@ class ScriptParameterController;
class EditorController : public ViewController, public TextAreaDelegate {
public:
EditorController(MenuController * menuController);
~EditorController();
void setScript(Script script);
/* ViewController */
@@ -23,7 +24,6 @@ public:
ViewController::DisplayParameter displayParameter() override { return ViewController::DisplayParameter::WantsMaximumSpace; }
/* TextAreaDelegate */
bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) override;
bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) override;
Toolbox * toolboxForTextInput(TextInput * textInput) override;
@@ -31,6 +31,8 @@ private:
static constexpr int k_indentationSpacesNumber = 2;
StackViewController * stackController();
TextArea m_textArea;
char * m_areaBuffer;
Script m_script;
MenuController * m_menuController;
};

View File

@@ -30,7 +30,7 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script
, lockOnConsole
#endif
),
m_scriptParameterController(nullptr, I18n::Message::ScriptOptions, m_scriptStore, this),
m_scriptParameterController(nullptr, I18n::Message::ScriptOptions, this),
m_editorController(this),
m_reloadConsoleWhenBecomingFirstResponder(false),
m_shouldDisplayAddScriptRow(true)
@@ -39,6 +39,7 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script
m_scriptCells[i].setParentResponder(&m_selectableTableView);
m_scriptCells[i].editableTextCell()->textField()->setDelegate(this);
m_scriptCells[i].editableTextCell()->textField()->setDraftTextBuffer(m_draftTextBuffer);
m_scriptCells[i].editableTextCell()->textField()->setTextBufferSize(Script::k_nameSize);
m_scriptCells[i].editableTextCell()->textField()->setAlignment(0.0f, 0.5f);
m_scriptCells[i].editableTextCell()->setMargins(0, 0, 0, Metric::HistoryHorizontalMargin);
}
@@ -122,12 +123,11 @@ void MenuController::renameSelectedScript() {
myCell->editableTextCell()->textField()->setEditing(true);
myCell->editableTextCell()->textField()->setText(previousText);
myCell->editableTextCell()->textField()->setCursorLocation(strlen(previousText) - strlen(ScriptStore::k_scriptExtension));
}
}
void MenuController::deleteScriptAtIndex(int i) {
assert(i >= 0);
assert(i < m_scriptStore->numberOfScripts());
m_scriptStore->deleteScriptAtIndex(i);
void MenuController::deleteScript(Script script) {
assert(!script.isNull());
script.remove();
updateAddScriptRowDisplay();
m_selectableTableView.reloadData();
}
@@ -141,11 +141,11 @@ void MenuController::loadPythonIfNeeded() {
m_consoleController.loadPythonEnvironment(false);
}
void MenuController::openConsoleWithScriptAtIndex(int scriptIndex) {
void MenuController::openConsoleWithScript(Script script) {
reloadConsole();
if (m_consoleController.loadPythonEnvironment(false)) {
stackViewController()->push(&m_consoleController);
m_consoleController.autoImportScriptAtIndex(scriptIndex, true);
m_consoleController.autoImportScript(script, true);
}
m_reloadConsoleWhenBecomingFirstResponder = true;
}
@@ -302,7 +302,8 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
} else {
newName = text;
}
if (m_scriptStore->renameScriptAtIndex(m_selectableTableView.selectedRow(), newName)) {
File::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).rename(newName);
if (error == File::ErrorStatus::None) {
updateAddScriptRowDisplay();
textField->setText(newName);
int currentRow = m_selectableTableView.selectedRow();
@@ -316,9 +317,19 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char
app()->setFirstResponder(&m_selectableTableView);
static_cast<AppsContainer *>(const_cast<Container *>(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
return true;
} else {
assert(error == File::ErrorStatus::NameTaken);
// The name cannot be to long as the text field size was set accordingly
// TODO:
// 2 solutions:
// 1. display a warning with
// app()->displayWarning(I18n::Message::ForbiddenValue);
// But that have to be done in textFieldDidReceiveEvent when textFieldShouldFinishEditing to avoid losing edition
// 2. Modify the name to take another one (add a number for instance).
// This would raise issues about the available space? We can't always add a character...
return false;
}
// TODO: add pop up to explain to the user that the name is too long.
return false;
}
bool MenuController::textFieldDidAbortEditing(TextField * textField, const char * text) {
@@ -326,7 +337,8 @@ bool MenuController::textFieldDidAbortEditing(TextField * textField, const char
// The previous text was an empty name. Use a numbered default script name.
char numberedDefaultName[k_defaultScriptNameMaxSize];
numberedDefaultScriptName(numberedDefaultName);
m_scriptStore->renameScriptAtIndex(m_selectableTableView.selectedRow(), const_cast<const char *>(numberedDefaultName));
File::ErrorStatus error = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).rename(numberedDefaultName);
assert(error == File::ErrorStatus::None);
updateAddScriptRowDisplay();
m_selectableTableView.reloadData();
}
@@ -357,13 +369,13 @@ void MenuController::addScript() {
void MenuController::configureScript() {
assert(m_selectableTableView.selectedRow() >= 0);
assert(m_selectableTableView.selectedRow() < m_scriptStore->numberOfScripts());
m_scriptParameterController.setScript(m_selectableTableView.selectedRow());
m_scriptParameterController.setScript(m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()));
stackViewController()->push(&m_scriptParameterController);
}
void MenuController::editScriptAtIndex(int scriptIndex) {
assert(scriptIndex >=0 && scriptIndex < m_scriptStore->numberOfScripts());
Script script = m_scriptStore->scriptAtIndex(scriptIndex, ScriptStore::EditableZone::Content);
Script script = m_scriptStore->scriptAtIndex(scriptIndex);
m_editorController.setScript(script);
stackViewController()->push(&m_editorController);
}

View File

@@ -22,10 +22,10 @@ public:
ConsoleController * consoleController() { return &m_consoleController; }
StackViewController * stackViewController();
void renameSelectedScript();
void deleteScriptAtIndex(int i);
void deleteScript(Script script);
void reloadConsole();
void loadPythonIfNeeded();
void openConsoleWithScriptAtIndex(int scriptIndex);
void openConsoleWithScript(Script script);
void scriptContentEditionDidFinish();
/* ViewController */
@@ -97,7 +97,7 @@ private:
/* In the initializer list of the MenuController constructor, we initialize
* m_scriptCells by copying k_maxNumberOfDisplayableScriptCells times the
* constructor of an EvenOddEditableTextCell. */
char m_draftTextBuffer[TextField::maxBufferSize()];
char m_draftTextBuffer[Script::k_nameSize];
EvenOddCellWithEllipsis m_scriptParameterCells[k_maxNumberOfDisplayableScriptCells];
Shared::NewFunctionCell m_addNewScriptCell;
EvenOddCell m_emptyCell;

View File

@@ -2,35 +2,36 @@
namespace Code {
Script::Script(const char * marker, const char * name, size_t nameBufferSize, const char * content, size_t contentBufferSize) :
m_marker(marker),
m_name(name),
m_nameBufferSize(nameBufferSize),
m_content(content),
m_contentBufferSize(contentBufferSize)
Script::Script(File f) :
File(f)
{
}
bool Script::isNull() const {
if (m_marker == nullptr) {
assert(m_name == nullptr);
assert(m_nameBufferSize == 0);
assert(m_content == nullptr);
assert(m_contentBufferSize == 0);
return true;
}
return false;
}
bool Script::autoImport() const {
bool Script::importationStatus() const {
assert(!isNull());
assert(m_marker != nullptr);
if (m_marker[0] == AutoImportationMarker) {
return true;
const char * body = read();
return (body[0] == 1);
}
void Script::toggleImportationStatus() {
assert(bodySize() >= 1);
m_body[0] = importationStatus() ? 0 : 1;
}
const char * Script::readContent() const {
const char * body = read();
return body+k_importationStatusSize;
}
File::ErrorStatus Script::writeContent(const char * data, size_t size) {
int deltaSize = (int)size+k_importationStatusSize - (int)bodySize();
if (FileSystem::sharedFileSystem()->moveNextFile(start(), deltaSize)) {
*m_size += deltaSize;
strlcpy(m_body+k_importationStatusSize, data, size);
return ErrorStatus::None;
} else {
return ErrorStatus::NoEnoughSpaceAvailable;
}
assert (m_marker[0] == NoAutoImportationMarker);
return false;
}
}

View File

@@ -5,64 +5,22 @@
namespace Code {
class Script {
/* File : | Total Size | Type | Name | Body |
* Script: | AutoImportationStatus | Content |*/
class Script : public File {
friend class ScriptStore;
public:
Script(const char * marker = nullptr, const char * name = nullptr, size_t nameBufferSize = 0, const char * content = nullptr, size_t contentBufferSize = 0);
bool isNull() const;
bool autoImport() const;
Script(File f);
const char * name() const {
assert(!isNull());
assert(m_name != nullptr);
return m_name;
}
bool importationStatus() const;
void toggleImportationStatus();
char * editableName() {
assert(!isNull());
assert(m_name != nullptr);
return const_cast<char *>(m_name);
}
const char * readContent() const;
ErrorStatus writeContent(const char * data, size_t size);
/* nameBufferSize() might not be equal to strlen(name()): There might be free
* space (chars equal to ScriptStore::FreeSpaceMarker), that is used to edit
* the script name. */
size_t nameBufferSize() const {
assert(!isNull());
return m_nameBufferSize;
}
const char * content() const {
assert(!isNull());
assert(m_content != nullptr);
return m_content;
}
char * editableContent() {
assert(!isNull());
assert(m_content != nullptr);
return const_cast<char *>(m_content);
}
/* contentBufferSize() might not be equal to strlen(name()): There might be
* free space (chars equal to ScriptStore::FreeSpaceMarker), that is used to
* edit the script content. */
size_t contentBufferSize() const {
assert(!isNull());
return m_contentBufferSize;
}
static constexpr int NumberOfStringsPerScript = 3;
static constexpr char AutoImportationMarker = 2;
static constexpr char NoAutoImportationMarker = 3;
static constexpr char DefaultAutoImportationMarker = AutoImportationMarker;
/* We made sure that these chars are not used in ion/include/ion/charset.h,
* nor used in the ScriptStore. */
private:
const char * m_marker;
const char * m_name;
size_t m_nameBufferSize;
const char * m_content;
size_t m_contentBufferSize;
constexpr static size_t k_importationStatusSize = 1;
};
}

View File

@@ -3,7 +3,7 @@
namespace Code {
ScriptParameterController::ScriptParameterController(Responder * parentResponder, I18n::Message title, ScriptStore * scriptStore, MenuController * menuController) :
ScriptParameterController::ScriptParameterController(Responder * parentResponder, I18n::Message title, MenuController * menuController) :
ViewController(parentResponder),
m_pageTitle(title),
m_executeScript(I18n::Message::ExecuteScript),
@@ -12,21 +12,17 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder
m_deleteScript(I18n::Message::DeleteScript),
m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin,
Metric::CommonBottomMargin, Metric::CommonLeftMargin, this),
m_scriptStore(scriptStore),
m_menuController(menuController),
m_autoImport(true),
m_currentScriptIndex(-1)
m_script(File()),
m_menuController(menuController)
{
}
void ScriptParameterController::setScript(int i){
Script script = (m_scriptStore->scriptAtIndex(i, ScriptStore::EditableZone::Content));
m_autoImport = script.autoImport();
m_currentScriptIndex = i;
void ScriptParameterController::setScript(Script script){
m_script = script;
}
void ScriptParameterController::dismissScriptParameterController() {
m_currentScriptIndex = -1;
m_script = Script(File());
stackViewController()->pop();
}
@@ -36,26 +32,25 @@ const char * ScriptParameterController::title() {
bool ScriptParameterController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
int scriptIndex = m_currentScriptIndex;
Script s = m_script;
switch (selectedRow()) {
case 0:
dismissScriptParameterController();
m_menuController->openConsoleWithScriptAtIndex(scriptIndex);
m_menuController->openConsoleWithScript(s);
return true;
case 1:
dismissScriptParameterController();
m_menuController->renameSelectedScript();
return true;
case 2:
m_scriptStore->switchAutoImportAtIndex(scriptIndex);
m_autoImport = !m_autoImport;
m_script.toggleImportationStatus();
m_selectableTableView.reloadData();
m_menuController->reloadConsole();
app()->setFirstResponder(&m_selectableTableView);
return true;
case 3:
dismissScriptParameterController();
m_menuController->deleteScriptAtIndex(scriptIndex);
m_menuController->deleteScript(s);
m_menuController->reloadConsole();
return true;
default:
@@ -86,7 +81,7 @@ HighlightCell * ScriptParameterController::reusableCell(int index) {
void ScriptParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) {
if (cell == &m_autoImportScript) {
SwitchView * switchView = (SwitchView *)m_autoImportScript.accessoryView();
switchView->setState(m_autoImport);
switchView->setState(m_script.importationStatus());
}
}

View File

@@ -11,8 +11,8 @@ class MenuController;
class ScriptParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource {
public:
ScriptParameterController(Responder * parentResponder, I18n::Message title, ScriptStore * scriptStore, MenuController * menuController);
void setScript(int i);
ScriptParameterController(Responder * parentResponder, I18n::Message title, MenuController * menuController);
void setScript(Script script);
void dismissScriptParameterController();
/* ViewController */
@@ -38,10 +38,8 @@ private:
MessageTableCellWithSwitch m_autoImportScript;
MessageTableCell m_deleteScript;
SelectableTableView m_selectableTableView;
ScriptStore * m_scriptStore;
Script m_script;
MenuController * m_menuController;
bool m_autoImport;
int m_currentScriptIndex;
};
}

View File

@@ -12,101 +12,39 @@ namespace Code {
constexpr char ScriptStore::k_scriptExtension[];
constexpr char ScriptStore::k_defaultScriptName[];
ScriptStore::ScriptStore() :
m_accordion(m_scriptData, k_scriptDataSize)
ScriptStore::ScriptStore()
{
addScriptFromTemplate(ScriptTemplate::Factorial());
addScriptFromTemplate(ScriptTemplate::Mandelbrot());
addScriptFromTemplate(ScriptTemplate::Polynomial());
}
const Script ScriptStore::scriptAtIndex(int index, EditableZone zone) {
assert(index >= 0 && index < numberOfScripts());
size_t nameBufferSize = 0;
size_t contentBufferSize = 0;
int accordionIndex;
// Move the Free Space at the end of the correct string.
switch (zone) {
case EditableZone::None:
break;
case EditableZone::Name:
accordionIndex = accordionIndexOfNameOfScriptAtIndex(index);
nameBufferSize = m_accordion.sizeOfEditableBufferAtIndex(accordionIndex);
break;
case EditableZone::Content:
accordionIndex = accordionIndexOfContentOfScriptAtIndex(index);
contentBufferSize = m_accordion.sizeOfEditableBufferAtIndex(accordionIndex);
break;
}
// Compute the positions and lengths of the Script Marker, Name and Content.
const char * marker = m_accordion.bufferAtIndex(accordionIndexOfMarkersOfScriptAtIndex(index));
const char * name = m_accordion.bufferAtIndex(accordionIndexOfNameOfScriptAtIndex(index));
if (nameBufferSize == 0) {
nameBufferSize = strlen(name);
}
const char * content = m_accordion.bufferAtIndex(accordionIndexOfContentOfScriptAtIndex(index));
if (contentBufferSize == 0) {
contentBufferSize = strlen(content);
}
return Script(marker, name, nameBufferSize, content, contentBufferSize);
Script ScriptStore::scriptAtIndex(int index) {
File f = FileSystem::sharedFileSystem()->fileOfTypeAtIndex(File::Type::Script, index);
return Script(f);
}
const Script ScriptStore::scriptNamed(const char * name) {
for (int i = 0; i < numberOfScripts(); i++) {
int accordionIndex = accordionIndexOfNameOfScriptAtIndex(i);
const char * currentScriptName = m_accordion.bufferAtIndex(accordionIndex);
if (strcmp(currentScriptName, name) == 0) {
return scriptAtIndex(i);
}
}
return Script();
Script ScriptStore::scriptNamed(const char * name) {
File f = FileSystem::sharedFileSystem()->getFile(File::Type::Script, name);
return Script(f);
}
int ScriptStore::numberOfScripts() {
return (m_accordion.numberOfBuffers())/Script::NumberOfStringsPerScript;
return FileSystem::sharedFileSystem()->numberOfFileOfType(File::Type::Script);
}
bool ScriptStore::addNewScript() {
return addScriptFromTemplate(ScriptTemplate::Empty());
}
bool ScriptStore::renameScriptAtIndex(int index, const char * newName) {
assert (index >= 0 && index < numberOfScripts());
int accordionIndex = accordionIndexOfNameOfScriptAtIndex(index);
return m_accordion.replaceBufferAtIndex(accordionIndex, newName);
}
void ScriptStore::switchAutoImportAtIndex(int index) {
assert(index >= 0 && index < numberOfScripts());
Script script = scriptAtIndex(index);
bool autoImportation = script.autoImport();
int accordionIndex = accordionIndexOfMarkersOfScriptAtIndex(index);
if (autoImportation) {
const char autoImportationString[2] = {Script::NoAutoImportationMarker, 0};
m_accordion.replaceBufferAtIndex(accordionIndex, autoImportationString);
return;
}
const char autoImportationString[2] = {Script::AutoImportationMarker, 0};
m_accordion.replaceBufferAtIndex(accordionIndex, autoImportationString);
}
void ScriptStore::deleteScriptAtIndex(int index) {
assert (index >= 0 && index < numberOfScripts());
int accordionIndex = accordionIndexOfContentOfScriptAtIndex(index);
// We delete in reverse order because we want the indexes to stay true.
m_accordion.deleteBufferAtIndex(accordionIndex);
m_accordion.deleteBufferAtIndex(accordionIndex-1);
m_accordion.deleteBufferAtIndex(accordionIndex-2);
}
void ScriptStore::deleteAllScripts() {
m_accordion.deleteAll();
for (int i = 0; i < numberOfScripts(); i++) {
scriptAtIndex(i).remove();
}
}
bool ScriptStore::isFull() {
return (numberOfScripts() >= k_maxNumberOfScripts || m_accordion.freeSpaceSize() < k_fullFreeSpaceSizeLimit);
return (numberOfScripts() >= k_maxNumberOfScripts || FileSystem::sharedFileSystem()->availableSize() < k_fullFreeSpaceSizeLimit);
}
void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallback storeFunction, ScanCallback storeVariable) {
@@ -114,7 +52,7 @@ void ScriptStore::scanScriptsForFunctionsAndVariables(void * context, ScanCallba
// Handle lexer or parser errors with nlr.
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
const char * scriptContent = scriptAtIndex(scriptIndex).content();
const char * scriptContent = scriptAtIndex(scriptIndex).readContent();
if (scriptContent == nullptr) {
continue;
}
@@ -196,44 +134,21 @@ const char * ScriptStore::contentOfScript(const char * name) {
if (script.isNull()) {
return nullptr;
}
return script.content();
return script.readContent();
}
bool ScriptStore::addScriptFromTemplate(const ScriptTemplate * scriptTemplate) {
const char autoImportationString[2] = {Script::DefaultAutoImportationMarker, 0};
if (!m_accordion.appendBuffer(autoImportationString)) {
return false;
size_t scriptSize = strlen(scriptTemplate->content())+1;
char * body = new char[scriptSize+Script::k_importationStatusSize];
body[0] = 1;
strlcpy(body+1, scriptTemplate->content(), scriptSize);
bool result = false;
if (FileSystem::sharedFileSystem()->sizeOfFileWithBody(body) <= FileSystem::sharedFileSystem()->availableSize()) {
FileSystem::sharedFileSystem()->addFile(scriptTemplate->name(), File::Type::Script, body);
result = true;
}
if (!m_accordion.appendBuffer(scriptTemplate->name())) {
// Delete the Auto Importation Marker
m_accordion.deleteLastBuffer();
return false;
}
if (copyStaticScriptOnFreeSpace(scriptTemplate)) {
return true;
}
// Delete the Auto Importation Marker and the Name Of the Script
m_accordion.deleteLastBuffer();
m_accordion.deleteLastBuffer();
return false;
}
bool ScriptStore::copyStaticScriptOnFreeSpace(const ScriptTemplate * scriptTemplate) {
return m_accordion.appendBuffer(scriptTemplate->content());
}
int ScriptStore::accordionIndexOfMarkersOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript;
}
int ScriptStore::accordionIndexOfNameOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript + 1;
}
int ScriptStore::accordionIndexOfContentOfScriptAtIndex(int index) const {
return index * Script::NumberOfStringsPerScript + 2;
delete[] body;
return result;
}
const char * ScriptStore::structID(mp_parse_node_struct_t *structNode) {

View File

@@ -1,7 +1,6 @@
#ifndef CODE_SCRIPT_STORE_H
#define CODE_SCRIPT_STORE_H
#include "accordion.h"
#include "script.h"
#include "script_template.h"
#include <python/port/port.h>
@@ -13,24 +12,15 @@ namespace Code {
class ScriptStore : public MicroPython::ScriptProvider {
public:
enum class EditableZone {
None,
Name,
Content
};
static constexpr char k_scriptExtension[] = ".py";
static constexpr char k_defaultScriptName[] = "script.py";
static constexpr int k_maxNumberOfScripts = 8;
ScriptStore();
const Script scriptAtIndex(int index, EditableZone zone = EditableZone::None);
const Script scriptNamed(const char * name);
Script scriptAtIndex(int index);
Script scriptNamed(const char * name);
int numberOfScripts();
bool addNewScript();
bool renameScriptAtIndex(int index, const char * newName);
void switchAutoImportAtIndex(int index);
void deleteScriptAtIndex(int index);
void deleteAllScripts();
bool isFull();
@@ -43,21 +33,13 @@ public:
bool addScriptFromTemplate(const ScriptTemplate * scriptTemplate);
private:
static constexpr int k_fullFreeSpaceSizeLimit = 50;
// If m_accordion's free space has a size smaller than
// If the system file free space has a size smaller than
// k_fullFreeSpaceSizeLimit, we consider the script store as full.
static constexpr size_t k_scriptDataSize = 4096;
static constexpr int k_fullFreeSpaceSizeLimit = File::k_sizeSize+File::k_nameSize+File::k_typeSize+10;
static constexpr size_t k_fileInput2ParseNodeStructKind = 1;
static constexpr size_t k_functionDefinitionParseNodeStructKind = 3;
static constexpr size_t k_expressionStatementParseNodeStructKind = 5;
bool copyStaticScriptOnFreeSpace(const ScriptTemplate * scriptTemplate);
int accordionIndexOfScriptAtIndex(int index) const;
int accordionIndexOfMarkersOfScriptAtIndex(int index) const;
int accordionIndexOfNameOfScriptAtIndex(int index) const;
int accordionIndexOfContentOfScriptAtIndex(int index) const;
const char * structID(mp_parse_node_struct_t *structNode);
char m_scriptData[k_scriptDataSize];
Accordion m_accordion;
};
}

View File

@@ -7,7 +7,6 @@ class TextArea;
class TextAreaDelegate : public TextInputDelegate {
public:
virtual bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) = 0;
virtual bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) = 0;
};

View File

@@ -12,6 +12,7 @@ public:
float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite);
void setDelegate(TextFieldDelegate * delegate) { m_delegate = delegate; }
void setDraftTextBuffer(char * draftTextBuffer);
void setTextBufferSize(size_t size);
bool isEditing() const;
size_t draftTextLength() const;
void setText(const char * text);
@@ -30,6 +31,7 @@ protected:
public:
ContentView(char * textBuffer, char * draftTextBuffer, size_t textBufferSize, KDText::FontSize size, float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor = KDColorWhite);
void setDraftTextBuffer(char * draftTextBuffer);
void setTextBufferSize(size_t size);
void drawRect(KDContext * ctx, KDRect rect) const override;
bool isEditing() const { return m_isEditing; }
const char * text() const override;

View File

@@ -22,6 +22,10 @@ void TextField::ContentView::setDraftTextBuffer(char * draftTextBuffer) {
m_draftTextBuffer = draftTextBuffer;
}
void TextField::ContentView::setTextBufferSize(size_t size) {
m_textBufferSize = size;
}
void TextField::ContentView::drawRect(KDContext * ctx, KDRect rect) const {
KDColor bckCol = m_backgroundColor;
if (m_isEditing) {
@@ -172,6 +176,10 @@ void TextField::setDraftTextBuffer(char * draftTextBuffer) {
m_contentView.setDraftTextBuffer(draftTextBuffer);
}
void TextField::setTextBufferSize(size_t size) {
m_contentView.setTextBufferSize(size);
}
bool TextField::isEditing() const {
return m_contentView.isEditing();
}