Files
Upsilon/apps/calculation/history_controller.cpp

260 lines
11 KiB
C++

#include "history_controller.h"
#include "app.h"
#include <poincare/exception_checkpoint.h>
#include <assert.h>
using namespace Shared;
using namespace Poincare;
namespace Calculation {
HistoryController::HistoryController(EditExpressionController * editExpressionController, CalculationStore * calculationStore) :
ViewController(editExpressionController),
m_selectableTableView(this, this, this, this),
m_calculationHistory{},
m_calculationStore(calculationStore),
m_complexController(editExpressionController),
m_integerController(editExpressionController),
m_rationalController(editExpressionController),
m_secondDegreeController(editExpressionController),
m_trigonometryController(editExpressionController),
m_unitController(editExpressionController),
m_matrixController(editExpressionController)
{
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].setParentResponder(&m_selectableTableView);
m_calculationHistory[i].setDataSource(this);
}
}
void HistoryController::reload() {
/* When reloading, we might not used anymore cell that hold previous layouts.
* We clean them all before reloading their content to avoid taking extra
* useless space in the Poincare pool. */
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].resetMemoization();
}
m_selectableTableView.reloadData();
/* TODO
* Replace the following by selectCellAtLocation in order to avoid laying out
* the table view twice.
*/
if (numberOfRows() > 0) {
m_selectableTableView.scrollToBottom();
// Force to reload last added cell (hide the burger and exact output if necessary)
tableViewDidChangeSelectionAndDidScroll(&m_selectableTableView, 0, numberOfRows()-1);
}
}
void HistoryController::viewWillAppear() {
ViewController::viewWillAppear();
reload();
}
void HistoryController::didBecomeFirstResponder() {
selectCellAtLocation(0, numberOfRows()-1);
Container::activeApp()->setFirstResponder(&m_selectableTableView);
}
void HistoryController::willExitResponderChain(Responder * nextFirstResponder) {
if (nextFirstResponder == nullptr) {
return;
}
if (nextFirstResponder == parentResponder()) {
m_selectableTableView.deselectTable();
}
}
bool HistoryController::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::Down) {
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
if (event == Ion::Events::Up) {
return true;
}
if (event == Ion::Events::OK || event == Ion::Events::EXE) {
int focusRow = selectedRow();
HistoryViewCell * selectedCell = (HistoryViewCell *)m_selectableTableView.selectedCell();
SubviewType subviewType = selectedSubviewType();
EditExpressionController * editController = (EditExpressionController *)parentResponder();
if (subviewType == SubviewType::Input) {
m_selectableTableView.deselectTable();
editController->insertTextBody(calculationAtIndex(focusRow)->inputText());
} else if (subviewType == SubviewType::Output) {
m_selectableTableView.deselectTable();
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(focusRow);
ScrollableTwoExpressionsView::SubviewPosition outputSubviewPosition = selectedCell->outputView()->selectedSubviewPosition();
if (outputSubviewPosition == ScrollableTwoExpressionsView::SubviewPosition::Right
&& !calculation->shouldOnlyDisplayExactOutput())
{
editController->insertTextBody(calculation->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal));
} else {
editController->insertTextBody(calculation->exactOutputText());
}
} else {
assert(subviewType == SubviewType::Ellipsis);
Calculation::AdditionalInformationType additionalInfoType = selectedCell->additionalInformationType();
ListController * vc = nullptr;
Expression e = calculationAtIndex(focusRow)->exactOutput();
if (additionalInfoType == Calculation::AdditionalInformationType::Complex) {
vc = &m_complexController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::SecondDegree) {
vc = &m_secondDegreeController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::TrigonometryInput) {
vc = &m_trigonometryController;
e = calculationAtIndex(focusRow)->input();
} else if (additionalInfoType == Calculation::AdditionalInformationType::TrigonometryOutput) {
vc = &m_trigonometryController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Integer) {
vc = &m_integerController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Rational) {
vc = &m_rationalController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Unit) {
vc = &m_unitController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Matrix) {
vc = &m_matrixController;
}
if (vc) {
vc->setExpression(e);
Container::activeApp()->displayModalViewController(vc, 0.f, 0.f, Metric::CommonTopMargin, Metric::PopUpLeftMargin, 0, Metric::PopUpRightMargin);
}
}
return true;
}
if (event == Ion::Events::Backspace) {
int focusRow = selectedRow();
SubviewType subviewType = selectedSubviewType();
m_selectableTableView.deselectTable();
m_calculationStore->deleteCalculationAtIndex(storeIndex(focusRow));
reload();
if (numberOfRows()== 0) {
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
m_selectableTableView.selectCellAtLocation(0, focusRow > 0 ? focusRow - 1 : 0);
/* The parameters 'sameCell' and 'previousSelectedY' are chosen to enforce
* toggling of the output when necessary. */
setSelectedSubviewType(subviewType, false, 0, (subviewType == SubviewType::Input) ? selectedRow() : -1);
return true;
}
if (event == Ion::Events::Clear) {
m_selectableTableView.deselectTable();
m_calculationStore->deleteAll();
reload();
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
if (event == Ion::Events::Back) {
m_selectableTableView.deselectTable();
Container::activeApp()->setFirstResponder(parentResponder());
return true;
}
return false;
}
Shared::ExpiringPointer<Calculation> HistoryController::calculationAtIndex(int i) {
return m_calculationStore->calculationAtIndex(storeIndex(i));
}
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() == -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;
}
Container::activeApp()->setFirstResponder(selectedCell);
}
int HistoryController::numberOfRows() const {
return m_calculationStore->numberOfCalculations();
};
HighlightCell * HistoryController::reusableCell(int index, int type) {
assert(type == 0);
assert(index >= 0);
assert(index < k_maxNumberOfDisplayedRows);
return &m_calculationHistory[index];
}
int HistoryController::reusableCellCount(int type) {
assert(type == 0);
return k_maxNumberOfDisplayedRows;
}
void HistoryController::willDisplayCellForIndex(HighlightCell * cell, int index) {
HistoryViewCell * myCell = (HistoryViewCell *)cell;
myCell->setCalculation(calculationAtIndex(index).pointer(), index == selectedRow() && selectedSubviewType() == SubviewType::Output);
myCell->setEven(index%2 == 0);
myCell->reloadSubviewHighlight();
}
KDCoordinate HistoryController::rowHeight(int j) {
if (j >= m_calculationStore->numberOfCalculations()) {
return 0;
}
Shared::ExpiringPointer<Calculation> calculation = calculationAtIndex(j);
bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output;
return calculation->height(expanded);
}
int HistoryController::typeAtLocation(int i, int j) {
return 0;
}
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<HistoryViewCell *>(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. */
if ((type == SubviewType::Output || previousType == SubviewType::Output) && (calculationAtIndexToggles(selectedRow()) || calculationAtIndexToggles(previousSelectedCellY))) {
m_selectableTableView.reloadData();
}
// It might be necessary to scroll to the sub type if the cell overflows the screen
if (selectedRow() >= 0) {
m_selectableTableView.scrollToSubviewOfTypeOfCellAtLocation(type, m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow());
}
// Fill the selected cell and the previous selected cell because cells repartition might have changed
*cell = static_cast<HistoryViewCell *>(m_selectableTableView.selectedCell());
*previousCell = static_cast<HistoryViewCell *>(m_selectableTableView.cellAtLocation(previousSelectedCellX, previousSelectedCellY));
/* 'reloadData' calls 'willDisplayCellForIndex' for each cell while the table
* has been deselected. To reload the expanded cell, we call one more time
* 'willDisplayCellForIndex' but once the right cell has been selected. */
if (*cell) {
willDisplayCellForIndex(*cell, selectedRow());
}
}
}