All projects are built with warning level 4 (/W4) and treat warnings as errors (/WX). Fixed build errors resulting from enabling these compiler flags.
2008 lines
68 KiB
C++
2008 lines
68 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#include "pch.h"
|
|
#include "StandardCalculatorViewModel.h"
|
|
#include "Common/CalculatorButtonPressedEventArgs.h"
|
|
#include "Common/LocalizationStringUtil.h"
|
|
#include "Common/LocalizationSettings.h"
|
|
#include "Common/CopyPasteManager.h"
|
|
#include "Common/TraceLogger.h"
|
|
|
|
using namespace CalculatorApp;
|
|
using namespace CalculatorApp::Common;
|
|
using namespace CalculatorApp::Common::Automation;
|
|
using namespace CalculatorApp::ViewModel;
|
|
using namespace CalculationManager;
|
|
using namespace Platform;
|
|
using namespace Platform::Collections;
|
|
using namespace std;
|
|
using namespace Windows::ApplicationModel::Resources;
|
|
using namespace Windows::Foundation;
|
|
using namespace Windows::System;
|
|
using namespace Windows::UI::Core;
|
|
using namespace Windows::UI::Popups;
|
|
using namespace Windows::Storage::Streams;
|
|
using namespace Windows::Foundation::Collections;
|
|
using namespace Utils;
|
|
|
|
constexpr int StandardModePrecision = 16;
|
|
constexpr int ScientificModePrecision = 32;
|
|
constexpr int ProgrammerModePrecision = 64;
|
|
|
|
namespace CalculatorApp::ViewModel
|
|
{
|
|
namespace CalculatorViewModelProperties
|
|
{
|
|
StringReference IsMemoryEmpty(L"IsMemoryEmpty");
|
|
StringReference IsScientific(L"IsScientific");
|
|
StringReference IsStandard(L"IsStandard");
|
|
StringReference IsProgrammer(L"IsProgrammer");
|
|
StringReference DisplayValue(L"DisplayValue");
|
|
StringReference IsInError(L"IsInError");
|
|
StringReference BinaryDisplayValue(L"BinaryDisplayValue");
|
|
}
|
|
|
|
namespace CalculatorResourceKeys
|
|
{
|
|
StringReference CalculatorExpression(L"Format_CalculatorExpression");
|
|
StringReference CalculatorResults(L"Format_CalculatorResults");
|
|
StringReference CalculatorResults_DecimalSeparator_Announced(L"Format_CalculatorResults_Decimal");
|
|
StringReference HexButton(L"Format_HexButtonValue");
|
|
StringReference DecButton(L"Format_DecButtonValue");
|
|
StringReference OctButton(L"Format_OctButtonValue");
|
|
StringReference BinButton(L"Format_BinButtonValue");
|
|
StringReference LeftParenthesisAutomationFormat(L"Format_OpenParenthesisAutomationNamePrefix");
|
|
StringReference MaxDigitsReachedFormat(L"Format_MaxDigitsReached");
|
|
StringReference ButtonPressFeedbackFormat(L"Format_ButtonPressAuditoryFeedback");
|
|
StringReference MemorySave(L"Format_MemorySave");
|
|
StringReference MemoryItemChanged(L"Format_MemorySlotChanged");
|
|
StringReference MemoryItemCleared(L"Format_MemorySlotCleared");
|
|
StringReference MemoryCleared(L"Memory_Cleared");
|
|
StringReference DisplayCopied(L"Display_Copied");
|
|
}
|
|
}
|
|
|
|
StandardCalculatorViewModel::StandardCalculatorViewModel() :
|
|
m_DisplayValue(L"0"),
|
|
m_DecimalDisplayValue(L"0"),
|
|
m_HexDisplayValue(L"0"),
|
|
m_BinaryDisplayValue(L"0"),
|
|
m_OctalDisplayValue(L"0"),
|
|
m_standardCalculatorManager(&m_calculatorDisplay, &m_resourceProvider),
|
|
m_MemorizedNumbers(ref new Vector<MemoryItemViewModel^>()),
|
|
m_IsMemoryEmpty(true),
|
|
m_IsFToEChecked(false),
|
|
m_isShiftChecked(false),
|
|
m_IsShiftProgrammerChecked(false),
|
|
m_IsQwordEnabled(true),
|
|
m_IsDwordEnabled(true),
|
|
m_IsWordEnabled(true),
|
|
m_IsByteEnabled(true),
|
|
m_isBitFlipChecked(false),
|
|
m_isBinaryBitFlippingEnabled(false),
|
|
m_CurrentRadixType(RADIX_TYPE::DEC_RADIX),
|
|
m_CurrentAngleType(NumbersAndOperatorsEnum::Degree),
|
|
m_OpenParenthesisCount(L""),
|
|
m_Announcement(nullptr),
|
|
m_feedbackForButtonPress(nullptr),
|
|
m_isRtlLanguage(false),
|
|
m_localizedMaxDigitsReachedAutomationFormat(nullptr),
|
|
m_localizedButtonPressFeedbackAutomationFormat(nullptr),
|
|
m_localizedMemorySavedAutomationFormat(nullptr),
|
|
m_localizedMemoryItemChangedAutomationFormat(nullptr),
|
|
m_localizedMemoryItemClearedAutomationFormat(nullptr),
|
|
m_localizedMemoryCleared(nullptr)
|
|
{
|
|
WeakReference calculatorViewModel(this);
|
|
m_calculatorDisplay.SetCallback(calculatorViewModel);
|
|
m_expressionAutomationNameFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorExpression);
|
|
m_localizedCalculationResultAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorResults);
|
|
m_localizedCalculationResultDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorResults_DecimalSeparator_Announced);
|
|
m_localizedHexaDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::HexButton);
|
|
m_localizedDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::DecButton);
|
|
m_localizedOctalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::OctButton);
|
|
m_localizedBinaryAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::BinButton);
|
|
m_leftParenthesisAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::LeftParenthesisAutomationFormat);
|
|
|
|
// Initialize the Automation Name
|
|
CalculationResultAutomationName = GetLocalizedStringFormat(m_localizedCalculationResultAutomationFormat, m_DisplayValue);
|
|
CalculationExpressionAutomationName = GetLocalizedStringFormat(m_expressionAutomationNameFormat, L"");
|
|
|
|
// Initialize history view model
|
|
m_HistoryVM = ref new HistoryViewModel(&m_standardCalculatorManager);
|
|
m_HistoryVM->SetCalculatorDisplay(m_calculatorDisplay);
|
|
|
|
m_decimalSeparator = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
|
|
|
if (CoreWindow::GetForCurrentThread() != nullptr)
|
|
{
|
|
// Must have a CoreWindow to access the resource context.
|
|
m_isRtlLanguage = LocalizationService::GetInstance()->IsRtlLayout();
|
|
}
|
|
|
|
IsEditingEnabled = false;
|
|
IsUnaryOperatorEnabled = true;
|
|
IsBinaryOperatorEnabled = true;
|
|
IsOperandEnabled = true;
|
|
IsNegateEnabled = true;
|
|
IsDecimalEnabled = true;
|
|
AreHistoryShortcutsEnabled = true;
|
|
AreProgrammerRadixOperatorsEnabled = false;
|
|
|
|
m_tokenPosition = -1;
|
|
m_isLastOperationHistoryLoad = false;
|
|
}
|
|
|
|
String^ StandardCalculatorViewModel::LocalizeDisplayValue(_In_ wstring const &displayValue, _In_ bool isError)
|
|
{
|
|
wstring result(displayValue);
|
|
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&result);
|
|
|
|
// WINBLUE: 440747 - In BiDi languages, error messages need to be wrapped in LRE/PDF
|
|
if (isError && m_isRtlLanguage)
|
|
{
|
|
result.insert(result.begin(), Utils::LRE);
|
|
result.push_back(Utils::PDF);
|
|
}
|
|
|
|
return ref new Platform::String(result.c_str());
|
|
}
|
|
|
|
String^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring const &displayValue, _In_ String^ localizedDisplayValue, _In_ bool isError)
|
|
{
|
|
String^ localizedValue = localizedDisplayValue;
|
|
String^ automationFormat = m_localizedCalculationResultAutomationFormat;
|
|
|
|
// The narrator doesn't read the decimalSeparator if it's the last character
|
|
if (Utils::IsLastCharacterTarget(displayValue, m_decimalSeparator))
|
|
{
|
|
// remove the decimal separator, to avoid a long pause between words
|
|
localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.length() - 1), isError);
|
|
|
|
// Use a format which has a word in the decimal separator's place
|
|
// "The Display is 10 point"
|
|
automationFormat = m_localizedCalculationResultDecimalAutomationFormat;
|
|
}
|
|
|
|
// In Programmer modes using non-base10, we want the strings to be read as literal digits.
|
|
if (IsProgrammer && CurrentRadixType != RADIX_TYPE::DEC_RADIX)
|
|
{
|
|
localizedValue = GetNarratorStringReadRawNumbers(localizedValue);
|
|
}
|
|
|
|
return GetLocalizedStringFormat(automationFormat, localizedValue);
|
|
}
|
|
|
|
String^ StandardCalculatorViewModel::GetNarratorStringReadRawNumbers(_In_ String^ localizedDisplayValue)
|
|
{
|
|
wstringstream wss;
|
|
auto& locSettings = LocalizationSettings::GetInstance();
|
|
|
|
// Insert a space after each digit in the string, to force Narrator to read them as separate numbers.
|
|
wstring wstrValue(localizedDisplayValue->Data());
|
|
for (wchar_t& c : wstrValue)
|
|
{
|
|
wss << c;
|
|
if (locSettings.IsLocalizedHexDigit(c))
|
|
{
|
|
wss << L' ';
|
|
}
|
|
}
|
|
|
|
return ref new String(wss.str().c_str());
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ wstring const &displayStringValue, _In_ bool isError)
|
|
{
|
|
String^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue, isError);
|
|
|
|
// Set this variable before the DisplayValue is modified, Otherwise the DisplayValue will
|
|
// not match what the narrator is saying
|
|
m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue, localizedDisplayStringValue, isError);
|
|
|
|
DisplayValue = localizedDisplayStringValue;
|
|
|
|
IsInError = isError;
|
|
|
|
if (IsProgrammer)
|
|
{
|
|
UpdateProgrammerPanelDisplay();
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::DisplayPasteError()
|
|
{
|
|
m_standardCalculatorManager.DisplayPasteError();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetParenthesisCount(_In_ const wstring& parenthesisCount)
|
|
{
|
|
if (IsProgrammer || IsScientific)
|
|
{
|
|
OpenParenthesisCount = ref new String(parenthesisCount.c_str());
|
|
RaisePropertyChanged("LeftParenthesisAutomationName");
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::DisableButtons(CommandType selectedExpressionCommandType)
|
|
{
|
|
if (selectedExpressionCommandType == CommandType::OperandCommand)
|
|
{
|
|
IsBinaryOperatorEnabled = false;
|
|
IsUnaryOperatorEnabled = false;
|
|
IsOperandEnabled = true;
|
|
IsNegateEnabled = true;
|
|
IsDecimalEnabled = true;
|
|
}
|
|
if (selectedExpressionCommandType == CommandType::BinaryCommand)
|
|
{
|
|
IsBinaryOperatorEnabled = true;
|
|
IsUnaryOperatorEnabled = false;
|
|
IsOperandEnabled = false;
|
|
IsNegateEnabled = false;
|
|
IsDecimalEnabled = false;
|
|
}
|
|
if (selectedExpressionCommandType == CommandType::UnaryCommand)
|
|
{
|
|
IsBinaryOperatorEnabled = false;
|
|
IsUnaryOperatorEnabled = true;
|
|
IsOperandEnabled = false;
|
|
IsNegateEnabled = true;
|
|
IsDecimalEnabled = false;
|
|
}
|
|
}
|
|
|
|
String ^ StandardCalculatorViewModel::GetLeftParenthesisAutomationName()
|
|
{
|
|
String^ parenthesisCount = ((m_OpenParenthesisCount == nullptr) ? "0" : m_OpenParenthesisCount);
|
|
wstring localizedParenthesisCount = std::wstring(parenthesisCount->Data());
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount);
|
|
|
|
return GetLocalizedStringFormat(m_leftParenthesisAutomationFormat, ref new String(localizedParenthesisCount.c_str()));
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetExpressionDisplay(_Inout_ shared_ptr<CalculatorVector<pair<wstring, int>>> const &tokens, _Inout_ shared_ptr<CalculatorVector <shared_ptr<IExpressionCommand>>> const &commands)
|
|
{
|
|
m_tokens = tokens;
|
|
m_commands = commands;
|
|
if (!IsEditingEnabled)
|
|
{
|
|
SetTokens(tokens);
|
|
}
|
|
|
|
CalculationExpressionAutomationName = GetCalculatorExpressionAutomationName();
|
|
|
|
AreTokensUpdated = true;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetHistoryExpressionDisplay(_Inout_ shared_ptr<CalculatorVector<pair<wstring, int>>> const &tokens, _Inout_ shared_ptr<CalculatorVector <shared_ptr<IExpressionCommand>>> const &commands)
|
|
{
|
|
m_tokens = make_shared<CalculatorVector<pair<wstring, int>>>(*tokens);
|
|
m_commands = make_shared<CalculatorVector <shared_ptr<IExpressionCommand>>>(*commands);
|
|
IsEditingEnabled = false;
|
|
|
|
// Setting the History Item Load Mode so that UI does not get updated with recalculation of every token
|
|
m_standardCalculatorManager.SetInHistoryItemLoadMode(true);
|
|
Recalculate(true);
|
|
m_standardCalculatorManager.SetInHistoryItemLoadMode(false);
|
|
m_isLastOperationHistoryLoad = true;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetTokens(_Inout_ shared_ptr<CalculatorVector<pair<wstring, int>>> const &tokens)
|
|
{
|
|
AreTokensUpdated = false;
|
|
|
|
if (m_ExpressionTokens == nullptr)
|
|
{
|
|
m_ExpressionTokens = ref new Vector<DisplayExpressionToken^>();
|
|
}
|
|
else
|
|
{
|
|
m_ExpressionTokens->Clear();
|
|
}
|
|
|
|
unsigned int nTokens = 0;
|
|
tokens->GetSize(&nTokens);
|
|
pair <wstring, int> currentToken;
|
|
const auto& localizer = LocalizationSettings::GetInstance();
|
|
|
|
for (unsigned int i = 0; i < nTokens; ++i)
|
|
{
|
|
if (SUCCEEDED(tokens->GetAt(i, ¤tToken)))
|
|
{
|
|
Common::TokenType type;
|
|
const wstring separator = L" ";
|
|
bool isEditable = (currentToken.second == -1) ? false : true;
|
|
localizer.LocalizeDisplayValue(&(currentToken.first));
|
|
|
|
if (!isEditable)
|
|
{
|
|
if (currentToken.first == separator)
|
|
{
|
|
type = TokenType::Separator;
|
|
}
|
|
else
|
|
{
|
|
type = TokenType::Operator;
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
shared_ptr<IExpressionCommand> command;
|
|
IFTPlatformException(m_commands->GetAt(static_cast<unsigned int>(currentToken.second), &command));
|
|
|
|
if (command->GetCommandType() == CommandType::OperandCommand)
|
|
{
|
|
type = TokenType::Operand;
|
|
}
|
|
else
|
|
{
|
|
type = TokenType::Operator;
|
|
}
|
|
}
|
|
DisplayExpressionToken^ expressionToken = ref new DisplayExpressionToken(ref new String(currentToken.first.c_str()), i, isEditable, type);
|
|
m_ExpressionTokens->Append(expressionToken);
|
|
}
|
|
}
|
|
}
|
|
|
|
String^ StandardCalculatorViewModel::GetCalculatorExpressionAutomationName()
|
|
{
|
|
String^ expression = L"";
|
|
for (auto&& token : m_ExpressionTokens)
|
|
{
|
|
expression += LocalizationService::GetNarratorReadableToken(token->Token);
|
|
}
|
|
|
|
return GetLocalizedStringFormat(m_expressionAutomationNameFormat, expression);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetMemorizedNumbers(const vector<wstring>& newMemorizedNumbers)
|
|
{
|
|
const auto& localizer = LocalizationSettings::GetInstance();
|
|
if (newMemorizedNumbers.size() == 0) // Memory has been cleared
|
|
{
|
|
MemorizedNumbers->Clear();
|
|
IsMemoryEmpty = true;
|
|
}
|
|
// A new value is added to the memory
|
|
else if (newMemorizedNumbers.size() > MemorizedNumbers->Size)
|
|
{
|
|
while (newMemorizedNumbers.size() > MemorizedNumbers->Size)
|
|
{
|
|
size_t newValuePosition = newMemorizedNumbers.size() - MemorizedNumbers->Size - 1;
|
|
auto stringValue = newMemorizedNumbers.at(newValuePosition);
|
|
|
|
MemoryItemViewModel^ memorySlot = ref new MemoryItemViewModel(this);
|
|
memorySlot->Position = 0;
|
|
localizer.LocalizeDisplayValue(&stringValue);
|
|
memorySlot->Value = Utils::LRO + ref new String(stringValue.c_str()) + Utils::PDF;
|
|
|
|
MemorizedNumbers->InsertAt(0, memorySlot);
|
|
IsMemoryEmpty = false;
|
|
|
|
// Update the slot position for the rest of the slots
|
|
for (unsigned int i = 1; i < MemorizedNumbers->Size; i++)
|
|
{
|
|
MemorizedNumbers->GetAt(i)->Position++;
|
|
}
|
|
}
|
|
}
|
|
else if (newMemorizedNumbers.size() == MemorizedNumbers->Size) // Either M+ or M-
|
|
{
|
|
for (unsigned int i = 0; i < MemorizedNumbers->Size; i++)
|
|
{
|
|
auto newStringValue = newMemorizedNumbers.at(i);
|
|
localizer.LocalizeDisplayValue(&newStringValue);
|
|
|
|
// If the value is different, update the value
|
|
if (MemorizedNumbers->GetAt(i)->Value != StringReference(newStringValue.c_str()))
|
|
{
|
|
MemorizedNumbers->GetAt(i)->Value = Utils::LRO + ref new String(newStringValue.c_str()) + Utils::PDF;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::FtoEButtonToggled()
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::FToE);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum)
|
|
{
|
|
DisplayExpressionToken^ displayExpressionToken = ExpressionTokens->GetAt(m_tokenPosition);
|
|
if (displayExpressionToken == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
if ((displayExpressionToken->Token == nullptr) || (displayExpressionToken->Token->Length() == 0))
|
|
{
|
|
displayExpressionToken->CommandIndex = 0;
|
|
}
|
|
|
|
wchar_t ch = 0;
|
|
if ((cmdenum >= Command::Command0) && (cmdenum <= Command::Command9))
|
|
{
|
|
switch (cmdenum)
|
|
{
|
|
case Command::Command0:
|
|
ch = L'0';
|
|
break;
|
|
case Command::Command1:
|
|
ch = L'1';
|
|
break;
|
|
case Command::Command2:
|
|
ch = L'2';
|
|
break;
|
|
case Command::Command3:
|
|
ch = L'3';
|
|
break;
|
|
case Command::Command4:
|
|
ch = L'4';
|
|
break;
|
|
case Command::Command5:
|
|
ch = L'5';
|
|
break;
|
|
case Command::Command6:
|
|
ch = L'6';
|
|
break;
|
|
case Command::Command7:
|
|
ch = L'7';
|
|
break;
|
|
case Command::Command8:
|
|
ch = L'8';
|
|
break;
|
|
case Command::Command9:
|
|
ch = L'9';
|
|
break;
|
|
}
|
|
}
|
|
else if (cmdenum == Command::CommandPNT)
|
|
{
|
|
ch = L'.';
|
|
}
|
|
else if (cmdenum == Command::CommandBACK)
|
|
{
|
|
ch = L'x';
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
int length = 0;
|
|
wchar_t* temp = new wchar_t[100];
|
|
const wchar_t* data = m_selectedExpressionLastData->Data();
|
|
int i = 0, j = 0;
|
|
int commandIndex = displayExpressionToken->CommandIndex;
|
|
|
|
if (IsOperandTextCompletelySelected)
|
|
{
|
|
//Clear older text;
|
|
m_selectedExpressionLastData = L"";
|
|
if (ch == L'x')
|
|
{
|
|
temp[0] = L'\0';
|
|
commandIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
temp[0] = ch;
|
|
temp[1] = L'\0';
|
|
commandIndex = 1;
|
|
}
|
|
IsOperandTextCompletelySelected = false;
|
|
}
|
|
else
|
|
{
|
|
if (ch == L'x')
|
|
{
|
|
if (commandIndex == 0)
|
|
{
|
|
delete [] temp;
|
|
return;
|
|
}
|
|
|
|
length = m_selectedExpressionLastData->Length();
|
|
for (; j < length; ++j)
|
|
{
|
|
if (j == commandIndex - 1)
|
|
{
|
|
continue;
|
|
}
|
|
temp[i++] = data[j];
|
|
}
|
|
temp[i] = L'\0';
|
|
commandIndex -= 1;
|
|
}
|
|
else
|
|
{
|
|
length = m_selectedExpressionLastData->Length() + 1;
|
|
if (length > 50)
|
|
{
|
|
return;
|
|
}
|
|
for (; i < length; ++i)
|
|
{
|
|
if (i == commandIndex)
|
|
{
|
|
temp[i] = ch;
|
|
continue;
|
|
}
|
|
temp[i] = data[j++];
|
|
}
|
|
temp[i] = L'\0';
|
|
commandIndex += 1;
|
|
}
|
|
}
|
|
|
|
String^ updatedData = ref new String(temp);
|
|
UpdateOperand(m_tokenPosition, updatedData);
|
|
displayExpressionToken->Token = updatedData;
|
|
IsOperandUpdatedUsingViewModel = true;
|
|
displayExpressionToken->CommandIndex = commandIndex;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsOperator(Command cmdenum)
|
|
{
|
|
if ((cmdenum == Command::Command0) || (cmdenum == Command::Command1) || (cmdenum == Command::Command2) || (cmdenum == Command::Command3) || (cmdenum == Command::Command4) || (cmdenum == Command::Command5)
|
|
|| (cmdenum == Command::Command6) || (cmdenum == Command::Command7) || (cmdenum == Command::Command8) || (cmdenum == Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK)
|
|
|| (cmdenum == Command::CommandEXP) || (cmdenum == Command::CommandFE) || (cmdenum == Command::ModeBasic) || (cmdenum == Command::ModeProgrammer) || (cmdenum == Command::ModeScientific)
|
|
|| (cmdenum == Command::CommandINV) || (cmdenum == Command::CommandCENTR) || (cmdenum == Command::CommandDEG) || (cmdenum == Command::CommandRAD) || (cmdenum == Command::CommandGRAD)
|
|
|| ((cmdenum >= Command::CommandBINEDITSTART) && (cmdenum <= Command::CommandBINEDITEND)))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter)
|
|
{
|
|
m_feedbackForButtonPress = CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(parameter);
|
|
NumbersAndOperatorsEnum numOpEnum = CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(parameter);
|
|
Command cmdenum = ConvertToOperatorsEnum(numOpEnum);
|
|
|
|
TraceLogger::GetInstance().UpdateFunctionUsage((int)numOpEnum);
|
|
|
|
if (IsInError)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandCLEAR);
|
|
|
|
if (!IsRecoverableCommand((int)numOpEnum))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IsEditingEnabled &&
|
|
numOpEnum != NumbersAndOperatorsEnum::IsScientificMode &&
|
|
numOpEnum != NumbersAndOperatorsEnum::IsStandardMode &&
|
|
numOpEnum != NumbersAndOperatorsEnum::IsProgrammerMode &&
|
|
numOpEnum != NumbersAndOperatorsEnum::FToE &&
|
|
(numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) && (numOpEnum != NumbersAndOperatorsEnum::Grads))
|
|
{
|
|
if (!m_keyPressed)
|
|
{
|
|
SaveEditedCommand(m_selectedExpressionToken->TokenPosition, cmdenum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (numOpEnum == NumbersAndOperatorsEnum::IsStandardMode ||
|
|
numOpEnum == NumbersAndOperatorsEnum::IsScientificMode ||
|
|
numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode)
|
|
{
|
|
IsEditingEnabled = false;
|
|
}
|
|
if (numOpEnum == NumbersAndOperatorsEnum::Memory)
|
|
{
|
|
OnMemoryButtonPressed();
|
|
}
|
|
else
|
|
{
|
|
if (numOpEnum == NumbersAndOperatorsEnum::Clear ||
|
|
numOpEnum == NumbersAndOperatorsEnum::ClearEntry ||
|
|
numOpEnum == NumbersAndOperatorsEnum::IsStandardMode ||
|
|
numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode)
|
|
{
|
|
// On Clear('C') the F-E button needs to be UnChecked if it in Checked state.
|
|
// Also, the Primary Display Value should not show in exponential format.
|
|
// Hence the check below to ensure parity with Desktop Calculator.
|
|
// Clear the FE mode if the switching to StandardMode, since 'C'/'CE' in StandardMode
|
|
// doesn't honor the FE button.
|
|
if (IsFToEChecked)
|
|
{
|
|
IsFToEChecked = false;
|
|
}
|
|
}
|
|
if (numOpEnum == NumbersAndOperatorsEnum::Degree || numOpEnum == NumbersAndOperatorsEnum::Radians || numOpEnum == NumbersAndOperatorsEnum::Grads)
|
|
{
|
|
m_CurrentAngleType = numOpEnum;
|
|
}
|
|
if ((cmdenum == Command::Command0) || (cmdenum == Command::Command1) || (cmdenum == Command::Command2) || (cmdenum == Command::Command3) || (cmdenum == Command::Command4) || (cmdenum == Command::Command5)
|
|
|| (cmdenum == Command::Command6) || (cmdenum == Command::Command7) || (cmdenum == Command::Command8) || (cmdenum == Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK) || (cmdenum == Command::CommandEXP))
|
|
{
|
|
IsOperatorCommand = false;
|
|
}
|
|
else
|
|
{
|
|
IsOperatorCommand = true;
|
|
}
|
|
|
|
if (m_isLastOperationHistoryLoad &&
|
|
((numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) && (numOpEnum != NumbersAndOperatorsEnum::Grads)))
|
|
{
|
|
IsFToEEnabled = true;
|
|
m_isLastOperationHistoryLoad = false;
|
|
}
|
|
|
|
m_standardCalculatorManager.SendCommand(cmdenum);
|
|
}
|
|
}
|
|
}
|
|
|
|
int StandardCalculatorViewModel::GetBitLengthType()
|
|
{
|
|
if (IsQwordEnabled)
|
|
{
|
|
return QwordType;
|
|
}
|
|
else if (IsDwordEnabled)
|
|
{
|
|
return DwordType;
|
|
}
|
|
else if (IsWordEnabled)
|
|
{
|
|
return WordType;
|
|
}
|
|
else
|
|
{
|
|
return ByteType;
|
|
}
|
|
}
|
|
|
|
int StandardCalculatorViewModel::GetNumberBase()
|
|
{
|
|
if (CurrentRadixType == HEX_RADIX)
|
|
{
|
|
return HexBase;
|
|
}
|
|
else if (CurrentRadixType == DEC_RADIX)
|
|
{
|
|
return DecBase;
|
|
}
|
|
else if (CurrentRadixType == OCT_RADIX)
|
|
{
|
|
return OctBase;
|
|
}
|
|
else
|
|
{
|
|
return BinBase;
|
|
}
|
|
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnCopyCommand(Object^ parameter)
|
|
{
|
|
CopyPasteManager::CopyToClipboard(GetRawDisplayValue());
|
|
|
|
String^ announcement = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::DisplayCopied);
|
|
Announcement = CalculatorAnnouncement::GetDisplayCopiedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPasteCommand(Object^ parameter)
|
|
{
|
|
ViewMode mode;
|
|
int NumberBase = -1;
|
|
int bitLengthType = -1;
|
|
if (IsScientific)
|
|
{
|
|
mode = ViewMode::Scientific;
|
|
}
|
|
else if (IsProgrammer)
|
|
{
|
|
mode = ViewMode::Programmer;
|
|
NumberBase = GetNumberBase();
|
|
bitLengthType = GetBitLengthType();
|
|
}
|
|
else
|
|
{
|
|
mode = ViewMode::Standard;
|
|
}
|
|
// if there's nothing to copy early out
|
|
if (IsEditingEnabled || !CopyPasteManager::HasStringToPaste())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ensure that the paste happens on the UI thread
|
|
CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), NumberBase, bitLengthType).then(
|
|
[this, mode](String^ pastedString)
|
|
{
|
|
OnPaste(pastedString, mode);
|
|
}, concurrency::task_continuation_context::use_current());
|
|
}
|
|
|
|
CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation)
|
|
{
|
|
return safe_cast<Command>(operation);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode)
|
|
{
|
|
// If pastedString is invalid("NoOp") then display pasteError else process the string
|
|
if (pastedString == StringReference(CopyPasteManager::PasteErrorString))
|
|
{
|
|
this->DisplayPasteError();
|
|
return;
|
|
}
|
|
|
|
TraceLogger::GetInstance().LogValidInputPasted(mode);
|
|
bool isFirstLegalChar = true;
|
|
m_standardCalculatorManager.SendCommand(Command::CommandCENTR);
|
|
bool sendNegate = false;
|
|
bool processedDigit = false;
|
|
bool sentEquals = false;
|
|
bool isPreviousOperator = false;
|
|
|
|
vector<bool> negateStack;
|
|
|
|
// Iterate through each character pasted, and if it's valid, send it to the model.
|
|
auto it = pastedString->Begin();
|
|
|
|
while (it != pastedString->End())
|
|
{
|
|
bool sendCommand = true;
|
|
bool canSendNegate = false;
|
|
|
|
NumbersAndOperatorsEnum mappedNumOp = MapCharacterToButtonId(*it, canSendNegate);
|
|
|
|
if (isFirstLegalChar || isPreviousOperator)
|
|
{
|
|
isFirstLegalChar = false;
|
|
isPreviousOperator = false;
|
|
|
|
// If the character is a - sign, send negate
|
|
// after sending the next legal character. Send nothing now, or
|
|
// it will be ignored.
|
|
if (NumbersAndOperatorsEnum::Subtract == mappedNumOp)
|
|
{
|
|
sendNegate = true;
|
|
sendCommand = false;
|
|
}
|
|
|
|
// Support (+) sign prefix
|
|
if (NumbersAndOperatorsEnum::Add == mappedNumOp)
|
|
{
|
|
sendCommand = false;
|
|
}
|
|
}
|
|
|
|
if (mappedNumOp != NumbersAndOperatorsEnum::None)
|
|
{
|
|
switch (mappedNumOp)
|
|
{
|
|
// Opening parenthesis starts a new expression and pushes negation state onto the stack
|
|
case NumbersAndOperatorsEnum::OpenParenthesis:
|
|
negateStack.push_back(sendNegate);
|
|
sendNegate = false;
|
|
break;
|
|
|
|
// Closing parenthesis pops the negation state off the stack and sends it down to the calc engine
|
|
case NumbersAndOperatorsEnum::CloseParenthesis:
|
|
if (!negateStack.empty())
|
|
{
|
|
sendNegate = negateStack.back();
|
|
negateStack.pop_back();
|
|
canSendNegate = true;
|
|
}
|
|
else
|
|
{
|
|
// Don't send a closing parenthesis if a matching opening parenthesis hasn't been sent already
|
|
sendCommand = false;
|
|
}
|
|
break;
|
|
|
|
case NumbersAndOperatorsEnum::Zero:
|
|
case NumbersAndOperatorsEnum::One:
|
|
case NumbersAndOperatorsEnum::Two:
|
|
case NumbersAndOperatorsEnum::Three:
|
|
case NumbersAndOperatorsEnum::Four:
|
|
case NumbersAndOperatorsEnum::Five:
|
|
case NumbersAndOperatorsEnum::Six:
|
|
case NumbersAndOperatorsEnum::Seven:
|
|
case NumbersAndOperatorsEnum::Eight:
|
|
case NumbersAndOperatorsEnum::Nine:
|
|
processedDigit = true;
|
|
break;
|
|
|
|
case NumbersAndOperatorsEnum::Add:
|
|
case NumbersAndOperatorsEnum::Subtract:
|
|
case NumbersAndOperatorsEnum::Multiply:
|
|
case NumbersAndOperatorsEnum::Divide:
|
|
isPreviousOperator = true;
|
|
break;
|
|
}
|
|
|
|
if (sendCommand)
|
|
{
|
|
sentEquals = (mappedNumOp == NumbersAndOperatorsEnum::Equals);
|
|
Command cmdenum = ConvertToOperatorsEnum(mappedNumOp);
|
|
m_standardCalculatorManager.SendCommand(cmdenum);
|
|
|
|
// The CalcEngine state machine won't allow the negate command to be sent before any
|
|
// other digits, so instead a flag is set and the command is sent after the first appropriate
|
|
// command.
|
|
if (sendNegate)
|
|
{
|
|
if (canSendNegate)
|
|
{
|
|
Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate);
|
|
m_standardCalculatorManager.SendCommand(cmdNegate);
|
|
}
|
|
|
|
// Can't send negate on a leading zero, so wait until the appropriate time to send it.
|
|
if (NumbersAndOperatorsEnum::Zero != mappedNumOp && NumbersAndOperatorsEnum::Decimal != mappedNumOp)
|
|
{
|
|
sendNegate = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle exponent and exponent sign (...e+... or ...e-...)
|
|
if (mappedNumOp == NumbersAndOperatorsEnum::Exp)
|
|
{
|
|
++it;
|
|
if (!(MapCharacterToButtonId(*it, canSendNegate) == NumbersAndOperatorsEnum::Add))
|
|
{
|
|
Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate);
|
|
m_standardCalculatorManager.SendCommand(cmdNegate);
|
|
}
|
|
}
|
|
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnClearMemoryCommand(
|
|
Object^ parameter)
|
|
{
|
|
m_standardCalculatorManager.MemorizedNumberClearAll();
|
|
|
|
int windowId = Utils::GetWindowId();
|
|
TraceLogger::GetInstance().LogMemoryClearAll(windowId);
|
|
|
|
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(CalculatorResourceKeys::MemoryCleared, m_localizedMemoryCleared);
|
|
Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPinUnpinCommand(Object^ parameter)
|
|
{
|
|
SetViewPinnedState(!IsViewPinned());
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsViewPinned()
|
|
{
|
|
return m_IsCurrentViewPinned;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetViewPinnedState(
|
|
bool pinned)
|
|
{
|
|
IsCurrentViewPinned = pinned;
|
|
}
|
|
|
|
NumbersAndOperatorsEnum StandardCalculatorViewModel::MapCharacterToButtonId(
|
|
const wchar_t ch,
|
|
bool& canSendNegate)
|
|
{
|
|
NumbersAndOperatorsEnum mappedValue = NumbersAndOperatorsEnum::None;
|
|
canSendNegate = false;
|
|
|
|
switch (ch)
|
|
{
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
mappedValue = NumbersAndOperatorsEnum::Zero + static_cast<NumbersAndOperatorsEnum>(ch - L'0');
|
|
canSendNegate = true;
|
|
break;
|
|
|
|
case '*':
|
|
mappedValue = NumbersAndOperatorsEnum::Multiply;
|
|
break;
|
|
|
|
case '+':
|
|
mappedValue = NumbersAndOperatorsEnum::Add;
|
|
break;
|
|
|
|
case '-':
|
|
mappedValue = NumbersAndOperatorsEnum::Subtract;
|
|
break;
|
|
|
|
case '/':
|
|
mappedValue = NumbersAndOperatorsEnum::Divide;
|
|
break;
|
|
|
|
case '=':
|
|
mappedValue = NumbersAndOperatorsEnum::Equals;
|
|
break;
|
|
|
|
case '(':
|
|
mappedValue = NumbersAndOperatorsEnum::OpenParenthesis;
|
|
break;
|
|
|
|
case ')':
|
|
mappedValue = NumbersAndOperatorsEnum::CloseParenthesis;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'A':
|
|
mappedValue = NumbersAndOperatorsEnum::A;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
mappedValue = NumbersAndOperatorsEnum::B;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
mappedValue = NumbersAndOperatorsEnum::C;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
mappedValue = NumbersAndOperatorsEnum::D;
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
// Only allow scientific notation in scientific mode
|
|
if (IsProgrammer)
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::E;
|
|
}
|
|
else
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::Exp;
|
|
}
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
mappedValue = NumbersAndOperatorsEnum::F;
|
|
break;
|
|
default:
|
|
// For the decimalSeparator, we need to respect the user setting.
|
|
if (ch == m_decimalSeparator)
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::Decimal;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (mappedValue == NumbersAndOperatorsEnum::None)
|
|
{
|
|
if (LocalizationSettings::GetInstance().IsLocalizedDigit(ch))
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::Zero + static_cast<NumbersAndOperatorsEnum>(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0'));
|
|
canSendNegate = true;
|
|
}
|
|
}
|
|
|
|
// Negate cannot be sent for leading zeroes
|
|
if (NumbersAndOperatorsEnum::Zero == mappedValue)
|
|
{
|
|
canSendNegate = false;
|
|
}
|
|
|
|
return mappedValue;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryButtonPressed()
|
|
{
|
|
m_standardCalculatorManager.MemorizeNumber();
|
|
|
|
int windowId = Utils::GetWindowId();
|
|
TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer);
|
|
|
|
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::MemorySave,
|
|
m_localizedMemorySavedAutomationFormat,
|
|
m_DisplayValue->Data());
|
|
|
|
Announcement = CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryItemChanged(unsigned int indexOfMemory)
|
|
{
|
|
if (indexOfMemory < MemorizedNumbers->Size)
|
|
{
|
|
MemoryItemViewModel^ memSlot = MemorizedNumbers->GetAt(indexOfMemory);
|
|
String^ localizedValue = memSlot->Value;
|
|
|
|
wstring localizedIndex = to_wstring(indexOfMemory + 1);
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex);
|
|
|
|
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::MemoryItemChanged,
|
|
m_localizedMemoryItemChangedAutomationFormat,
|
|
localizedIndex.c_str(),
|
|
localizedValue->Data());
|
|
|
|
Announcement = CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(announcement);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryItemPressed(Object^ memoryItemPosition)
|
|
{
|
|
if (MemorizedNumbers && MemorizedNumbers->Size > 0)
|
|
{
|
|
auto boxedPosition = safe_cast<Box<int>^>(memoryItemPosition);
|
|
m_standardCalculatorManager.MemorizedNumberLoad(boxedPosition->Value);
|
|
HideMemoryClicked();
|
|
int windowId = Utils::GetWindowId();
|
|
TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryAdd(Object^ memoryItemPosition)
|
|
{
|
|
// M+ will add display to memorylist if memory list is empty.
|
|
int windowId = Utils::GetWindowId();
|
|
|
|
if (MemorizedNumbers)
|
|
{
|
|
auto boxedPosition = safe_cast<Box<int>^>(memoryItemPosition);
|
|
if (MemorizedNumbers->Size > 0)
|
|
{
|
|
TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size);
|
|
TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer);
|
|
}
|
|
else
|
|
{
|
|
TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer);
|
|
}
|
|
m_standardCalculatorManager.MemorizedNumberAdd(boxedPosition->Value);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemorySubtract(Object^ memoryItemPosition)
|
|
{
|
|
int windowId = Utils::GetWindowId();
|
|
|
|
// M- will add negative of displayed number to memorylist if memory list is empty.
|
|
if (MemorizedNumbers)
|
|
{
|
|
auto boxedPosition = safe_cast<Box<int>^>(memoryItemPosition);
|
|
if (MemorizedNumbers->Size > 0)
|
|
{
|
|
TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size);
|
|
TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer);
|
|
}
|
|
else
|
|
{
|
|
TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer);
|
|
}
|
|
m_standardCalculatorManager.MemorizedNumberSubtract(boxedPosition->Value);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryClear(_In_ Object^ memoryItemPosition)
|
|
{
|
|
if (MemorizedNumbers && MemorizedNumbers->Size > 0)
|
|
{
|
|
int windowId = Utils::GetWindowId();
|
|
auto boxedPosition = safe_cast<Box<int>^>(memoryItemPosition);
|
|
|
|
if (boxedPosition->Value >= 0)
|
|
{
|
|
unsigned int unsignedPosition = safe_cast<unsigned int>(boxedPosition->Value);
|
|
m_standardCalculatorManager.MemorizedNumberClear(unsignedPosition);
|
|
|
|
MemorizedNumbers->RemoveAt(unsignedPosition);
|
|
for (unsigned int i = 0; i < MemorizedNumbers->Size; i++)
|
|
{
|
|
MemorizedNumbers->GetAt(i)->Position = i;
|
|
}
|
|
|
|
if (MemorizedNumbers->Size == 0)
|
|
{
|
|
IsMemoryEmpty = true;
|
|
}
|
|
|
|
TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size);
|
|
TraceLogger::GetInstance().DeleteFromMemoryMap(windowId, boxedPosition->Value);
|
|
|
|
wstring localizedIndex = to_wstring(boxedPosition->Value + 1);
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex);
|
|
|
|
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::MemoryItemCleared,
|
|
m_localizedMemoryItemClearedAutomationFormat,
|
|
localizedIndex.c_str());
|
|
|
|
Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement);
|
|
}
|
|
}
|
|
}
|
|
|
|
Array<unsigned char>^ StandardCalculatorViewModel::Serialize()
|
|
{
|
|
DataWriter^ writer = ref new DataWriter();
|
|
writer->WriteUInt32(static_cast<UINT32>(m_CurrentAngleType));
|
|
writer->WriteBoolean(IsFToEChecked);
|
|
writer->WriteBoolean(IsCurrentViewPinned);
|
|
writer->WriteUInt32(static_cast<UINT32>(m_standardCalculatorManager.SerializeSavedDegreeMode()));
|
|
|
|
// Serialize Memory
|
|
vector<long> serializedMemory;
|
|
serializedMemory = m_standardCalculatorManager.GetSerializedMemory();
|
|
size_t lengthOfSerializedMemory = serializedMemory.size();
|
|
writer->WriteUInt32(static_cast<UINT32>(lengthOfSerializedMemory));
|
|
for (auto data : serializedMemory)
|
|
{
|
|
writer->WriteInt32(data);
|
|
}
|
|
|
|
// Serialize Primary Display
|
|
vector<long> serializedPrimaryDisplay = m_standardCalculatorManager.GetSerializedPrimaryDisplay();
|
|
writer->WriteUInt32(static_cast<UINT32>(serializedPrimaryDisplay.size()));
|
|
for (auto data : serializedPrimaryDisplay)
|
|
{
|
|
writer->WriteInt32(data);
|
|
}
|
|
|
|
//For ProgrammerMode
|
|
writer->WriteUInt32(static_cast<UINT32>(CurrentRadixType));
|
|
|
|
//Serialize commands of calculator manager
|
|
vector<unsigned char> serializedCommand = m_standardCalculatorManager.SerializeCommands();
|
|
writer->WriteUInt32(static_cast<UINT32>(serializedCommand.size()));
|
|
writer->WriteBytes(ref new Array<unsigned char>(serializedCommand.data(), static_cast<unsigned int>(serializedCommand.size())));
|
|
|
|
if (IsInError)
|
|
{
|
|
Utils::SerializeCommandsAndTokens(m_tokens, m_commands, writer);
|
|
}
|
|
|
|
//Convert viewmodel data in writer to bytes
|
|
IBuffer^ buffer = writer->DetachBuffer();
|
|
DataReader^ reader = DataReader::FromBuffer(buffer);
|
|
Platform::Array<unsigned char>^ viewModelDataAsBytes = ref new Array<unsigned char>(buffer->Length);
|
|
reader->ReadBytes(viewModelDataAsBytes);
|
|
|
|
// Return byte array
|
|
return viewModelDataAsBytes;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::Deserialize(Array<unsigned char>^ state)
|
|
{
|
|
// Read byte array into a buffer
|
|
DataWriter^ writer = ref new DataWriter();
|
|
writer->WriteBytes(state);
|
|
IBuffer^ buffer = writer->DetachBuffer();
|
|
|
|
// Read view model data
|
|
if (buffer->Length != 0)
|
|
{
|
|
DataReader^ reader = DataReader::FromBuffer(buffer);
|
|
m_CurrentAngleType = ConvertIntegerToNumbersAndOperatorsEnum(reader->ReadUInt32());
|
|
|
|
IsFToEChecked = reader->ReadBoolean();
|
|
IsCurrentViewPinned = reader->ReadBoolean();
|
|
Command serializedDegreeMode = static_cast<Command>(reader->ReadUInt32());
|
|
|
|
m_standardCalculatorManager.SendCommand(serializedDegreeMode);
|
|
|
|
// Deserialize Memory
|
|
UINT32 memoryDataLength = reader->ReadUInt32();
|
|
vector<long> serializedMemory;
|
|
for (unsigned int i = 0; i < memoryDataLength; i++)
|
|
{
|
|
serializedMemory.push_back(reader->ReadInt32());
|
|
}
|
|
m_standardCalculatorManager.DeSerializeMemory(serializedMemory);
|
|
|
|
// Serialize Primary Display
|
|
UINT32 serializedPrimaryDisplayLength = reader->ReadUInt32();
|
|
vector<long> serializedPrimaryDisplay;
|
|
for (unsigned int i = 0; i < serializedPrimaryDisplayLength; i++)
|
|
{
|
|
serializedPrimaryDisplay.push_back(reader->ReadInt32());
|
|
}
|
|
m_standardCalculatorManager.DeSerializePrimaryDisplay(serializedPrimaryDisplay);
|
|
|
|
CurrentRadixType = reader->ReadUInt32();
|
|
//Read command data and Deserialize
|
|
UINT32 modeldatalength = reader->ReadUInt32();
|
|
Array<unsigned char>^ modelDataAsBytes = ref new Array<unsigned char>(modeldatalength);
|
|
reader->ReadBytes(modelDataAsBytes);
|
|
m_standardCalculatorManager.DeSerializeCommands(vector<unsigned char>(modelDataAsBytes->begin(), modelDataAsBytes->end()));
|
|
|
|
// After recalculation. If there is an error then
|
|
// IsInError should be set synchronously.
|
|
if (IsInError)
|
|
{
|
|
shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> commandVector = Utils::DeserializeCommands(reader);
|
|
shared_ptr<CalculatorVector <pair<wstring, int>>> tokenVector = Utils::DeserializeTokens(reader);
|
|
SetExpressionDisplay(tokenVector, commandVector);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPropertyChanged(String^ propertyname)
|
|
{
|
|
if (propertyname == CalculatorViewModelProperties::IsScientific)
|
|
{
|
|
if (IsScientific)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsScientificMode);
|
|
}
|
|
}
|
|
else if (propertyname == CalculatorViewModelProperties::IsProgrammer)
|
|
{
|
|
if (IsProgrammer)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsProgrammerMode);
|
|
}
|
|
}
|
|
else if (propertyname == CalculatorViewModelProperties::IsStandard)
|
|
{
|
|
if (IsStandard)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsStandardMode);
|
|
}
|
|
}
|
|
else if (propertyname == CalculatorViewModelProperties::DisplayValue)
|
|
{
|
|
RaisePropertyChanged(CalculationResultAutomationName_PropertyName);
|
|
Announcement = GetDisplayUpdatedNarratorAnnouncement();
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState)
|
|
{
|
|
// Reset error state so that commands caused by the mode change are still
|
|
// sent if calc is currently in error state.
|
|
IsInError = false;
|
|
|
|
// Setting one of these properties to true will set the others to false.
|
|
switch (targetState)
|
|
{
|
|
case ViewMode::Standard:
|
|
IsStandard = true;
|
|
ResetDisplay();
|
|
SetPrecision(StandardModePrecision);
|
|
UpdateMaxIntDigits();
|
|
break;
|
|
|
|
case ViewMode::Scientific:
|
|
IsScientific = true;
|
|
ResetDisplay();
|
|
SetPrecision(ScientificModePrecision);
|
|
break;
|
|
|
|
case ViewMode::Programmer:
|
|
IsProgrammer = true;
|
|
ResetDisplay();
|
|
SetPrecision(ProgrammerModePrecision);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Platform::String^ StandardCalculatorViewModel::GetRawDisplayValue()
|
|
{
|
|
wstring rawValue;
|
|
|
|
LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue->Data(), DisplayValue->Length(), &rawValue);
|
|
|
|
return ref new Platform::String(rawValue.c_str());
|
|
}
|
|
|
|
// Given a format string, returns a string with the input display value inserted.
|
|
// 'format' is a localized string containing a %1 formatting mark where the display value should be inserted.
|
|
// 'displayValue' is a localized string containing a numerical value to be displayed to the user.
|
|
String^ StandardCalculatorViewModel::GetLocalizedStringFormat(String^ format, String^ displayValue)
|
|
{
|
|
String^ localizedString = ref new String(LocalizationStringUtil::GetLocalizedString(format->Data(), displayValue->Data()).c_str());
|
|
return localizedString;
|
|
}
|
|
|
|
|
|
void StandardCalculatorViewModel::ResetDisplay()
|
|
{
|
|
AreHEXButtonsEnabled = false;
|
|
CurrentRadixType = (int)RADIX_TYPE::DEC_RADIX;
|
|
m_standardCalculatorManager.SetRadix(DEC_RADIX);
|
|
ProgModeRadixChange();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetPrecision(int32_t precision)
|
|
{
|
|
m_standardCalculatorManager.SetPrecision(precision);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SwitchProgrammerModeBase(RADIX_TYPE radixType)
|
|
{
|
|
if (IsInError)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandCLEAR);
|
|
}
|
|
|
|
AreHEXButtonsEnabled = (radixType == RADIX_TYPE::HEX_RADIX);
|
|
CurrentRadixType = (int)radixType;
|
|
m_standardCalculatorManager.SetRadix(radixType);
|
|
ProgModeRadixChange();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetMemorizedNumbersString()
|
|
{
|
|
m_standardCalculatorManager.SetMemorizedNumbersString();
|
|
}
|
|
|
|
ANGLE_TYPE GetAngleTypeFromCommand(Command command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case Command::CommandDEG:
|
|
return ANGLE_DEG;
|
|
case Command::CommandRAD:
|
|
return ANGLE_RAD;
|
|
case Command::CommandGRAD:
|
|
return ANGLE_GRAD;
|
|
default:
|
|
throw ref new Exception(E_FAIL, L"Invalid command type");
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosition, _In_ Command command)
|
|
{
|
|
pair<wstring, int> token;
|
|
bool handleOperand = false;
|
|
int nOpCode = static_cast<int>(command);
|
|
wstring updatedToken = L"";
|
|
|
|
shared_ptr<IExpressionCommand> tokenCommand;
|
|
IFTPlatformException(m_tokens->GetAt(tokenPosition, &token));
|
|
|
|
unsigned int tokenCommandIndex = token.second;
|
|
IFTPlatformException(m_commands->GetAt(tokenCommandIndex, &tokenCommand));
|
|
|
|
if (IsUnaryOp(nOpCode) && command != Command::CommandSIGN)
|
|
{
|
|
int angleCmd = static_cast<int>(m_standardCalculatorManager.GetCurrentDegreeMode());
|
|
ANGLE_TYPE angleType = GetAngleTypeFromCommand(static_cast<Command>(angleCmd));
|
|
|
|
if (IsTrigOp(nOpCode))
|
|
{
|
|
shared_ptr<IUnaryCommand> spUnaryCommand = dynamic_pointer_cast<IUnaryCommand>(tokenCommand);
|
|
spUnaryCommand->SetCommands(angleCmd, nOpCode);
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<IUnaryCommand> spUnaryCommand = dynamic_pointer_cast<IUnaryCommand>(tokenCommand);
|
|
spUnaryCommand->SetCommand(nOpCode);
|
|
}
|
|
|
|
switch (nOpCode)
|
|
{
|
|
case static_cast<int>(Command::CommandASIN) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSIN), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandACOS) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOS), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandATAN) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTAN), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandASINH) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSINH), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandACOSH) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOSH), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandATANH) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTANH), true, angleType);
|
|
break;
|
|
case static_cast<int>(Command::CommandPOWE) :
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandLN), true, angleType);
|
|
break;
|
|
default:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType);
|
|
}
|
|
if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'('))
|
|
{
|
|
wstring chOpenBrace = L"(";
|
|
updatedToken.append(chOpenBrace);
|
|
}
|
|
}
|
|
else if (IsBinOp(nOpCode))
|
|
{
|
|
shared_ptr<IBinaryCommand> spBinaryCommand = dynamic_pointer_cast<IBinaryCommand>(tokenCommand);
|
|
spBinaryCommand->SetCommand(nOpCode);
|
|
updatedToken = CCalcEngine::OpCodeToString(nOpCode);
|
|
}
|
|
else if (IsOpnd(nOpCode) || command == Command::CommandBACK)
|
|
{
|
|
HandleUpdatedOperandData(command);
|
|
handleOperand = true;
|
|
}
|
|
else if (command == Command::CommandSIGN)
|
|
{
|
|
if (tokenCommand->GetCommandType() == CommandType::UnaryCommand)
|
|
{
|
|
shared_ptr<IExpressionCommand> spSignCommand = make_shared<CUnaryCommand>(nOpCode);
|
|
IFTPlatformException(m_commands->InsertAt(tokenCommandIndex + 1, spSignCommand));
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<IOpndCommand> spOpndCommand = dynamic_pointer_cast<IOpndCommand>(tokenCommand);
|
|
spOpndCommand->ToggleSign();
|
|
updatedToken = spOpndCommand->GetToken(m_standardCalculatorManager.DecimalSeparator());
|
|
}
|
|
IsOperandUpdatedUsingViewModel = true;
|
|
}
|
|
|
|
if (!handleOperand)
|
|
{
|
|
IFTPlatformException(m_commands->SetAt(tokenCommandIndex, tokenCommand));
|
|
|
|
pair < wstring, int> selectedToken;
|
|
IFTPlatformException(m_tokens->GetAt(tokenPosition, &selectedToken));
|
|
selectedToken.first = updatedToken;
|
|
IFTPlatformException(m_tokens->SetAt(tokenPosition, selectedToken));
|
|
|
|
DisplayExpressionToken^ displayExpressionToken = ExpressionTokens->GetAt(tokenPosition);
|
|
displayExpressionToken->Token = ref new Platform::String(updatedToken.c_str());
|
|
|
|
// Special casing
|
|
if (command == Command::CommandSIGN && tokenCommand->GetCommandType() == CommandType::UnaryCommand)
|
|
{
|
|
IsEditingEnabled = false;
|
|
Recalculate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::Recalculate(bool fromHistory)
|
|
{
|
|
// Recalculate
|
|
Command currentDegreeMode = m_standardCalculatorManager.GetCurrentDegreeMode();
|
|
shared_ptr <CalculatorVector<shared_ptr<IExpressionCommand>>> savedCommands = make_shared <CalculatorVector<shared_ptr<IExpressionCommand>>>();
|
|
|
|
vector<int> currentCommands;
|
|
unsigned int commandListCount;
|
|
m_commands->GetSize(&commandListCount);
|
|
for (unsigned int i = 0; i < commandListCount; i++)
|
|
{
|
|
shared_ptr<IExpressionCommand> command;
|
|
IFTPlatformException(m_commands->GetAt(i, &command));
|
|
|
|
savedCommands->Append(command);
|
|
CommandType commandType = command->GetCommandType();
|
|
|
|
if (commandType == CommandType::UnaryCommand)
|
|
{
|
|
shared_ptr<IUnaryCommand> spCommand = dynamic_pointer_cast<IUnaryCommand>(command);
|
|
shared_ptr<CalculatorVector<int>> unaryCommands = spCommand->GetCommands();
|
|
unsigned int unaryCommandCount;
|
|
unaryCommands->GetSize(&unaryCommandCount);
|
|
|
|
int nUCode;
|
|
for (unsigned int j = 0; j < unaryCommandCount; ++j)
|
|
{
|
|
IFTPlatformException(unaryCommands->GetAt(j, &nUCode));
|
|
currentCommands.push_back(nUCode);
|
|
}
|
|
}
|
|
|
|
if (commandType == CommandType::BinaryCommand)
|
|
{
|
|
shared_ptr<IBinaryCommand> spCommand = dynamic_pointer_cast<IBinaryCommand>(command);
|
|
currentCommands.push_back(spCommand->GetCommand());
|
|
}
|
|
|
|
if (commandType == CommandType::Parentheses)
|
|
{
|
|
shared_ptr<IParenthesisCommand> spCommand = dynamic_pointer_cast<IParenthesisCommand>(command);
|
|
currentCommands.push_back(spCommand->GetCommand());
|
|
}
|
|
|
|
if (commandType == CommandType::OperandCommand)
|
|
{
|
|
shared_ptr<IOpndCommand> spCommand = dynamic_pointer_cast<IOpndCommand>(command);
|
|
shared_ptr<CalculatorVector<int>> opndCommands = spCommand->GetCommands();
|
|
unsigned int opndCommandCount;
|
|
opndCommands->GetSize(&opndCommandCount);
|
|
bool fNeedIDCSign = spCommand->IsNegative();
|
|
|
|
int nOCode;
|
|
for (unsigned int j = 0; j < opndCommandCount; ++j)
|
|
{
|
|
IFTPlatformException(opndCommands->GetAt(j, &nOCode));
|
|
currentCommands.push_back(nOCode);
|
|
|
|
if (fNeedIDCSign && nOCode != IDC_0)
|
|
{
|
|
currentCommands.push_back(static_cast<int>(CalculationManager::Command::CommandSIGN));
|
|
fNeedIDCSign = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
shared_ptr<CalculatorVector<pair<wstring, int>>> savedTokens = make_shared<CalculatorVector<pair<wstring, int>>>();
|
|
|
|
unsigned int tokenCount;
|
|
IFTPlatformException(m_tokens->GetSize(&tokenCount));
|
|
|
|
for (unsigned int i = 0; i < tokenCount; ++i)
|
|
{
|
|
pair<wstring, int> currentToken;
|
|
IFTPlatformException(m_tokens->GetAt(i, ¤tToken));
|
|
savedTokens->Append(currentToken);
|
|
}
|
|
|
|
m_standardCalculatorManager.Reset(false);
|
|
if (IsScientific)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::ModeScientific);
|
|
}
|
|
|
|
if (IsFToEChecked)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandFE);
|
|
}
|
|
|
|
m_standardCalculatorManager.SendCommand(currentDegreeMode);
|
|
size_t currentCommandsSize = currentCommands.size();
|
|
for (size_t i = 0; i < currentCommandsSize; i++)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(static_cast<CalculationManager::Command>(currentCommands[i]));
|
|
}
|
|
|
|
if (fromHistory) // This is for the cases where the expression is loaded from history
|
|
{
|
|
// To maintain F-E state of the engine, as the last operand hasn't reached engine by now
|
|
m_standardCalculatorManager.SendCommand(Command::CommandFE);
|
|
m_standardCalculatorManager.SendCommand(Command::CommandFE);
|
|
}
|
|
|
|
// After recalculation. If there is an error then
|
|
// IsInError should be set synchronously.
|
|
if (IsInError)
|
|
{
|
|
SetExpressionDisplay(savedTokens, savedCommands);
|
|
}
|
|
}
|
|
|
|
CommandType StandardCalculatorViewModel::GetSelectedTokenType(_In_ unsigned int tokenPosition)
|
|
{
|
|
pair<wstring, int>token;
|
|
shared_ptr<IExpressionCommand> tokenCommand;
|
|
IFTPlatformException(m_tokens->GetAt(tokenPosition, &token));
|
|
|
|
unsigned int tokenCommandIndex = token.second;
|
|
IFTPlatformException(m_commands->GetAt(tokenCommandIndex, &tokenCommand));
|
|
|
|
return tokenCommand->GetCommandType();
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsOpnd(int nOpCode)
|
|
{
|
|
|
|
static Command opnd[] = {
|
|
Command::Command0,
|
|
Command::Command1,
|
|
Command::Command2,
|
|
Command::Command3,
|
|
Command::Command4,
|
|
Command::Command5,
|
|
Command::Command6,
|
|
Command::Command7,
|
|
Command::Command8,
|
|
Command::Command9,
|
|
Command::CommandPNT
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(opnd); i++)
|
|
{
|
|
if (nOpCode == static_cast<int>(opnd[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsUnaryOp(int nOpCode)
|
|
{
|
|
static Command unaryOp[] = {
|
|
Command::CommandSQRT,
|
|
Command::CommandFAC,
|
|
Command::CommandSQR,
|
|
Command::CommandLOG,
|
|
Command::CommandPOW10,
|
|
Command::CommandPOWE,
|
|
Command::CommandLN,
|
|
Command::CommandREC,
|
|
Command::CommandSIGN,
|
|
Command::CommandSINH,
|
|
Command::CommandASINH,
|
|
Command::CommandCOSH,
|
|
Command::CommandACOSH,
|
|
Command::CommandTANH,
|
|
Command::CommandATANH,
|
|
Command::CommandCUB
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(unaryOp); i++)
|
|
{
|
|
if (nOpCode == static_cast<int>(unaryOp[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (IsTrigOp(nOpCode))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsTrigOp(int nOpCode)
|
|
{
|
|
static Command trigOp[] = {
|
|
Command::CommandSIN,
|
|
Command::CommandCOS,
|
|
Command::CommandTAN,
|
|
Command::CommandASIN,
|
|
Command::CommandACOS,
|
|
Command::CommandATAN
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(trigOp); i++)
|
|
{
|
|
if (nOpCode == static_cast<int>(trigOp[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsBinOp(int nOpCode)
|
|
{
|
|
static Command binOp[] = {
|
|
Command::CommandADD,
|
|
Command::CommandSUB,
|
|
Command::CommandMUL,
|
|
Command::CommandDIV,
|
|
Command::CommandEXP,
|
|
Command::CommandROOT,
|
|
Command::CommandMOD,
|
|
Command::CommandPWR
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(binOp); i++)
|
|
{
|
|
if (nOpCode == static_cast<int>(binOp[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsRecoverableCommand(int nOpCode)
|
|
{
|
|
if (IsOpnd(nOpCode))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Programmer mode, bit flipping
|
|
int minBinPos = static_cast<int>(Command::CommandBINEDITSTART);
|
|
int maxBinPos = static_cast<int>(Command::CommandBINEDITEND);
|
|
if (minBinPos <= nOpCode && nOpCode <= maxBinPos)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static Command recoverableCommands[] = {
|
|
Command::CommandA,
|
|
Command::CommandB,
|
|
Command::CommandC,
|
|
Command::CommandD,
|
|
Command::CommandE,
|
|
Command::CommandF
|
|
};
|
|
|
|
for (int i = 0; i < ARRAYSIZE(recoverableCommands); i++)
|
|
{
|
|
if (nOpCode == static_cast<int>(recoverableCommands[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t StandardCalculatorViewModel::LengthWithoutPadding(wstring str)
|
|
{
|
|
size_t count = 0;
|
|
for (size_t i = 0; i < str.length(); i++)
|
|
{
|
|
if (str[i] != L' ')
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
wstring StandardCalculatorViewModel::AddPadding(wstring binaryString)
|
|
{
|
|
if (LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(binaryString) == L"0")
|
|
{
|
|
return binaryString;
|
|
}
|
|
size_t pad = 4 - LengthWithoutPadding(binaryString) % 4;
|
|
if (pad == 4)
|
|
{
|
|
pad = 0;
|
|
}
|
|
wstring padString = L"";
|
|
for (size_t i = 0; i < pad; i++)
|
|
{
|
|
padString += L"0";
|
|
}
|
|
return padString + binaryString;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay()
|
|
{
|
|
wstring hexDisplayString;
|
|
wstring decimalDisplayString;
|
|
wstring octalDisplayString;
|
|
wstring binaryDisplayString;
|
|
if (!IsInError)
|
|
{
|
|
// we want the precision to be set to maximum value so that the autoconversions result as desired
|
|
int32_t precision = 64;
|
|
if (m_standardCalculatorManager.GetResultForRadix(16, precision) == L"")
|
|
{
|
|
hexDisplayString = DisplayValue->Data();
|
|
decimalDisplayString = DisplayValue->Data();
|
|
octalDisplayString = DisplayValue->Data();
|
|
binaryDisplayString = DisplayValue->Data();
|
|
}
|
|
else
|
|
{
|
|
hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision);
|
|
decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision);
|
|
octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision);
|
|
binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision);
|
|
}
|
|
}
|
|
const auto& localizer = LocalizationSettings::GetInstance();
|
|
binaryDisplayString = AddPadding(binaryDisplayString);
|
|
|
|
localizer.LocalizeDisplayValue(&hexDisplayString);
|
|
localizer.LocalizeDisplayValue(&decimalDisplayString);
|
|
localizer.LocalizeDisplayValue(&octalDisplayString);
|
|
localizer.LocalizeDisplayValue(&binaryDisplayString);
|
|
|
|
HexDisplayValue = Utils::LRO + ref new Platform::String(hexDisplayString.c_str()) + Utils::PDF;
|
|
DecimalDisplayValue = Utils::LRO + ref new Platform::String(decimalDisplayString.c_str()) + Utils::PDF;
|
|
OctalDisplayValue = Utils::LRO + ref new Platform::String(octalDisplayString.c_str()) + Utils::PDF;
|
|
BinaryDisplayValue = Utils::LRO + ref new Platform::String(binaryDisplayString.c_str()) + Utils::PDF;
|
|
HexDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedHexaDecimalAutomationFormat, GetNarratorStringReadRawNumbers(HexDisplayValue));
|
|
DecDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedDecimalAutomationFormat, DecimalDisplayValue);
|
|
OctDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedOctalAutomationFormat, GetNarratorStringReadRawNumbers(OctalDisplayValue));
|
|
BinDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedBinaryAutomationFormat, GetNarratorStringReadRawNumbers(BinaryDisplayValue));
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SwitchAngleType(NumbersAndOperatorsEnum num)
|
|
{
|
|
OnButtonPressed(num);
|
|
}
|
|
|
|
NumbersAndOperatorsEnum StandardCalculatorViewModel::ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter)
|
|
{
|
|
NumbersAndOperatorsEnum angletype;
|
|
switch (parameter)
|
|
{
|
|
case 321:
|
|
angletype = NumbersAndOperatorsEnum::Degree;
|
|
break;
|
|
case 322:
|
|
angletype = NumbersAndOperatorsEnum::Radians;
|
|
break;
|
|
case 323:
|
|
angletype = NumbersAndOperatorsEnum::Grads;
|
|
break;
|
|
default:
|
|
angletype = NumbersAndOperatorsEnum::Degree;
|
|
};
|
|
return angletype;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::UpdateOperand(int pos, String^ text)
|
|
{
|
|
pair<wstring, int> p;
|
|
m_tokens->GetAt(pos, &p);
|
|
|
|
String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(text->Data());
|
|
p.first = englishString->Data();
|
|
|
|
int commandPos = p.second;
|
|
shared_ptr<IExpressionCommand> exprCmd;
|
|
m_commands->GetAt(commandPos, &exprCmd);
|
|
auto operandCommand = std::dynamic_pointer_cast<IOpndCommand>(exprCmd);
|
|
|
|
if (operandCommand != nullptr)
|
|
{
|
|
shared_ptr<CalculatorVector<int>> commands = make_shared<CalculatorVector<int>>();
|
|
size_t length = p.first.length();
|
|
if (length > 0)
|
|
{
|
|
int num = 0;
|
|
for (unsigned int i = 0; i < length; ++i)
|
|
{
|
|
if (p.first[i] == L'.')
|
|
{
|
|
num = static_cast<int>(Command::CommandPNT);
|
|
}
|
|
else if (p.first[i] == L'e')
|
|
{
|
|
num = static_cast<int>(Command::CommandEXP);
|
|
}
|
|
else if (p.first[i] == L'-')
|
|
{
|
|
num = static_cast<int>(Command::CommandSIGN);
|
|
|
|
if (i == 0)
|
|
{
|
|
shared_ptr<IOpndCommand> spOpndCommand = dynamic_pointer_cast<IOpndCommand>(exprCmd);
|
|
if (!spOpndCommand->IsNegative())
|
|
{
|
|
spOpndCommand->ToggleSign();
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
num = static_cast<int>(p.first[i]) - ASCII_0;
|
|
num += IDC_0;
|
|
if (num == static_cast<int>(Command::CommandMPLUS))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
commands->Append(num);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
commands->Append(0);
|
|
}
|
|
operandCommand->SetCommands(commands);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::UpdatecommandsInRecordingMode()
|
|
{
|
|
vector<unsigned char> savedCommands = m_standardCalculatorManager.GetSavedCommands();
|
|
shared_ptr<CalculatorVector<int>> commands = make_shared<CalculatorVector<int>>();
|
|
bool isDecimal = false;
|
|
bool isNegative = false;
|
|
bool isExpMode = false;
|
|
bool ePlusMode = false;
|
|
bool eMinusMode = false;
|
|
|
|
int num = 0;
|
|
Command val;
|
|
for (unsigned int i = 0; i < savedCommands.size(); ++i)
|
|
{
|
|
val = safe_cast<Command>(savedCommands[i]);
|
|
num = static_cast<int>(val);
|
|
if (val == Command::CommandSIGN)
|
|
{
|
|
isNegative = true;
|
|
continue;
|
|
}
|
|
else if ((val >= Command::Command0 && val <= Command::Command9))
|
|
{
|
|
}
|
|
else if (val == Command::CommandPNT)
|
|
{
|
|
isDecimal = true;
|
|
}
|
|
else if (val == Command::CommandEXP)
|
|
{
|
|
isExpMode = true;
|
|
}
|
|
else if (isExpMode && !ePlusMode && (val == Command::CommandMPLUS))
|
|
{
|
|
ePlusMode = true;
|
|
continue;
|
|
}
|
|
else if (isExpMode && !eMinusMode && (val == Command::CommandMMINUS))
|
|
{
|
|
eMinusMode = true;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//reset all vars
|
|
isDecimal = false;
|
|
isNegative = false;
|
|
isExpMode = false;
|
|
ePlusMode = false;
|
|
eMinusMode = false;
|
|
commands->Clear();
|
|
continue;
|
|
}
|
|
commands->Append(num);
|
|
}
|
|
|
|
unsigned int size = 0;
|
|
commands->GetSize(&size);
|
|
if (size > 0)
|
|
{
|
|
shared_ptr<IOpndCommand> sp = make_shared<COpndCommand>(commands, isNegative, isDecimal, isExpMode);
|
|
m_commands->Append(sp);
|
|
}
|
|
Recalculate();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMaxDigitsReached()
|
|
{
|
|
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::MaxDigitsReachedFormat,
|
|
m_localizedMaxDigitsReachedAutomationFormat,
|
|
m_CalculationResultAutomationName->Data());
|
|
|
|
Announcement = CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnBinaryOperatorReceived()
|
|
{
|
|
Announcement = GetDisplayUpdatedNarratorAnnouncement();
|
|
}
|
|
|
|
NarratorAnnouncement^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnnouncement()
|
|
{
|
|
String^ announcement;
|
|
if (m_feedbackForButtonPress == nullptr || m_feedbackForButtonPress->IsEmpty())
|
|
{
|
|
announcement = m_CalculationResultAutomationName;
|
|
}
|
|
else
|
|
{
|
|
announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::ButtonPressFeedbackFormat,
|
|
m_localizedButtonPressFeedbackAutomationFormat,
|
|
m_CalculationResultAutomationName->Data(),
|
|
m_feedbackForButtonPress->Data());
|
|
}
|
|
|
|
// Make sure we don't accidentally repeat an announcement.
|
|
m_feedbackForButtonPress = nullptr;
|
|
|
|
return CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(announcement);
|
|
}
|