#include "history_controller.h" #include "app.h" #include #include 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 = 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 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 = 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(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(m_selectableTableView.selectedCell()); *previousCell = static_cast(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()); } } }