mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
197 lines
8.8 KiB
C++
197 lines
8.8 KiB
C++
#include "list_controller.h"
|
|
#include "../app.h"
|
|
#include <apps/i18n.h>
|
|
#include <assert.h>
|
|
#include <escher/metric.h>
|
|
#include <apps/apps_container.h>
|
|
|
|
using namespace Shared;
|
|
|
|
namespace Graph {
|
|
|
|
ListController::ListController(Responder * parentResponder, ButtonRowController * header, ButtonRowController * footer, InputEventHandlerDelegate * inputEventHandlerDelegate) :
|
|
Shared::FunctionListController(parentResponder, header, footer, I18n::Message::AddFunction),
|
|
m_functionTitleCells{ //TODO find better initialization
|
|
TextFieldFunctionTitleCell(this),
|
|
TextFieldFunctionTitleCell(this),
|
|
TextFieldFunctionTitleCell(this),
|
|
TextFieldFunctionTitleCell(this),
|
|
TextFieldFunctionTitleCell(this),
|
|
},
|
|
m_expressionCells{},
|
|
m_parameterController(this, this, I18n::Message::FunctionColor, I18n::Message::DeleteFunction, inputEventHandlerDelegate)
|
|
{
|
|
for (int i = 0; i < k_maxNumberOfDisplayableRows; i++) {
|
|
m_expressionCells[i].setLeftMargin(k_expressionMargin);
|
|
}
|
|
}
|
|
|
|
const char * ListController::title() {
|
|
return I18n::translate(I18n::Message::FunctionTab);
|
|
}
|
|
|
|
void ListController::renameSelectedFunction() {
|
|
assert(selectedColumn() == 0);
|
|
assert(selectedRow() >= 0 && selectedRow() < numberOfRows()-1); // TODO change if sometimes the addFunction row is not displayed
|
|
|
|
// Increase the size of the name column
|
|
computeTitlesColumnWidth(true);
|
|
selectableTableView()->reloadData();
|
|
|
|
AppsContainer::sharedAppsContainer()->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::AlphaLock);
|
|
TextFieldFunctionTitleCell * selectedTitleCell = (TextFieldFunctionTitleCell *)(selectableTableView()->selectedCell());
|
|
selectedTitleCell->setHorizontalAlignment(1.0f);
|
|
Container::activeApp()->setFirstResponder(selectedTitleCell);
|
|
selectedTitleCell->setEditing(true);
|
|
}
|
|
|
|
bool ListController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
|
|
assert(textField != nullptr);
|
|
// Compute the new name
|
|
size_t textLength = strlen(text);
|
|
size_t argumentLength = UTF8Helper::HasCodePoint(text, UCodePointGreekSmallLetterTheta) ? Function::k_parenthesedThetaArgumentByteLength : Function::k_parenthesedXNTArgumentByteLength;
|
|
constexpr int maxBaseNameSize = Function::k_maxNameWithArgumentSize;
|
|
char baseName[maxBaseNameSize];
|
|
if (textLength <= argumentLength) {
|
|
// The user entered an empty name. Use a default function name.
|
|
ContinuousFunction::DefaultName(baseName, maxBaseNameSize);
|
|
/* We don't need to update the textfield edited text here because we are
|
|
* sure that the default name is compliant. It will thus lead to the end of
|
|
* edition and its content will be reloaded by willDisplayTitleCellAtIndex. */
|
|
} else {
|
|
assert(argumentLength <= textLength + 1);
|
|
strlcpy(baseName, text, textLength - argumentLength + 1);
|
|
}
|
|
|
|
// Delete any variable with the same name
|
|
GlobalContext::DestroyRecordsBaseNamedWithoutExtension(baseName, Ion::Storage::funcExtension);
|
|
|
|
// Set the name
|
|
Function::NameNotCompliantError nameError = Function::NameNotCompliantError::None;
|
|
Ion::Storage::Record::ErrorStatus error = Function::BaseNameCompliant(baseName, &nameError) ? modelStore()->recordAtIndex(m_selectableTableView.selectedRow()).setBaseNameWithExtension(baseName, Ion::Storage::funcExtension) : Ion::Storage::Record::ErrorStatus::NonCompliantName;
|
|
|
|
// Handle any error
|
|
if (error == Ion::Storage::Record::ErrorStatus::None) {
|
|
bool selectTab = false;
|
|
textField->setEditing(false);
|
|
computeTitlesColumnWidth();
|
|
int currentRow = m_selectableTableView.selectedRow();
|
|
if (event == Ion::Events::Down && currentRow < numberOfRows() - 1) {
|
|
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), currentRow + 1);
|
|
} else if (event == Ion::Events::Up) {
|
|
if (currentRow > 0) {
|
|
m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), currentRow - 1);
|
|
} else {
|
|
selectTab = true;
|
|
}
|
|
}
|
|
m_selectableTableView.selectedCell()->setHighlighted(true);
|
|
m_selectableTableView.reloadData();
|
|
Container::activeApp()->setFirstResponder(&m_selectableTableView);
|
|
if (selectTab) {
|
|
m_selectableTableView.parentResponder()->handleEvent(event);
|
|
}
|
|
AppsContainer::sharedAppsContainer()->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
|
|
return true;
|
|
} else if (error == Ion::Storage::Record::ErrorStatus::NameTaken) {
|
|
Container::activeApp()->displayWarning(I18n::Message::NameTaken);
|
|
} else if (error == Ion::Storage::Record::ErrorStatus::NonCompliantName) {
|
|
assert(nameError != Function::NameNotCompliantError::None);
|
|
if (nameError == Function::NameNotCompliantError::CharacterNotAllowed) {
|
|
Container::activeApp()->displayWarning(I18n::Message::AllowedCharactersAZaz09);
|
|
} else if (nameError == Function::NameNotCompliantError::NameCannotStartWithNumber) {
|
|
Container::activeApp()->displayWarning(I18n::Message::NameCannotStartWithNumber);
|
|
} else {
|
|
assert(nameError == Function::NameNotCompliantError::ReservedName);
|
|
Container::activeApp()->displayWarning(I18n::Message::ReservedName);
|
|
}
|
|
} else {
|
|
assert(error == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable);
|
|
Container::activeApp()->displayWarning(I18n::Message::NameTooLong);
|
|
}
|
|
textField->setEditing(true);
|
|
return false;
|
|
}
|
|
|
|
bool ListController::textFieldDidAbortEditing(TextField * textField) {
|
|
assert(textField != nullptr);
|
|
// Put the name column back to normal size
|
|
computeTitlesColumnWidth();
|
|
selectableTableView()->reloadData();
|
|
ExpiringPointer<ContinuousFunction> function = modelStore()->modelForRecord(modelStore()->recordAtIndex(selectedRow()));
|
|
setFunctionNameInTextField(function, textField);
|
|
m_selectableTableView.selectedCell()->setHighlighted(true);
|
|
Container::activeApp()->setFirstResponder(&m_selectableTableView);
|
|
AppsContainer::sharedAppsContainer()->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default);
|
|
return true;
|
|
}
|
|
|
|
bool ListController::textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) {
|
|
return event == Ion::Events::Up || event == Ion::Events::Down || Shared::TextFieldDelegate::textFieldShouldFinishEditing(textField, event);
|
|
}
|
|
|
|
bool ListController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
|
assert(textField != nullptr);
|
|
if (textField->isEditing() && textField->shouldFinishEditing(event)) {
|
|
return false;
|
|
}
|
|
return Shared::TextFieldDelegate::textFieldDidReceiveEvent(textField, event);
|
|
}
|
|
|
|
Shared::ListParameterController * ListController::parameterController() {
|
|
return &m_parameterController;
|
|
}
|
|
|
|
int ListController::maxNumberOfDisplayableRows() {
|
|
return k_maxNumberOfDisplayableRows;
|
|
}
|
|
|
|
FunctionTitleCell * ListController::titleCells(int index) {
|
|
assert(index >= 0 && index < k_maxNumberOfDisplayableRows);
|
|
return &m_functionTitleCells[index];
|
|
}
|
|
|
|
HighlightCell * ListController::expressionCells(int index) {
|
|
assert(index >= 0 && index < k_maxNumberOfDisplayableRows);
|
|
return &m_expressionCells[index];
|
|
}
|
|
|
|
void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) {
|
|
assert(cell != nullptr);
|
|
assert(j >= 0 && j < modelStore()->numberOfModels());
|
|
TextFieldFunctionTitleCell * titleCell = static_cast<TextFieldFunctionTitleCell *>(cell);
|
|
// Update the corresponding expression cell in order to get the baseline
|
|
ExpressionModelListController::willDisplayExpressionCellAtIndex(m_selectableTableView.cellAtLocation(1, j), j);
|
|
titleCell->setBaseline(baseline(j));
|
|
if (!titleCell->isEditing()) {
|
|
// Set name and color if the name is not being edited
|
|
ExpiringPointer<ContinuousFunction> function = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
|
|
setFunctionNameInTextField(function, titleCell->textField());
|
|
KDColor functionNameColor = function->isActive() ? function->color() : Palette::GreyDark;
|
|
titleCell->setColor(functionNameColor);
|
|
}
|
|
}
|
|
|
|
void ListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) {
|
|
assert(cell != nullptr);
|
|
assert(j >= 0 && j < modelStore()->numberOfModels());
|
|
Shared::FunctionListController::willDisplayExpressionCellAtIndex(cell, j);
|
|
FunctionExpressionCell * myCell = (FunctionExpressionCell *)cell;
|
|
ExpiringPointer<ContinuousFunction> f = modelStore()->modelForRecord(modelStore()->recordAtIndex(j));
|
|
KDColor textColor = f->isActive() ? Palette::Text : Palette::GreyDark;
|
|
myCell->setTextColor(textColor);
|
|
}
|
|
|
|
void ListController::setFunctionNameInTextField(ExpiringPointer<ContinuousFunction> function, TextField * textField) {
|
|
assert(textField != nullptr);
|
|
char bufferName[BufferTextView::k_maxNumberOfChar];
|
|
function->nameWithArgument(bufferName, BufferTextView::k_maxNumberOfChar);
|
|
textField->setText(bufferName);
|
|
}
|
|
|
|
ContinuousFunctionStore * ListController::modelStore() {
|
|
return App::app()->functionStore();
|
|
}
|
|
|
|
}
|