diff --git a/apps/code/app.cpp b/apps/code/app.cpp index 8eeeea845..0eafd4871 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -18,7 +18,7 @@ const Image * App::Descriptor::icon() { } App::Snapshot::Snapshot() { - m_scriptStore.addFactorialScript(); + m_scriptStore.addNewScript(ScriptStore::DefaultScript::Factorial); } App * App::Snapshot::unpack(Container * container) { @@ -26,7 +26,7 @@ App * App::Snapshot::unpack(Container * container) { } void App::Snapshot::reset() { - m_scriptStore.deleteAll(); + m_scriptStore.deleteAllScripts(); } App::Descriptor * App::Snapshot::descriptor() { diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index cad867ddb..548894788 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -9,7 +9,7 @@ EditorController::EditorController() : } void EditorController::setScript(Script script){ - m_view.setText(script.editableContent(), script.bufferSize()); + m_view.setText(script.editableContent(), script.contentBufferSize()); } bool EditorController::handleEvent(Ion::Events::Event event) { diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h index d819a4eff..c619c664b 100644 --- a/apps/code/editor_controller.h +++ b/apps/code/editor_controller.h @@ -10,6 +10,8 @@ class EditorController : public ViewController { public: EditorController(); void setScript(Script script); + + /* ViewController */ View * view() override { return &m_view; } bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index b58cff649..e80ad401e 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -89,7 +89,7 @@ void MenuController::renameScriptAtIndex(int i) { } void MenuController::deleteScriptAtIndex(int i) { - m_scriptStore->deleteScript(i); + m_scriptStore->deleteScriptAtIndex(i); m_selectableTableView.reloadData(); } @@ -150,7 +150,7 @@ int MenuController::typeAtLocation(int i, int j) { void MenuController::willDisplayCellForIndex(HighlightCell * cell, int index) { if (index < m_scriptStore->numberOfScripts()) { EvenOddEditableTextCell * myCell = static_cast(cell); - myCell->editableTextCell()->textField()->setText(m_scriptStore->nameOfScript(index)); + myCell->editableTextCell()->textField()->setText(m_scriptStore->scriptAtIndex(index).name()); myCell->setEven(index%2 == 0); } else { assert(index == m_scriptStore->numberOfScripts()); @@ -169,7 +169,7 @@ bool MenuController::textFieldDidReceiveEvent(TextField * textField, Ion::Events } bool MenuController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { - if (m_scriptStore->renameScript(m_selectableTableView.selectedRow(), text)) { + if (m_scriptStore->renameScriptAtIndex(m_selectableTableView.selectedRow(), text)) { int currentRow = m_selectableTableView.selectedRow(); if (event == Ion::Events::Down && currentRow < numberOfRows() - 1) { m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), currentRow + 1); diff --git a/apps/code/script.cpp b/apps/code/script.cpp index d2ed1a662..5f087c244 100644 --- a/apps/code/script.cpp +++ b/apps/code/script.cpp @@ -2,21 +2,35 @@ namespace Code { -Script::Script(char * textBuffer, size_t sizeOfBuffer) : - m_bufferSize(sizeOfBuffer), - m_textBuffer(textBuffer) +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) { } -const char * Script::readOnlyContent() const { - return m_textBuffer; +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; } -char * Script::editableContent() { - return m_textBuffer; -} -size_t Script::bufferSize() const { - return m_bufferSize; +bool Script::autoimport() const { + assert(!isNull()); + assert(m_marker != nullptr); + if (m_marker[0] == AutoImportationMarker) { + return true; + } + assert (m_marker[0] == NoAutoImportationMarker); + return false; } } + diff --git a/apps/code/script.h b/apps/code/script.h index 28d30785b..0fe247661 100644 --- a/apps/code/script.h +++ b/apps/code/script.h @@ -7,13 +7,62 @@ namespace Code { class Script { public: - Script(char * textBuffer = nullptr, size_t sizeOfBuffer = 0); - const char * readOnlyContent() const; - char * editableContent(); - size_t bufferSize() const; + 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; + + const char * name() const { + assert(!isNull()); + assert(m_name != nullptr); + return m_name; + } + + char * editableName() { + assert(!isNull()); + assert(m_name != nullptr); + return const_cast(m_name); + } + + /* 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(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: - size_t m_bufferSize; - char * m_textBuffer; + const char * m_marker; + const char * m_name; + size_t m_nameBufferSize; + const char * m_content; + size_t m_contentBufferSize; }; } diff --git a/apps/code/script_parameter_controller.cpp b/apps/code/script_parameter_controller.cpp index 1e7ae7d80..54d3da581 100644 --- a/apps/code/script_parameter_controller.cpp +++ b/apps/code/script_parameter_controller.cpp @@ -19,7 +19,7 @@ ScriptParameterController::ScriptParameterController(Responder * parentResponder } void ScriptParameterController::setScript(int i){ - m_editorController.setScript(m_scriptStore->editableScript(i)); + m_editorController.setScript(m_scriptStore->scriptAtIndex(i, ScriptStore::EditableZone::Content)); m_currentScriptIndex = i; } diff --git a/apps/code/script_store.cpp b/apps/code/script_store.cpp index fe9aa89b1..bd21dedad 100644 --- a/apps/code/script_store.cpp +++ b/apps/code/script_store.cpp @@ -7,355 +7,138 @@ namespace Code { constexpr char ScriptStore::k_defaultScriptName[]; ScriptStore::ScriptStore() : - m_numberOfScripts(0), - m_lastEditedStringPosition(0) + m_accordion(m_scriptData, k_scriptDataSize) { - for (int i = 0; i= 0 && i < numberOfScripts()); - cleanAndMoveFreeSpaceAfterScriptContent(i); - int beginningOfScriptContent = indexOfScriptContent(i); +const Script ScriptStore::scriptAtIndex(int index, EditableZone zone) { + assert(index >= 0 && index < numberOfScripts()); + size_t nameBufferSize = 0; + size_t contentBufferSize = 0; + int accordionIndex; - // Compute the size of the script, including the free space of m_history - int sizeOfEditableScript = 0; - for (int j=beginningOfScriptContent; j= 0 && i < numberOfScripts()); - cleanFreeSpace(); - int beginningOfScriptContent = indexOfScriptContent(i); - return Script(&m_history[beginningOfScriptContent], strlen(&m_history[beginningOfScriptContent]) + 1); -} - -Script ScriptStore::script(const char * name) { - cleanFreeSpace(); - for (int i=0; i= 0 && i < numberOfScripts()); - cleanAndMoveFreeSpaceAfterScriptName(i); - return &m_history[indexOfScriptName(i)]; -} - -int ScriptStore::sizeOfEditableNameOfScript(int i) { - // Compute the size of the name of the script, including the free space of m_history - int sizeOfEditableNameScript = 0; - for (int j=indexOfScriptName(i); j= 0 && i < numberOfScripts()); - cleanAndMoveFreeSpaceAfterScriptName(i); - if (strlen(newName) <= sizeOfEditableNameOfScript(i)) { - copyName(indexOfScriptName(i), newName); + if (didCopy) { return true; } + // Delete the Auto Importation Marker and the Name Of the Script + m_accordion.deleteLastBuffer(); + m_accordion.deleteLastBuffer(); return false; } -void ScriptStore::deleteScript(int i) { - assert (i >= 0 && i < numberOfScripts()); - cleanAndMoveFreeSpaceAfterScriptContent(i); - int indexOfCharToDelete = indexOfScript(i); - while (m_history[indexOfCharToDelete] != FreeSpaceMarker && indexOfCharToDelete < k_historySize) { - m_history[indexOfCharToDelete] = FreeSpaceMarker; - indexOfCharToDelete++; - } - m_numberOfScripts--; +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::deleteAll() { - for (int i = 0; i= 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(); } const char * ScriptStore::contentOfScript(const char * name) { - int filenameIndex = indexBeginningFilename(name); - const char * filename = &name[filenameIndex]; - return script(filename).readOnlyContent(); + Script script = scriptNamed(name); + if (script.isNull()) { + return nullptr; + } + return script.content(); } -bool ScriptStore::copyName(int position, const char * name) { - if (name) { - int len = strlen(name); - if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - memcpy(&m_history[position], name, len+1); - return true; - } else { - int len = strlen(k_defaultScriptName); - if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - memcpy(&m_history[position], k_defaultScriptName, len+1); - return true; - - } +int ScriptStore::accordionIndexOfMarkersOfScriptAtIndex(int index) const { + return index * Script::NumberOfStringsPerScript; } -int ScriptStore::indexOfScript(int i) const { - assert (i >= 0 && i < numberOfScripts()); - int currentScriptNumber = 0; - int beginningOfScript = 0; - while (m_history[beginningOfScript] == FreeSpaceMarker && beginningOfScript < k_historySize) { - beginningOfScript++; - } - if (i == 0) { - return beginningOfScript; - } - bool goingThroughName = true; - for (int j=beginningOfScript; j= 0 && i < numberOfScripts()); - return indexOfScript(i)+1; -} - - -int ScriptStore::indexOfScriptContent(int i) const { - assert (i >= 0 && i < numberOfScripts()); - int indexOfScriptContent = indexOfScriptName(i); - while (m_history[indexOfScriptContent] != 0 && indexOfScriptContent= 0 && i < numberOfScripts()); - int indexOfPrgm = indexOfScriptContent(i); - int lastIndexOfScript = indexOfPrgm + strlen(&m_history[indexOfPrgm]); - return lastIndexOfScript; -} - -int ScriptStore::indexOfFirstFreeSpaceMarker() const { - for (int i=0; i 0) { - return sizeOfFreeSpace; - } - } - } - return sizeOfFreeSpace; -} - -void ScriptStore::cleanFreeSpace() { - if (m_history[m_lastEditedStringPosition] == FreeSpaceMarker - || m_history[m_lastEditedStringPosition] == AutoImportationMarker - || m_history[m_lastEditedStringPosition] == NoAutoImportationMarker) - { - return; - } - int indexOfCharToChangeIntoFreeSpaceMarker = m_lastEditedStringPosition - + strlen (&m_history[m_lastEditedStringPosition]) + 1; - while (m_history[indexOfCharToChangeIntoFreeSpaceMarker] != FreeSpaceMarker - && indexOfCharToChangeIntoFreeSpaceMarker= 0 && i < numberOfScripts()); - int indexOfFreeSpace = indexOfFirstFreeSpaceMarker(); - int newFreeSpacePosition = lastIndexOfScript(i) + 1; - if (indexOfFreeSpace < newFreeSpacePosition) { - newFreeSpacePosition -= sizeOfFreeSpace(); - } - moveFreeSpaceAtPosition(newFreeSpacePosition); -} - -void ScriptStore::moveFreeSpaceAfterScriptName(int i) { - assert (i >= 0 && i < numberOfScripts()); - int newFreeSpacePosition = indexOfScriptName(i); - while (m_history[newFreeSpacePosition] != 0 && newFreeSpacePosition < k_historySize) { - newFreeSpacePosition++; - } - newFreeSpacePosition++; - moveFreeSpaceAtPosition(newFreeSpacePosition); -} - -void ScriptStore::moveFreeSpaceAtPosition(int i) { - assert (i >= 0 && i < k_historySize); - int indexOfFreeSpace = indexOfFirstFreeSpaceMarker(); - if (indexOfFreeSpace != i){ - - // First, move the chars that would be overriden by the free space. - int freeSpaceSize = sizeOfFreeSpace(); - int len, src, dst; - // The indexes depend on the relative positions of the free space and the - // new destination. - if (indexOfFreeSpace > i) { - len = indexOfFreeSpace - i; - src = i; - dst = i + freeSpaceSize; - } else { - src = indexOfFreeSpace + freeSpaceSize; - len = i + freeSpaceSize - src; - dst = indexOfFreeSpace; - } - memmove(&m_history[dst], &m_history[src], len); - - // Then move the free space. - for (int j = i ; j 0 - && m_history[m_lastEditedStringPosition-1] != 0 - && m_history[m_lastEditedStringPosition-1] != AutoImportationMarker - && m_history[m_lastEditedStringPosition-1] != NoAutoImportationMarker) - { - m_lastEditedStringPosition--; - } -} - -void ScriptStore::cleanAndMoveFreeSpaceAfterScriptContent(int i) { - if (i >= 0 && i= 0 && i sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1); - return true; + return m_accordion.appendBuffer(script); } - bool ScriptStore::copyFactorialScriptOnFreeSpace() { const char script[] = R"(def factorial(n): if n == 0: return 1 else: - return n * factorial(n-1) -)"; + return n * factorial(n-1))"; - int len = strlen(script); - if (len + 1 > sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len+1); - return true; + return m_accordion.appendBuffer(script); } bool ScriptStore::copyEmptyScriptOnFreeSpace() { const char script[] = "\0"; - - int len = 1; - if (len > sizeOfFreeSpace() - 1) { // We keep at keast one free char. - return false; - } - memcpy(&m_history[indexOfFirstFreeSpaceMarker()], script, len); - return true; -} - -int ScriptStore::indexBeginningFilename(const char * path) { - int beginningFilename = strlen(path)-1; - while (beginningFilename > 0 && path[beginningFilename - 1] != '\\') { - beginningFilename--; - } - return beginningFilename; + return m_accordion.appendBuffer(script); } } diff --git a/apps/code/script_store.h b/apps/code/script_store.h index 22d3ec9ef..b08b28dc0 100644 --- a/apps/code/script_store.h +++ b/apps/code/script_store.h @@ -2,74 +2,49 @@ #define CODE_SCRIPT_STORE_H #include "script.h" +#include #include namespace Code { class ScriptStore : public MicroPython::ScriptProvider { public: + enum class EditableZone { + None, + Name, + Content + }; + + enum class DefaultScript { + Empty, + Mandelbrot, + Factorial + }; + ScriptStore(); - Script editableScript(int i); - /* editableScript moves the free space of m_history at the end of the - * ith script. It returns a Script object that points to the beginning of the - * wanted script and has a length taking into account both the script and the - * free space. */ - Script script(int i); - /* script returns a Script object that points to the beginning of the - * ith script and has the length of the script. */ - Script script(const char * name); - /* script(const char * name) looks for a script that has the right name and - * returns it. If there is no such script, it returns an empty Script. */ - char * nameOfScript(int i); - char * editableNameOfScript(int i); - int sizeOfEditableNameOfScript(int i); - int numberOfScripts() const; - bool addNewScript(); - bool addMandelbrotScript(); - bool addFactorialScript(); - bool renameScript(int i, const char * newName); - void deleteScript(int i); - void deleteAll(); + const Script scriptAtIndex(int index, EditableZone zone = EditableZone::None); + const Script scriptNamed(const char * name); + int numberOfScripts(); + bool addNewScript(DefaultScript defaultScript = DefaultScript::Empty); + bool renameScriptAtIndex(int index, const char * newName); + void deleteScriptAtIndex(int index); + void deleteAllScripts(); /* MicroPython::ScriptProvider */ const char * contentOfScript(const char * name) override; private: - static constexpr char FreeSpaceMarker = 0x01; - static constexpr char AutoImportationMarker = 0x02; - static constexpr char NoAutoImportationMarker = 0x03; - /* We made sure that these chars are not used in ion/include/ion/charset.h */ - static constexpr int k_historySize = 1024; static constexpr char k_defaultScriptName[] = ".py"; - bool copyName(int position, const char * name = nullptr); - int indexOfScript(int i) const; - int indexOfScriptName(int i) const; - int indexOfScriptContent(int i) const; - int lastIndexOfScript(int i) const; - int indexOfFirstFreeSpaceMarker() const; - int sizeOfFreeSpace() const; - void cleanFreeSpace(); - void moveFreeSpaceAfterScriptContent(int i); - void moveFreeSpaceAfterScriptName(int i); - void moveFreeSpaceAtPosition(int i); - void cleanAndMoveFreeSpaceAfterScriptContent(int i); - void cleanAndMoveFreeSpaceAfterScriptName(int i); + static constexpr size_t k_scriptDataSize = 1024; + int accordionIndexOfScriptAtIndex(int index) const; + int accordionIndexOfMarkersOfScriptAtIndex(int index) const; + int accordionIndexOfNameOfScriptAtIndex(int index) const; + int accordionIndexOfContentOfScriptAtIndex(int index) const; bool copyMandelbrotScriptOnFreeSpace(); bool copyFactorialScriptOnFreeSpace(); bool copyEmptyScriptOnFreeSpace(); - int indexBeginningFilename(const char * path); - char m_history[k_historySize]; - /* The m_history variable sequentially stores scripts as text buffers. - * Each script is stored as follow: - * - First, a char that says whether the script should be automatically - * imported in the console. - * - Then, the name of the script. - * - Finally, the content of the script. - * The free bytes of m_history contain the FreeSpaceMarker. By construction, - * there is always at least one free byte, and the free space is always - * continuous. */ - int m_numberOfScripts; - int m_lastEditedStringPosition; + char m_scriptData[k_scriptDataSize]; + Accordion m_accordion; }; } diff --git a/escher/Makefile b/escher/Makefile index 4680a2178..22d2f66ce 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -1,6 +1,7 @@ SFLAGS += -Iescher/include objs += $(addprefix escher/src/,\ + accordion.o\ alternate_empty_view_controller.o\ app.o\ buffer_text_view.o\ diff --git a/escher/include/escher.h b/escher/include/escher.h index d76d27856..0dec5ecc3 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -1,6 +1,7 @@ #ifndef ESCHER_H #define ESCHER_H +#include #include #include #include diff --git a/escher/include/escher/accordion.h b/escher/include/escher/accordion.h new file mode 100644 index 000000000..5c6a241b7 --- /dev/null +++ b/escher/include/escher/accordion.h @@ -0,0 +1,51 @@ +#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(); + 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(); +private: + static constexpr char k_freeSpaceMarker = 0x01; + int startOfBufferAtIndex(int index); + int endOfBufferAtIndex(int index); + int startOfFreeSpace(); + int freeSpaceSize(); + 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 diff --git a/escher/include/escher/text_area_delegate.h b/escher/include/escher/text_area_delegate.h index 016813171..86cc16bf5 100644 --- a/escher/include/escher/text_area_delegate.h +++ b/escher/include/escher/text_area_delegate.h @@ -7,7 +7,7 @@ class TextAreaDelegate { public: virtual bool textAreaShouldFinishEditing(TextArea * textArea, Ion::Events::Event event) = 0; virtual bool textAreaDidReceiveEvent(TextArea * textArea, Ion::Events::Event event) = 0; - virtual Toolbox * toolboxForTextArea(TextArea * textFied) = 0; + virtual Toolbox * toolboxForTextArea(TextArea * textArea) = 0; }; #endif diff --git a/escher/src/accordion.cpp b/escher/src/accordion.cpp new file mode 100644 index 000000000..07f71bcf2 --- /dev/null +++ b/escher/src/accordion.cpp @@ -0,0 +1,228 @@ +#include +#include +#include + +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; + } +} + +int Accordion::numberOfBuffers() { + return m_numberOfBuffers; +} + +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 sizeat 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::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; +} + +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; +} + +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(); + 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, src, dst, newFreeSpaceStart; + 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 = i-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); + } +} + diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index f7673af0b..36eb46820 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -323,6 +323,7 @@ TextArea::TextArea(Responder * parentResponder, char * textBuffer, void TextArea::setText(char * textBuffer, size_t textBufferSize) { m_contentView.setText(textBuffer, textBufferSize); + m_contentView.moveCursorGeo(0, 0); } bool TextArea::TextArea::handleEvent(Ion::Events::Event event) {