// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include // for UCHAR_MAX #include "Header Files/CalcEngine.h" #include "CalculatorManager.h" #include "CalculatorResource.h" using namespace std; using namespace CalcEngine; static constexpr size_t MAX_HISTORY_ITEMS = 20; #ifndef _MSC_VER #define __pragma(x) #endif namespace CalculationManager { CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider) : m_displayCallback(displayCallback) , m_currentCalculatorEngine(nullptr) , m_resourceProvider(resourceProvider) , m_inHistoryItemLoadMode(false) , m_persistedPrimaryValue() , m_isExponentialFormat(false) , m_currentDegreeMode(Command::CommandNULL) , m_pStdHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) , m_pSciHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) , m_pHistory(nullptr) { CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider); } /// /// Call the callback function using passed in IDisplayHelper. /// Used to set the primary display value on ViewModel /// /// wstring representing text to be displayed void CalculatorManager::SetPrimaryDisplay(_In_ const wstring& displayString, _In_ bool isError) { if (!m_inHistoryItemLoadMode) { m_displayCallback->SetPrimaryDisplay(displayString, isError); } } void CalculatorManager::SetIsInError(bool isError) { m_displayCallback->SetIsInError(isError); } void CalculatorManager::DisplayPasteError() { m_currentCalculatorEngine->DisplayError(CALC_E_DOMAIN /*code for "Invalid input" error*/); } void CalculatorManager::MaxDigitsReached() { m_displayCallback->MaxDigitsReached(); } void CalculatorManager::BinaryOperatorReceived() { m_displayCallback->BinaryOperatorReceived(); } void CalculatorManager::MemoryItemChanged(unsigned int indexOfMemory) { m_displayCallback->MemoryItemChanged(indexOfMemory); } void CalculatorManager::InputChanged() { m_displayCallback->InputChanged(); } /// /// Call the callback function using passed in IDisplayHelper. /// Used to set the expression display value on ViewModel /// /// wstring representing expression to be displayed void CalculatorManager::SetExpressionDisplay( _Inout_ shared_ptr>> const& tokens, _Inout_ shared_ptr>> const& commands) { if (!m_inHistoryItemLoadMode) { m_displayCallback->SetExpressionDisplay(tokens, commands); } } /// /// Callback from the CalculatorControl /// Passed in string representations of memorized numbers get passed to the client /// /// vector containing wstring values of memorized numbers void CalculatorManager::SetMemorizedNumbers(_In_ const vector& memorizedNumbers) { m_displayCallback->SetMemorizedNumbers(memorizedNumbers); } /// /// Callback from the engine /// /// string containing the parenthesis count void CalculatorManager::SetParenthesisNumber(_In_ unsigned int parenthesisCount) { m_displayCallback->SetParenthesisNumber(parenthesisCount); } /// /// Callback from the engine /// void CalculatorManager::OnNoRightParenAdded() { m_displayCallback->OnNoRightParenAdded(); } /// /// Reset CalculatorManager. /// Set the mode to the standard calculator /// Set the degree mode as regular degree (as oppose to Rad or Grad) /// Clear all the entries and memories /// Clear Memory if clearMemory parameter is true.(Default value is true) /// void CalculatorManager::Reset(bool clearMemory /* = true*/) { SetStandardMode(); if (m_scientificCalculatorEngine) { m_scientificCalculatorEngine->ProcessCommand(IDC_DEG); m_scientificCalculatorEngine->ProcessCommand(IDC_CLEAR); if (m_isExponentialFormat) { m_isExponentialFormat = false; m_scientificCalculatorEngine->ProcessCommand(IDC_FE); } } if (m_programmerCalculatorEngine) { m_programmerCalculatorEngine->ProcessCommand(IDC_CLEAR); } if (clearMemory) { this->MemorizedNumberClearAll(); } } /// /// Change the current calculator engine to standard calculator engine. /// void CalculatorManager::SetStandardMode() { if (!m_standardCalculatorEngine) { m_standardCalculatorEngine = make_unique(false /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pStdHistory); } m_currentCalculatorEngine = m_standardCalculatorEngine.get(); m_currentCalculatorEngine->ProcessCommand(IDC_DEC); m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR); m_currentCalculatorEngine->ChangePrecision(static_cast(CalculatorPrecision::StandardModePrecision)); UpdateMaxIntDigits(); m_pHistory = m_pStdHistory.get(); } /// /// Change the current calculator engine to scientific calculator engine. /// void CalculatorManager::SetScientificMode() { if (!m_scientificCalculatorEngine) { m_scientificCalculatorEngine = make_unique(true /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pSciHistory); } m_currentCalculatorEngine = m_scientificCalculatorEngine.get(); m_currentCalculatorEngine->ProcessCommand(IDC_DEC); m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR); m_currentCalculatorEngine->ChangePrecision(static_cast(CalculatorPrecision::ScientificModePrecision)); m_pHistory = m_pSciHistory.get(); } /// /// Change the current calculator engine to scientific calculator engine. /// void CalculatorManager::SetProgrammerMode() { if (!m_programmerCalculatorEngine) { m_programmerCalculatorEngine = make_unique(true /* Respect Order of Operations */, true /* Set to Integer Mode */, m_resourceProvider, this, nullptr); } m_currentCalculatorEngine = m_programmerCalculatorEngine.get(); m_currentCalculatorEngine->ProcessCommand(IDC_DEC); m_currentCalculatorEngine->ProcessCommand(IDC_CLEAR); m_currentCalculatorEngine->ChangePrecision(static_cast(CalculatorPrecision::ProgrammerModePrecision)); } /// /// Send command to the Calc Engine /// Cast Command Enum to OpCode. /// Handle special commands such as mode change and combination of two commands. /// /// Enum Command void CalculatorManager::SendCommand(_In_ Command command) { // When the expression line is cleared, we save the current state, which includes, // primary display, memory, and degree mode if (command == Command::CommandCLEAR || command == Command::CommandEQU || command == Command::ModeBasic || command == Command::ModeScientific || command == Command::ModeProgrammer) { switch (command) { case Command::ModeBasic: this->SetStandardMode(); break; case Command::ModeScientific: this->SetScientificMode(); break; case Command::ModeProgrammer: this->SetProgrammerMode(); break; default: m_currentCalculatorEngine->ProcessCommand(static_cast(command)); } InputChanged(); return; } if (command == Command::CommandDEG || command == Command::CommandRAD || command == Command::CommandGRAD) { m_currentDegreeMode = command; } switch (command) { case Command::CommandASIN: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSIN)); break; case Command::CommandACOS: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOS)); break; case Command::CommandATAN: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTAN)); break; case Command::CommandPOWE: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandLN)); break; case Command::CommandASINH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSINH)); break; case Command::CommandACOSH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOSH)); break; case Command::CommandATANH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTANH)); break; case Command::CommandASEC: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSEC)); break; case Command::CommandACSC: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCSC)); break; case Command::CommandACOT: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOT)); break; case Command::CommandASECH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSECH)); break; case Command::CommandACSCH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCSCH)); break; case Command::CommandACOTH: m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOTH)); break; case Command::CommandFE: m_isExponentialFormat = !m_isExponentialFormat; [[fallthrough]]; default: m_currentCalculatorEngine->ProcessCommand(static_cast(command)); break; } InputChanged(); } /// /// Load the persisted value that is saved in memory of CalcEngine /// void CalculatorManager::LoadPersistedPrimaryValue() { m_currentCalculatorEngine->PersistedMemObject(m_persistedPrimaryValue); m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); InputChanged(); } /// /// Memorize the current displayed value /// Notify the client with new the new memorize value vector /// void CalculatorManager::MemorizeNumber() { if (m_currentCalculatorEngine->FInErrorState()) { return; } m_currentCalculatorEngine->ProcessCommand(IDC_STORE); auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject(); if (memoryObjectPtr != nullptr) { m_memorizedNumbers.insert(m_memorizedNumbers.begin(), *memoryObjectPtr); } if (m_memorizedNumbers.size() > m_maximumMemorySize) { m_memorizedNumbers.resize(m_maximumMemorySize); } this->SetMemorizedNumbersString(); } /// /// Recall the memorized number. /// The memorized number gets loaded to the primary display /// /// Index of the target memory void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory) { if (m_currentCalculatorEngine->FInErrorState()) { return; } this->MemorizedNumberSelect(indexOfMemory); m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); InputChanged(); } /// /// Do the addition to the selected memory /// It adds primary display value to the selected memory /// Notify the client with new the new memorize value vector /// /// Index of the target memory void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory) { if (m_currentCalculatorEngine->FInErrorState()) { return; } if (m_memorizedNumbers.empty()) { this->MemorizeNumber(); } else { this->MemorizedNumberSelect(indexOfMemory); m_currentCalculatorEngine->ProcessCommand(IDC_MPLUS); this->MemorizedNumberChanged(indexOfMemory); this->SetMemorizedNumbersString(); } m_displayCallback->MemoryItemChanged(indexOfMemory); } void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory) { if (indexOfMemory < m_memorizedNumbers.size()) { m_memorizedNumbers.erase(m_memorizedNumbers.begin() + indexOfMemory); } } /// /// Do the subtraction to the selected memory /// It adds primary display value to the selected memory /// Notify the client with new the new memorize value vector /// /// Index of the target memory void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory) { if (m_currentCalculatorEngine->FInErrorState()) { return; } // To add negative of the number on display to the memory -x = x - 2x if (m_memorizedNumbers.empty()) { this->MemorizeNumber(); this->MemorizedNumberSubtract(0); this->MemorizedNumberSubtract(0); } else { this->MemorizedNumberSelect(indexOfMemory); m_currentCalculatorEngine->ProcessCommand(IDC_MMINUS); this->MemorizedNumberChanged(indexOfMemory); this->SetMemorizedNumbersString(); } m_displayCallback->MemoryItemChanged(indexOfMemory); } /// /// Clear all the memorized values /// Notify the client with new the new memorize value vector /// void CalculatorManager::MemorizedNumberClearAll() { m_memorizedNumbers.clear(); m_currentCalculatorEngine->ProcessCommand(IDC_MCLEAR); this->SetMemorizedNumbersString(); } /// /// Helper function that selects a memory from the vector and set it to CCalcEngine /// Saved RAT number needs to be copied and passed in, as CCalcEngine destroyed the passed in RAT /// /// Index of the target memory void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory) { if (m_currentCalculatorEngine->FInErrorState()) { return; } auto memoryObject = m_memorizedNumbers.at(indexOfMemory); m_currentCalculatorEngine->PersistedMemObject(memoryObject); } /// /// Helper function that needs to be executed when memory is modified /// When memory is modified, destroy the old RAT and put the new RAT in vector /// /// Index of the target memory void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory) { if (m_currentCalculatorEngine->FInErrorState()) { return; } auto memoryObject = m_currentCalculatorEngine->PersistedMemObject(); if (memoryObject != nullptr) { m_memorizedNumbers.at(indexOfMemory) = *memoryObject; } } vector> const& CalculatorManager::GetHistoryItems() { return m_pHistory->GetHistory(); } vector> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode) { return (mode == CalculatorMode::Standard) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory(); } shared_ptr const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx) { return m_pHistory->GetHistoryItem(uIdx); } void CalculatorManager::OnHistoryItemAdded(_In_ unsigned int addedItemIndex) { m_displayCallback->OnHistoryItemAdded(addedItemIndex); } bool CalculatorManager::RemoveHistoryItem(_In_ unsigned int uIdx) { return m_pHistory->RemoveItem(uIdx); } void CalculatorManager::ClearHistory() { m_pHistory->ClearHistory(); } void CalculatorManager::SetRadix(RadixType iRadixType) { switch (iRadixType) { case RadixType::Hex: m_currentCalculatorEngine->ProcessCommand(IDC_HEX); break; case RadixType::Decimal: m_currentCalculatorEngine->ProcessCommand(IDC_DEC); break; case RadixType::Octal: m_currentCalculatorEngine->ProcessCommand(IDC_OCT); break; case RadixType::Binary: m_currentCalculatorEngine->ProcessCommand(IDC_BIN); break; default: break; } SetMemorizedNumbersString(); } void CalculatorManager::SetMemorizedNumbersString() { vector resultVector; for (auto const& memoryItem : m_memorizedNumbers) { int radix = m_currentCalculatorEngine->GetCurrentRadix(); wstring stringValue = m_currentCalculatorEngine->GetStringForDisplay(memoryItem, radix); if (!stringValue.empty()) { resultVector.push_back(m_currentCalculatorEngine->GroupDigitsPerRadix(stringValue, radix)); } } m_displayCallback->SetMemorizedNumbers(resultVector); } CalculationManager::Command CalculatorManager::GetCurrentDegreeMode() { if (m_currentDegreeMode == Command::CommandNULL) { m_currentDegreeMode = Command::CommandDEG; } return m_currentDegreeMode; } wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L""; } void CalculatorManager::SetPrecision(int32_t precision) { m_currentCalculatorEngine->ChangePrecision(precision); } void CalculatorManager::UpdateMaxIntDigits() { m_currentCalculatorEngine->UpdateMaxIntDigits(); } wchar_t CalculatorManager::DecimalSeparator() { return m_currentCalculatorEngine ? m_currentCalculatorEngine->DecimalSeparator() : m_resourceProvider->GetCEngineString(L"sDecimal")[0]; } bool CalculatorManager::IsEngineRecording() { return m_currentCalculatorEngine->FInRecordingState(); } bool CalculatorManager::IsInputEmpty() { return m_currentCalculatorEngine->IsInputEmpty(); } void CalculatorManager::SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode) { m_inHistoryItemLoadMode = isHistoryItemLoadMode; } }