mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[ion/storage] Added the "ctrl-z" system everywhere
This commit is contained in:
@@ -180,6 +180,7 @@ size_t CalculationStore::deleteOldestCalculation() {
|
||||
|
||||
// Delete all calculations
|
||||
void CalculationStore::deleteAll() {
|
||||
m_trashIndex = -1;
|
||||
m_calculationAreaEnd = m_buffer;
|
||||
m_numberOfCalculations = 0;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ bool MenuController::handleEvent(Ion::Events::Event event) {
|
||||
footer()->setSelectedButton(0);
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::ShiftBack) {
|
||||
Ion::Storage::sharedStorage()->reinsertTrash("py");
|
||||
m_selectableTableView.reloadData();
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Up) {
|
||||
if (footer()->selectedButton() == 0) {
|
||||
footer()->setSelectedButton(-1);
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
bool textFieldDidAbortEditing(TextField * textField) override;
|
||||
bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override;
|
||||
bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override;
|
||||
protected:
|
||||
virtual const char * recordExtension() const override { return Ion::Storage::funcExtension; }
|
||||
private:
|
||||
constexpr static int k_maxNumberOfDisplayableRows = 5;
|
||||
Shared::ListParameterController * parameterController() override;
|
||||
|
||||
@@ -25,6 +25,8 @@ public:
|
||||
Toolbox * toolboxForInputEventHandler(InputEventHandler * handler) override;
|
||||
void selectPreviousNewSequenceCell();
|
||||
void editExpression(int sequenceDefinitionIndex, Ion::Events::Event event);
|
||||
protected:
|
||||
virtual const char * recordExtension() const override { return Ion::Storage::seqExtension; }
|
||||
private:
|
||||
static constexpr KDCoordinate k_expressionCellVerticalMargin = 3;
|
||||
bool editInitialConditionOfSelectedRecordWithText(const char * text, bool firstInitialCondition);
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
* not defined. We thus have to keep both methods. */
|
||||
virtual bool isDefined();
|
||||
virtual bool isEmpty();
|
||||
virtual bool shouldBeClearedBeforeRemove() { return !isEmpty(); }
|
||||
/* tidy is responsible to tidy the whole model whereas tidyExpressionModel
|
||||
* tidies only the members associated with the ExpressionModel. In
|
||||
* ExpressionModel, tidy and tidyExpressionModel trigger the same
|
||||
|
||||
@@ -184,18 +184,19 @@ bool ExpressionModelListController::handleEventOnExpression(Ion::Events::Event e
|
||||
}
|
||||
if (event == Ion::Events::Backspace && !isAddEmptyRow(selectedRow())) {
|
||||
Ion::Storage::Record record = modelStore()->recordAtIndex(modelIndexForRow(selectedRow()));
|
||||
ExpiringPointer<ExpressionModelHandle> model = modelStore()->modelForRecord(record);
|
||||
if (model->shouldBeClearedBeforeRemove()) {
|
||||
reinitSelectedExpression(model);
|
||||
} else {
|
||||
if (removeModelRow(record)) {
|
||||
int newSelectedRow = selectedRow() >= numberOfExpressionRows() ? numberOfExpressionRows()-1 : selectedRow();
|
||||
selectCellAtLocation(selectedColumn(), newSelectedRow);
|
||||
selectableTableView()->reloadData();
|
||||
}
|
||||
ExpiringPointer<ExpressionModelHandle> model = modelStore()->modelForRecord(record);
|
||||
if (removeModelRow(record)) {
|
||||
int newSelectedRow = selectedRow() >= numberOfExpressionRows() ? numberOfExpressionRows()-1 : selectedRow();
|
||||
selectCellAtLocation(selectedColumn(), newSelectedRow);
|
||||
selectableTableView()->reloadData();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::ShiftBack) {
|
||||
Ion::Storage::sharedStorage()->reinsertTrash(recordExtension());
|
||||
selectableTableView()->reloadData();
|
||||
return true;
|
||||
}
|
||||
if ((event.hasText() || event == Ion::Events::XNT || event == Ion::Events::Paste || event == Ion::Events::Toolbox || event == Ion::Events::Var)
|
||||
&& !isAddEmptyRow(selectedRow())) {
|
||||
editExpression(event);
|
||||
|
||||
@@ -11,6 +11,7 @@ class ExpressionModelListController : public ViewController, public SelectableTa
|
||||
public:
|
||||
ExpressionModelListController(Responder * parentResponder, I18n::Message text);
|
||||
protected:
|
||||
virtual const char * recordExtension() const = 0;
|
||||
static constexpr KDCoordinate k_expressionMargin = 5;
|
||||
// SelectableTableViewDelegate
|
||||
void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY, bool withinTemporarySelection) override;
|
||||
|
||||
@@ -200,6 +200,11 @@ bool FunctionListController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::ShiftBack) {
|
||||
Ion::Storage::sharedStorage()->reinsertTrash(recordExtension());
|
||||
selectableTableView()->reloadData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "list_parameter_controller.h"
|
||||
#include "expression_model_list_controller.h"
|
||||
#include <apps/i18n.h>
|
||||
#include <ion/storage.h>
|
||||
|
||||
namespace Shared {
|
||||
|
||||
|
||||
@@ -8,9 +8,6 @@ namespace Solver {
|
||||
class Equation : public Shared::ExpressionModelHandle {
|
||||
public:
|
||||
Equation(Ion::Storage::Record record = Record()) : ExpressionModelHandle(record) {}
|
||||
bool shouldBeClearedBeforeRemove() override {
|
||||
return false;
|
||||
}
|
||||
Poincare::Expression standardForm(Poincare::Context * context, bool replaceFunctionsButNotSymbols, Poincare::ExpressionNode::ReductionTarget reductionTarget) const { return m_model.standardForm(this, context, replaceFunctionsButNotSymbols, reductionTarget); }
|
||||
bool containsIComplex(Poincare::Context * context) const;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "equation_list_view.h"
|
||||
#include "equation_models_parameter_controller.h"
|
||||
#include <apps/i18n.h>
|
||||
#include <ion/storage.h>
|
||||
|
||||
namespace Solver {
|
||||
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
bool layoutFieldDidFinishEditing(LayoutField * layoutField, Poincare::Layout layout, Ion::Events::Event event) override;
|
||||
/* Specific to Solver */
|
||||
void resolveEquations();
|
||||
protected:
|
||||
virtual const char * recordExtension() const override { return Ion::Storage::eqExtension; }
|
||||
private:
|
||||
constexpr static int k_maxNumberOfRows = 5; // Ion::Display::Height / Metric::StoreRowHeight = 4.8;
|
||||
SelectableTableView * selectableTableView() override;
|
||||
|
||||
@@ -125,7 +125,10 @@ public:
|
||||
|
||||
// Used by Python OS module
|
||||
int numberOfRecords();
|
||||
Record recordAtIndex(int index);
|
||||
Record recordAtIndex(int index); // Unlike realRecordAtIndex, this ignore trash
|
||||
|
||||
// Trash
|
||||
void reinsertTrash(const char * extension);
|
||||
|
||||
private:
|
||||
constexpr static uint32_t Magic = 0xEE0BDDBA;
|
||||
@@ -140,6 +143,7 @@ private:
|
||||
Record::Data valueOfRecord(const Record record);
|
||||
Record::ErrorStatus setValueOfRecord(const Record record, Record::Data data);
|
||||
void destroyRecord(const Record record);
|
||||
void realDestroyRecord(const Record record);
|
||||
|
||||
/* Getters on address in buffer */
|
||||
char * pointerOfRecord(const Record record) const;
|
||||
@@ -147,12 +151,18 @@ private:
|
||||
const char * fullNameOfRecordStarting(char * start) const;
|
||||
const void * valueOfRecordStarting(char * start) const;
|
||||
|
||||
/* Trash */
|
||||
void emptyTrash();
|
||||
Storage::Record realRecordAtIndex(int index);
|
||||
int realNumberOfRecords();
|
||||
|
||||
/* Overriders */
|
||||
size_t overrideSizeAtPosition(char * position, record_size_t size);
|
||||
size_t overrideFullNameAtPosition(char * position, const char * fullName);
|
||||
size_t overrideBaseNameWithExtensionAtPosition(char * position, const char * baseName, const char * extension);
|
||||
size_t overrideValueAtPosition(char * position, const void * data, record_size_t size);
|
||||
|
||||
size_t realAvailableSize();
|
||||
bool isFullNameTaken(const char * fullName, const Record * recordToExclude = nullptr);
|
||||
bool isBaseNameWithExtensionTaken(const char * baseName, const char * extension, Record * recordToExclude = nullptr);
|
||||
bool isNameOfRecordTaken(Record r, const Record * recordToExclude);
|
||||
@@ -182,6 +192,7 @@ private:
|
||||
|
||||
uint32_t m_magicHeader;
|
||||
char m_buffer[k_storageSize];
|
||||
Record m_trashRecord;
|
||||
uint32_t m_magicFooter;
|
||||
StorageDelegate * m_delegate;
|
||||
mutable Record m_lastRecordRetrieved;
|
||||
|
||||
@@ -97,6 +97,14 @@ void Storage::log() {
|
||||
#endif
|
||||
|
||||
size_t Storage::availableSize() {
|
||||
if (m_trashRecord != NULL) {
|
||||
return realAvailableSize() + sizeof(record_size_t) + m_trashRecord.value().size;
|
||||
} else {
|
||||
return realAvailableSize();
|
||||
}
|
||||
}
|
||||
|
||||
size_t Storage::realAvailableSize() {
|
||||
/* TODO maybe do: availableSize(char ** endBuffer) to get the endBuffer if it
|
||||
* is needed after calling availableSize */
|
||||
assert(k_storageSize >= (endBuffer() - m_buffer) + sizeof(record_size_t));
|
||||
@@ -106,7 +114,7 @@ size_t Storage::availableSize() {
|
||||
size_t Storage::putAvailableSpaceAtEndOfRecord(Storage::Record r) {
|
||||
char * p = pointerOfRecord(r);
|
||||
size_t previousRecordSize = sizeOfRecordStarting(p);
|
||||
size_t availableStorageSize = availableSize();
|
||||
size_t availableStorageSize = realAvailableSize();
|
||||
char * nextRecord = p + previousRecordSize;
|
||||
memmove(nextRecord + availableStorageSize,
|
||||
nextRecord,
|
||||
@@ -146,6 +154,7 @@ Storage::Record::ErrorStatus Storage::notifyFullnessToDelegate() const {
|
||||
}
|
||||
|
||||
Storage::Record::ErrorStatus Storage::createRecordWithFullName(const char * fullName, const void * data, size_t size) {
|
||||
emptyTrash();
|
||||
size_t recordSize = sizeOfRecordWithFullName(fullName, size);
|
||||
if (recordSize >= k_maxRecordSize || recordSize > availableSize()) {
|
||||
return notifyFullnessToDelegate();
|
||||
@@ -172,6 +181,7 @@ Storage::Record::ErrorStatus Storage::createRecordWithFullName(const char * full
|
||||
}
|
||||
|
||||
Storage::Record::ErrorStatus Storage::createRecordWithExtension(const char * baseName, const char * extension, const void * data, size_t size) {
|
||||
emptyTrash();
|
||||
size_t recordSize = sizeOfRecordWithBaseNameAndExtension(baseName, extension, size);
|
||||
if (recordSize >= k_maxRecordSize || recordSize > availableSize()) {
|
||||
return notifyFullnessToDelegate();
|
||||
@@ -202,7 +212,7 @@ int Storage::numberOfRecordsWithExtension(const char * extension) {
|
||||
size_t extensionLength = strlen(extension);
|
||||
for (char * p : *this) {
|
||||
const char * name = fullNameOfRecordStarting(p);
|
||||
if (FullNameHasExtension(name, extension, extensionLength)) {
|
||||
if (FullNameHasExtension(name, extension, extensionLength) && Record(name) != m_trashRecord) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -210,6 +220,10 @@ int Storage::numberOfRecordsWithExtension(const char * extension) {
|
||||
}
|
||||
|
||||
int Storage::numberOfRecords() {
|
||||
return realNumberOfRecords() - (m_trashRecord == NULL ? 0 : 1);
|
||||
}
|
||||
|
||||
int Storage::realNumberOfRecords() {
|
||||
int count = 0;
|
||||
for (char * p : *this) {
|
||||
const char * name = fullNameOfRecordStarting(p);
|
||||
@@ -218,7 +232,7 @@ int Storage::numberOfRecords() {
|
||||
return count;
|
||||
}
|
||||
|
||||
Storage::Record Storage::recordAtIndex(int index) {
|
||||
Storage::Record Storage::realRecordAtIndex(int index) {
|
||||
int currentIndex = -1;
|
||||
const char * name = nullptr;
|
||||
char * recordAddress = nullptr;
|
||||
@@ -240,6 +254,40 @@ Storage::Record Storage::recordAtIndex(int index) {
|
||||
return Record(name);
|
||||
}
|
||||
|
||||
|
||||
Storage::Record Storage::recordAtIndex(int index) {
|
||||
int currentIndex = -1;
|
||||
const char * name = nullptr;
|
||||
char * recordAddress = nullptr;
|
||||
for (char * p : *this) {
|
||||
const char * currentName = fullNameOfRecordStarting(p);
|
||||
Record r = Record(currentName);
|
||||
if (r == m_trashRecord) {
|
||||
continue;
|
||||
}
|
||||
currentIndex++;
|
||||
if (currentIndex == index) {
|
||||
recordAddress = p;
|
||||
name = currentName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name == nullptr) {
|
||||
return Record();
|
||||
}
|
||||
Record r = Record(name);
|
||||
m_lastRecordRetrieved = r;
|
||||
m_lastRecordRetrievedPointer = recordAddress;
|
||||
return Record(name);
|
||||
}
|
||||
|
||||
void Storage::emptyTrash() {
|
||||
if (m_trashRecord != NULL) {
|
||||
realDestroyRecord(m_trashRecord);
|
||||
m_trashRecord = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int index) {
|
||||
int currentIndex = -1;
|
||||
const char * name = nullptr;
|
||||
@@ -247,13 +295,13 @@ Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int
|
||||
char * recordAddress = nullptr;
|
||||
for (char * p : *this) {
|
||||
const char * currentName = fullNameOfRecordStarting(p);
|
||||
if (FullNameHasExtension(currentName, extension, extensionLength)) {
|
||||
if (FullNameHasExtension(currentName, extension, extensionLength) && Record(currentName) != m_trashRecord) {
|
||||
currentIndex++;
|
||||
}
|
||||
if (currentIndex == index) {
|
||||
recordAddress = p;
|
||||
name = currentName;
|
||||
break;
|
||||
if (currentIndex == index) {
|
||||
recordAddress = p;
|
||||
name = currentName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name == nullptr) {
|
||||
@@ -328,7 +376,8 @@ Storage::Storage() :
|
||||
m_magicFooter(Magic),
|
||||
m_delegate(nullptr),
|
||||
m_lastRecordRetrieved(nullptr),
|
||||
m_lastRecordRetrievedPointer(nullptr)
|
||||
m_lastRecordRetrievedPointer(nullptr),
|
||||
m_trashRecord(NULL)
|
||||
{
|
||||
assert(m_magicHeader == Magic);
|
||||
assert(m_magicFooter == Magic);
|
||||
@@ -430,7 +479,24 @@ Storage::Record::ErrorStatus Storage::setValueOfRecord(Record record, Record::Da
|
||||
return Record::ErrorStatus::RecordDoesNotExist;
|
||||
}
|
||||
|
||||
|
||||
void Storage::reinsertTrash(const char * extension) {
|
||||
if (m_trashRecord != NULL) {
|
||||
char * p = pointerOfRecord(m_trashRecord);
|
||||
const char * fullName = fullNameOfRecordStarting(p);
|
||||
if (FullNameHasExtension(fullName, extension, strlen(extension))) {
|
||||
m_trashRecord = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Storage::destroyRecord(Record record) {
|
||||
emptyTrash();
|
||||
m_trashRecord = record;
|
||||
}
|
||||
|
||||
void Storage::realDestroyRecord(Record record) {
|
||||
if (record.isNull()) {
|
||||
return;
|
||||
}
|
||||
@@ -520,7 +586,7 @@ bool Storage::isNameOfRecordTaken(Record r, const Record * recordToExclude) {
|
||||
if (recordToExclude && s == *recordToExclude) {
|
||||
continue;
|
||||
}
|
||||
if (s == r) {
|
||||
if (s == r && s != m_trashRecord) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -576,8 +642,11 @@ size_t Storage::sizeOfRecordWithFullName(const char * fullName, size_t dataSize)
|
||||
}
|
||||
|
||||
bool Storage::slideBuffer(char * position, int delta) {
|
||||
if (delta > (int)availableSize()) {
|
||||
return false;
|
||||
if (delta > (int)realAvailableSize()) {
|
||||
emptyTrash();
|
||||
if (delta > (int)realAvailableSize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
memmove(position+delta, position, endBuffer()+sizeof(record_size_t)-position);
|
||||
return true;
|
||||
@@ -587,7 +656,7 @@ Storage::Record Storage::privateRecordAndExtensionOfRecordBaseNamedWithExtension
|
||||
size_t nameLength = baseNameLength < 0 ? strlen(baseName) : baseNameLength;
|
||||
{
|
||||
const char * lastRetrievedRecordFullName = fullNameOfRecordStarting(m_lastRecordRetrievedPointer);
|
||||
if (m_lastRecordRetrievedPointer != nullptr && strncmp(baseName, lastRetrievedRecordFullName, nameLength) == 0) {
|
||||
if (m_lastRecordRetrievedPointer != nullptr && strncmp(baseName, lastRetrievedRecordFullName, nameLength) == 0 && Record(lastRetrievedRecordFullName) == m_trashRecord) {
|
||||
for (size_t i = 0; i < numberOfExtensions; i++) {
|
||||
if (strcmp(lastRetrievedRecordFullName+nameLength+1 /*+1 to pass the dot*/, extensions[i]) == 0) {
|
||||
assert(UTF8Helper::CodePointIs(lastRetrievedRecordFullName + nameLength, '.'));
|
||||
@@ -602,6 +671,9 @@ Storage::Record Storage::privateRecordAndExtensionOfRecordBaseNamedWithExtension
|
||||
for (char * p : *this) {
|
||||
const char * currentName = fullNameOfRecordStarting(p);
|
||||
if (strncmp(baseName, currentName, nameLength) == 0) {
|
||||
if (Record(currentName) == m_trashRecord) {
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 0; i < numberOfExtensions; i++) {
|
||||
if (strcmp(currentName+nameLength+1 /*+1 to pass the dot*/, extensions[i]) == 0) {
|
||||
assert(UTF8Helper::CodePointIs(currentName + nameLength, '.'));
|
||||
|
||||
Reference in New Issue
Block a user