1860 lines
64 KiB
C++
1860 lines
64 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 concurrency;
|
|
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;
|
|
using namespace concurrency;
|
|
|
|
constexpr int StandardModePrecision = 16;
|
|
constexpr int ScientificModePrecision = 32;
|
|
constexpr int ProgrammerModePrecision = 64;
|
|
|
|
namespace
|
|
{
|
|
StringReference IsStandardPropertyName(L"IsStandard");
|
|
StringReference IsScientificPropertyName(L"IsScientific");
|
|
StringReference IsProgrammerPropertyName(L"IsProgrammer");
|
|
StringReference IsAlwaysOnTopPropertyName(L"IsAlwaysOnTop");
|
|
StringReference DisplayValuePropertyName(L"DisplayValue");
|
|
StringReference CalculationResultAutomationNamePropertyName(L"CalculationResultAutomationName");
|
|
StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked");
|
|
}
|
|
|
|
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 OpenParenthesisCountAutomationFormat(L"Format_OpenParenthesisCountAutomationNamePrefix");
|
|
StringReference NoParenthesisAdded(L"NoRightParenthesisAdded_Announcement");
|
|
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_BinaryDigits(ref new Vector<bool>(64, false))
|
|
, m_standardCalculatorManager(&m_calculatorDisplay, &m_resourceProvider)
|
|
, m_ExpressionTokens(ref new Vector<DisplayExpressionToken ^>())
|
|
, m_MemorizedNumbers(ref new Vector<MemoryItemViewModel ^>())
|
|
, m_IsMemoryEmpty(true)
|
|
, m_IsFToEChecked(false)
|
|
, m_IsShiftProgrammerChecked(false)
|
|
, m_valueBitLength(BitLength::BitLengthQWord)
|
|
, m_isBitFlipChecked(false)
|
|
, m_IsBinaryBitFlippingEnabled(false)
|
|
, m_CurrentRadixType(NumberBase::DecBase)
|
|
, m_CurrentAngleType(NumbersAndOperatorsEnum::Degree)
|
|
, m_Announcement(nullptr)
|
|
, m_OpenParenthesisCount(0)
|
|
, m_feedbackForButtonPress(nullptr)
|
|
, m_isRtlLanguage(false)
|
|
, m_localizedMaxDigitsReachedAutomationFormat(nullptr)
|
|
, m_localizedButtonPressFeedbackAutomationFormat(nullptr)
|
|
, m_localizedMemorySavedAutomationFormat(nullptr)
|
|
, m_localizedMemoryItemChangedAutomationFormat(nullptr)
|
|
, m_localizedMemoryItemClearedAutomationFormat(nullptr)
|
|
, m_localizedMemoryCleared(nullptr)
|
|
, m_localizedOpenParenthesisCountChangedAutomationFormat(nullptr)
|
|
, m_localizedNoRightParenthesisAddedFormat(nullptr)
|
|
, m_TokenPosition(-1)
|
|
, m_isLastOperationHistoryLoad(false)
|
|
{
|
|
WeakReference calculatorViewModel(this);
|
|
auto appResourceProvider = AppResourceProvider::GetInstance();
|
|
m_calculatorDisplay.SetCallback(calculatorViewModel);
|
|
m_expressionAutomationNameFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorExpression);
|
|
m_localizedCalculationResultAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorResults);
|
|
m_localizedCalculationResultDecimalAutomationFormat =
|
|
appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorResults_DecimalSeparator_Announced);
|
|
m_localizedHexaDecimalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::HexButton);
|
|
m_localizedDecimalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::DecButton);
|
|
m_localizedOctalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::OctButton);
|
|
m_localizedBinaryAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::BinButton);
|
|
|
|
// 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;
|
|
}
|
|
|
|
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 != NumberBase::DecBase)
|
|
{
|
|
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_ String ^ displayStringValue, _In_ bool isError)
|
|
{
|
|
String ^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue->Data(), isError);
|
|
|
|
// Set this variable before the DisplayValue is modified, Otherwise the DisplayValue will
|
|
// not match what the narrator is saying
|
|
m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue->Data(), localizedDisplayStringValue, isError);
|
|
|
|
AreAlwaysOnTopResultsUpdated = false;
|
|
if (DisplayValue != localizedDisplayStringValue)
|
|
{
|
|
DisplayValue = localizedDisplayStringValue;
|
|
AreAlwaysOnTopResultsUpdated = true;
|
|
}
|
|
|
|
IsInError = isError;
|
|
|
|
if (IsProgrammer)
|
|
{
|
|
UpdateProgrammerPanelDisplay();
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::DisplayPasteError()
|
|
{
|
|
m_standardCalculatorManager.DisplayPasteError();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetParenthesisCount(_In_ unsigned int parenthesisCount)
|
|
{
|
|
if (m_OpenParenthesisCount == parenthesisCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
OpenParenthesisCount = parenthesisCount;
|
|
if (IsProgrammer || IsScientific)
|
|
{
|
|
SetOpenParenthesisCountNarratorAnnouncement();
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetOpenParenthesisCountNarratorAnnouncement()
|
|
{
|
|
wstring localizedParenthesisCount = to_wstring(m_OpenParenthesisCount).c_str();
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount);
|
|
|
|
if (m_localizedOpenParenthesisCountChangedAutomationFormat == nullptr)
|
|
{
|
|
m_localizedOpenParenthesisCountChangedAutomationFormat =
|
|
AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::OpenParenthesisCountAutomationFormat);
|
|
}
|
|
String ^ announcement =
|
|
LocalizationStringUtil::GetLocalizedString(m_localizedOpenParenthesisCountChangedAutomationFormat, StringReference(localizedParenthesisCount.c_str()));
|
|
|
|
Announcement = CalculatorAnnouncement::GetOpenParenthesisCountChangedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnNoRightParenAdded()
|
|
{
|
|
SetNoParenAddedNarratorAnnouncement();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetNoParenAddedNarratorAnnouncement()
|
|
{
|
|
if (m_localizedNoRightParenthesisAddedFormat == nullptr)
|
|
{
|
|
m_localizedNoRightParenthesisAddedFormat =
|
|
AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::NoParenthesisAdded);
|
|
}
|
|
|
|
Announcement = CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(m_localizedNoRightParenthesisAddedFormat);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetExpressionDisplay(
|
|
_Inout_ shared_ptr<std::vector<pair<wstring, int>>> const& tokens,
|
|
_Inout_ shared_ptr<std::vector<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<vector<pair<wstring, int>>> const& tokens,
|
|
_Inout_ shared_ptr<vector<shared_ptr<IExpressionCommand>>> const& commands)
|
|
{
|
|
m_tokens = make_shared<vector<pair<wstring, int>>>(*tokens);
|
|
m_commands = make_shared<vector<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<vector<pair<wstring, int>>> const& tokens)
|
|
{
|
|
AreTokensUpdated = false;
|
|
|
|
const size_t nTokens = tokens->size();
|
|
|
|
if (nTokens == 0)
|
|
{
|
|
m_ExpressionTokens->Clear();
|
|
return;
|
|
}
|
|
|
|
const auto& localizer = LocalizationSettings::GetInstance();
|
|
|
|
const wstring separator = L" ";
|
|
for (unsigned int i = 0; i < nTokens; ++i)
|
|
{
|
|
auto currentToken = (*tokens)[i];
|
|
|
|
Common::TokenType type;
|
|
bool isEditable = currentToken.second != -1;
|
|
localizer.LocalizeDisplayValue(&(currentToken.first));
|
|
|
|
if (!isEditable)
|
|
{
|
|
type = currentToken.first == separator ? TokenType::Separator : TokenType::Operator;
|
|
}
|
|
else
|
|
{
|
|
const shared_ptr<IExpressionCommand>& command = m_commands->at(currentToken.second);
|
|
type = command->GetCommandType() == CommandType::OperandCommand ? TokenType::Operand : TokenType::Operator;
|
|
}
|
|
|
|
auto currentTokenString = StringReference(currentToken.first.c_str());
|
|
if (i < m_ExpressionTokens->Size)
|
|
{
|
|
auto existingItem = m_ExpressionTokens->GetAt(i);
|
|
if (type == existingItem->Type && existingItem->Token->Equals(currentTokenString))
|
|
{
|
|
existingItem->TokenPosition = i;
|
|
existingItem->IsTokenEditable = isEditable;
|
|
existingItem->CommandIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type);
|
|
m_ExpressionTokens->InsertAt(i, expressionToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type);
|
|
m_ExpressionTokens->Append(expressionToken);
|
|
}
|
|
}
|
|
|
|
while (m_ExpressionTokens->Size != nTokens)
|
|
{
|
|
m_ExpressionTokens->RemoveAtEnd();
|
|
}
|
|
}
|
|
|
|
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 = IsAlwaysOnTop;
|
|
|
|
// 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)
|
|
{
|
|
delete[] temp;
|
|
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::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);
|
|
|
|
if (IsInError)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandCLEAR);
|
|
|
|
if (!IsRecoverableCommand(static_cast<Command>(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::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;
|
|
}
|
|
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(numOpEnum, GetCalculatorMode());
|
|
m_standardCalculatorManager.SendCommand(cmdenum);
|
|
}
|
|
}
|
|
}
|
|
|
|
RADIX_TYPE StandardCalculatorViewModel::GetRadixTypeFromNumberBase(NumberBase base)
|
|
{
|
|
switch (base)
|
|
{
|
|
case NumberBase::BinBase:
|
|
return RADIX_TYPE::BIN_RADIX;
|
|
case NumberBase::HexBase:
|
|
return RADIX_TYPE::HEX_RADIX;
|
|
case NumberBase::OctBase:
|
|
return RADIX_TYPE::OCT_RADIX;
|
|
default:
|
|
return RADIX_TYPE::DEC_RADIX;
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnCopyCommand(Object ^ parameter)
|
|
{
|
|
CopyPasteManager::CopyToClipboard(GetRawDisplayValue());
|
|
|
|
String ^ announcement = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::DisplayCopied);
|
|
Announcement = CalculatorAnnouncement::GetDisplayCopiedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter)
|
|
{
|
|
auto that(this);
|
|
ViewMode mode;
|
|
BitLength bitLengthType = BitLength::BitLengthUnknown;
|
|
NumberBase numberBase = NumberBase::Unknown;
|
|
if (IsScientific)
|
|
{
|
|
mode = ViewMode::Scientific;
|
|
}
|
|
else if (IsProgrammer)
|
|
{
|
|
mode = ViewMode::Programmer;
|
|
bitLengthType = m_valueBitLength;
|
|
numberBase = CurrentRadixType;
|
|
}
|
|
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
|
|
create_task(CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), numberBase, bitLengthType))
|
|
.then([that, mode](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current());
|
|
}
|
|
|
|
CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation)
|
|
{
|
|
return safe_cast<Command>(operation);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPaste(String ^ pastedString)
|
|
{
|
|
// If pastedString is invalid("NoOp") then display pasteError else process the string
|
|
if (CopyPasteManager::IsErrorMessage(pastedString))
|
|
{
|
|
this->DisplayPasteError();
|
|
return;
|
|
}
|
|
|
|
TraceLogger::GetInstance()->LogInputPasted(GetCalculatorMode());
|
|
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;
|
|
auto buttonInfo = MapCharacterToButtonId(*it);
|
|
|
|
NumbersAndOperatorsEnum mappedNumOp = buttonInfo.buttonId;
|
|
bool canSendNegate = buttonInfo.canSendNegate;
|
|
|
|
if (mappedNumOp == NumbersAndOperatorsEnum::None)
|
|
{
|
|
++it;
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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-... or ...e...)
|
|
if (mappedNumOp == NumbersAndOperatorsEnum::Exp)
|
|
{
|
|
// Check the following item
|
|
switch (MapCharacterToButtonId(*(it + 1)).buttonId)
|
|
{
|
|
case NumbersAndOperatorsEnum::Subtract:
|
|
{
|
|
Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate);
|
|
m_standardCalculatorManager.SendCommand(cmdNegate);
|
|
++it;
|
|
}
|
|
break;
|
|
case NumbersAndOperatorsEnum::Add:
|
|
{
|
|
// Nothing to do, skip to the next item
|
|
++it;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnClearMemoryCommand(Object ^ parameter)
|
|
{
|
|
m_standardCalculatorManager.MemorizedNumberClearAll();
|
|
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode());
|
|
|
|
if (m_localizedMemoryCleared == nullptr)
|
|
{
|
|
m_localizedMemoryCleared = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryCleared);
|
|
}
|
|
Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(m_localizedMemoryCleared);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPinUnpinCommand(Object ^ parameter)
|
|
{
|
|
SetViewPinnedState(!IsViewPinned());
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsViewPinned()
|
|
{
|
|
return m_IsCurrentViewPinned;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetViewPinnedState(bool pinned)
|
|
{
|
|
IsCurrentViewPinned = pinned;
|
|
}
|
|
|
|
ButtonInfo StandardCalculatorViewModel::MapCharacterToButtonId(char16 ch)
|
|
{
|
|
ButtonInfo result;
|
|
result.buttonId = NumbersAndOperatorsEnum::None;
|
|
result.canSendNegate = false;
|
|
|
|
switch (ch)
|
|
{
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
result.buttonId = NumbersAndOperatorsEnum::Zero + static_cast<NumbersAndOperatorsEnum>(ch - L'0');
|
|
result.canSendNegate = true;
|
|
break;
|
|
|
|
case '*':
|
|
result.buttonId = NumbersAndOperatorsEnum::Multiply;
|
|
break;
|
|
|
|
case '+':
|
|
result.buttonId = NumbersAndOperatorsEnum::Add;
|
|
break;
|
|
|
|
case '-':
|
|
result.buttonId = NumbersAndOperatorsEnum::Subtract;
|
|
break;
|
|
|
|
case '/':
|
|
result.buttonId = NumbersAndOperatorsEnum::Divide;
|
|
break;
|
|
|
|
case '^':
|
|
if (IsScientific)
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::XPowerY;
|
|
}
|
|
break;
|
|
|
|
case '%':
|
|
if (IsScientific || IsProgrammer)
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::Mod;
|
|
}
|
|
break;
|
|
|
|
case '=':
|
|
result.buttonId = NumbersAndOperatorsEnum::Equals;
|
|
break;
|
|
|
|
case '(':
|
|
result.buttonId = NumbersAndOperatorsEnum::OpenParenthesis;
|
|
break;
|
|
|
|
case ')':
|
|
result.buttonId = NumbersAndOperatorsEnum::CloseParenthesis;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'A':
|
|
result.buttonId = NumbersAndOperatorsEnum::A;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
result.buttonId = NumbersAndOperatorsEnum::B;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
result.buttonId = NumbersAndOperatorsEnum::C;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
result.buttonId = NumbersAndOperatorsEnum::D;
|
|
break;
|
|
case 'e':
|
|
case 'E':
|
|
// Only allow scientific notation in scientific mode
|
|
if (IsProgrammer)
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::E;
|
|
}
|
|
else
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::Exp;
|
|
}
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
result.buttonId = NumbersAndOperatorsEnum::F;
|
|
break;
|
|
default:
|
|
// For the decimalSeparator, we need to respect the user setting.
|
|
if (ch == m_decimalSeparator)
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::Decimal;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (result.buttonId == NumbersAndOperatorsEnum::None)
|
|
{
|
|
if (LocalizationSettings::GetInstance().IsLocalizedDigit(ch))
|
|
{
|
|
result.buttonId = NumbersAndOperatorsEnum::Zero
|
|
+ static_cast<NumbersAndOperatorsEnum>(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0'));
|
|
result.canSendNegate = true;
|
|
}
|
|
}
|
|
|
|
// Negate cannot be sent for leading zeroes
|
|
if (NumbersAndOperatorsEnum::Zero == result.buttonId)
|
|
{
|
|
result.canSendNegate = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
void StandardCalculatorViewModel::OnInputChanged()
|
|
{
|
|
IsInputEmpty = m_standardCalculatorManager.IsInputEmpty();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryButtonPressed()
|
|
{
|
|
m_standardCalculatorManager.MemorizeNumber();
|
|
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::Memory, GetCalculatorMode());
|
|
|
|
if (m_localizedMemorySavedAutomationFormat == nullptr)
|
|
{
|
|
m_localizedMemorySavedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemorySave);
|
|
}
|
|
String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedMemorySavedAutomationFormat, m_DisplayValue);
|
|
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);
|
|
|
|
if (m_localizedMemoryItemChangedAutomationFormat == nullptr)
|
|
{
|
|
m_localizedMemoryItemChangedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryItemChanged);
|
|
}
|
|
String ^ announcement =
|
|
LocalizationStringUtil::GetLocalizedString(m_localizedMemoryItemChangedAutomationFormat, StringReference(localizedIndex.c_str()), localizedValue);
|
|
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();
|
|
|
|
auto mode = IsStandard ? ViewMode::Standard : IsScientific ? ViewMode::Scientific : ViewMode::Programmer;
|
|
TraceLogger::GetInstance()->LogMemoryItemLoad(mode, MemorizedNumbers->Size, boxedPosition->Value);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryAdd(Object ^ memoryItemPosition)
|
|
{
|
|
// M+ will add display to memorylist if memory list is empty.
|
|
|
|
if (MemorizedNumbers)
|
|
{
|
|
auto boxedPosition = safe_cast<Box<int> ^>(memoryItemPosition);
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryAdd, GetCalculatorMode());
|
|
m_standardCalculatorManager.MemorizedNumberAdd(boxedPosition->Value);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemorySubtract(Object ^ memoryItemPosition)
|
|
{
|
|
// M- will add negative of displayed number to memorylist if memory list is empty.
|
|
if (MemorizedNumbers)
|
|
{
|
|
auto boxedPosition = safe_cast<Box<int> ^>(memoryItemPosition);
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemorySubtract, GetCalculatorMode());
|
|
m_standardCalculatorManager.MemorizedNumberSubtract(boxedPosition->Value);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryClear(_In_ Object ^ memoryItemPosition)
|
|
{
|
|
if (MemorizedNumbers && MemorizedNumbers->Size > 0)
|
|
{
|
|
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()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode());
|
|
|
|
wstring localizedIndex = to_wstring(boxedPosition->Value + 1);
|
|
LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex);
|
|
|
|
if (m_localizedMemoryItemClearedAutomationFormat == nullptr)
|
|
{
|
|
m_localizedMemoryItemClearedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryItemCleared);
|
|
}
|
|
String ^ announcement =
|
|
LocalizationStringUtil::GetLocalizedString(m_localizedMemoryItemClearedAutomationFormat, StringReference(localizedIndex.c_str()));
|
|
|
|
Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnPropertyChanged(String ^ propertyname)
|
|
{
|
|
if (propertyname == IsScientificPropertyName)
|
|
{
|
|
if (IsScientific)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsScientificMode);
|
|
}
|
|
}
|
|
else if (propertyname == IsProgrammerPropertyName)
|
|
{
|
|
if (IsProgrammer)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsProgrammerMode);
|
|
}
|
|
}
|
|
else if (propertyname == IsStandardPropertyName)
|
|
{
|
|
if (IsStandard)
|
|
{
|
|
OnButtonPressed(NumbersAndOperatorsEnum::IsStandardMode);
|
|
}
|
|
}
|
|
else if (propertyname == DisplayValuePropertyName)
|
|
{
|
|
RaisePropertyChanged(CalculationResultAutomationNamePropertyName);
|
|
Announcement = GetDisplayUpdatedNarratorAnnouncement();
|
|
}
|
|
else if (propertyname == IsBitFlipCheckedPropertyName)
|
|
{
|
|
TraceLogger::GetInstance()->UpdateButtonUsage(
|
|
IsBitFlipChecked ? NumbersAndOperatorsEnum::BitflipButton : NumbersAndOperatorsEnum::FullKeypadButton, ViewMode::Programmer);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
String ^ StandardCalculatorViewModel::GetRawDisplayValue()
|
|
{
|
|
if (IsInError)
|
|
{
|
|
return DisplayValue;
|
|
}
|
|
else
|
|
{
|
|
return LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue);
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
return LocalizationStringUtil::GetLocalizedString(format, displayValue);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::ResetDisplay()
|
|
{
|
|
AreHEXButtonsEnabled = false;
|
|
CurrentRadixType = NumberBase::DecBase;
|
|
m_standardCalculatorManager.SetRadix(DEC_RADIX);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetPrecision(int32_t precision)
|
|
{
|
|
m_standardCalculatorManager.SetPrecision(precision);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SwitchProgrammerModeBase(NumberBase numberBase)
|
|
{
|
|
if (IsInError)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandCLEAR);
|
|
}
|
|
|
|
AreHEXButtonsEnabled = numberBase == NumberBase::HexBase;
|
|
CurrentRadixType = numberBase;
|
|
m_standardCalculatorManager.SetRadix(GetRadixTypeFromNumberBase(numberBase));
|
|
}
|
|
|
|
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)
|
|
{
|
|
bool handleOperand = false;
|
|
wstring updatedToken;
|
|
|
|
const pair<wstring, int>& token = m_tokens->at(tokenPosition);
|
|
const shared_ptr<IExpressionCommand>& tokenCommand = m_commands->at(token.second);
|
|
|
|
if (IsUnaryOp(command) && command != Command::CommandSIGN)
|
|
{
|
|
int angleCmd = static_cast<int>(m_standardCalculatorManager.GetCurrentDegreeMode());
|
|
ANGLE_TYPE angleType = GetAngleTypeFromCommand(static_cast<Command>(angleCmd));
|
|
|
|
if (IsTrigOp(command))
|
|
{
|
|
shared_ptr<IUnaryCommand> spUnaryCommand = dynamic_pointer_cast<IUnaryCommand>(tokenCommand);
|
|
spUnaryCommand->SetCommands(angleCmd, static_cast<int>(command));
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<IUnaryCommand> spUnaryCommand = dynamic_pointer_cast<IUnaryCommand>(tokenCommand);
|
|
spUnaryCommand->SetCommand(static_cast<int>(command));
|
|
}
|
|
|
|
switch (command)
|
|
{
|
|
case Command::CommandASIN:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSIN), true, angleType);
|
|
break;
|
|
case Command::CommandACOS:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOS), true, angleType);
|
|
break;
|
|
case Command::CommandATAN:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTAN), true, angleType);
|
|
break;
|
|
case Command::CommandASINH:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandSINH), true, angleType);
|
|
break;
|
|
case Command::CommandACOSH:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandCOSH), true, angleType);
|
|
break;
|
|
case Command::CommandATANH:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandTANH), true, angleType);
|
|
break;
|
|
case Command::CommandPOWE:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(Command::CommandLN), true, angleType);
|
|
break;
|
|
default:
|
|
updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast<int>(command), false, angleType);
|
|
}
|
|
if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'('))
|
|
{
|
|
updatedToken += L'(';
|
|
}
|
|
}
|
|
else if (IsBinOp(command))
|
|
{
|
|
shared_ptr<IBinaryCommand> spBinaryCommand = dynamic_pointer_cast<IBinaryCommand>(tokenCommand);
|
|
spBinaryCommand->SetCommand(static_cast<int>(command));
|
|
updatedToken = CCalcEngine::OpCodeToString(static_cast<int>(command));
|
|
}
|
|
else if (IsOpnd(command) || command == Command::CommandBACK)
|
|
{
|
|
HandleUpdatedOperandData(command);
|
|
handleOperand = true;
|
|
}
|
|
else if (command == Command::CommandSIGN)
|
|
{
|
|
if (tokenCommand->GetCommandType() == CommandType::UnaryCommand)
|
|
{
|
|
shared_ptr<IExpressionCommand> spSignCommand = make_shared<CUnaryCommand>(static_cast<int>(command));
|
|
m_commands->insert(m_commands->begin() + token.second + 1, spSignCommand);
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<IOpndCommand> spOpndCommand = dynamic_pointer_cast<IOpndCommand>(tokenCommand);
|
|
spOpndCommand->ToggleSign();
|
|
updatedToken = spOpndCommand->GetToken(m_standardCalculatorManager.DecimalSeparator());
|
|
}
|
|
IsOperandUpdatedUsingViewModel = true;
|
|
}
|
|
|
|
if (!handleOperand)
|
|
{
|
|
(*m_commands)[token.second] = tokenCommand;
|
|
(*m_tokens)[tokenPosition].first = updatedToken;
|
|
|
|
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<vector<shared_ptr<IExpressionCommand>>> savedCommands = make_shared<vector<shared_ptr<IExpressionCommand>>>();
|
|
vector<int> currentCommands;
|
|
|
|
for (const auto& command : *m_commands)
|
|
{
|
|
savedCommands->push_back(command);
|
|
CommandType commandType = command->GetCommandType();
|
|
|
|
if (commandType == CommandType::UnaryCommand)
|
|
{
|
|
shared_ptr<IUnaryCommand> spCommand = dynamic_pointer_cast<IUnaryCommand>(command);
|
|
const shared_ptr<vector<int>>& unaryCommands = spCommand->GetCommands();
|
|
|
|
for (int nUCode : *unaryCommands)
|
|
{
|
|
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);
|
|
const shared_ptr<vector<int>>& opndCommands = spCommand->GetCommands();
|
|
bool fNeedIDCSign = spCommand->IsNegative();
|
|
|
|
for (int nOCode : *opndCommands)
|
|
{
|
|
currentCommands.push_back(nOCode);
|
|
|
|
if (fNeedIDCSign && nOCode != IDC_0)
|
|
{
|
|
currentCommands.push_back(static_cast<int>(CalculationManager::Command::CommandSIGN));
|
|
fNeedIDCSign = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<vector<pair<wstring, int>>> savedTokens = make_shared<vector<pair<wstring, int>>>();
|
|
|
|
for (const auto& currentToken : *m_tokens)
|
|
{
|
|
savedTokens->push_back(currentToken);
|
|
}
|
|
|
|
m_standardCalculatorManager.Reset(false);
|
|
if (IsScientific)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::ModeScientific);
|
|
}
|
|
|
|
if (IsFToEChecked)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(Command::CommandFE);
|
|
}
|
|
|
|
m_standardCalculatorManager.SendCommand(currentDegreeMode);
|
|
|
|
for (int command : currentCommands)
|
|
{
|
|
m_standardCalculatorManager.SendCommand(static_cast<CalculationManager::Command>(command));
|
|
}
|
|
|
|
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)
|
|
{
|
|
const pair<wstring, int>& token = m_tokens->at(tokenPosition);
|
|
unsigned int tokenCommandIndex = token.second;
|
|
const shared_ptr<IExpressionCommand>& tokenCommand = m_commands->at(tokenCommandIndex);
|
|
|
|
return tokenCommand->GetCommandType();
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsOpnd(Command command)
|
|
{
|
|
static constexpr Command opnd[] = { Command::Command0, Command::Command1, Command::Command2, Command::Command3, Command::Command4, Command::Command5,
|
|
Command::Command6, Command::Command7, Command::Command8, Command::Command9, Command::CommandPNT };
|
|
|
|
return find(begin(opnd), end(opnd), command) != end(opnd);
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsUnaryOp(Command command)
|
|
{
|
|
static constexpr 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 };
|
|
|
|
if (find(begin(unaryOp), end(unaryOp), command) != end(unaryOp))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (IsTrigOp(command))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsTrigOp(Command command)
|
|
{
|
|
static constexpr Command trigOp[] = {
|
|
Command::CommandSIN, Command::CommandCOS, Command::CommandTAN, Command::CommandASIN, Command::CommandACOS, Command::CommandATAN
|
|
};
|
|
|
|
return find(begin(trigOp), end(trigOp), command) != end(trigOp);
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsBinOp(Command command)
|
|
{
|
|
static constexpr Command binOp[] = { Command::CommandADD, Command::CommandSUB, Command::CommandMUL, Command::CommandDIV,
|
|
Command::CommandEXP, Command::CommandROOT, Command::CommandMOD, Command::CommandPWR };
|
|
|
|
return find(begin(binOp), end(binOp), command) != end(binOp);
|
|
}
|
|
|
|
bool StandardCalculatorViewModel::IsRecoverableCommand(Command command)
|
|
{
|
|
if (IsOpnd(command))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Programmer mode, bit flipping
|
|
if (Command::CommandBINEDITSTART <= command && command <= Command::CommandBINEDITEND)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static constexpr Command recoverableCommands[] = { Command::CommandA, Command::CommandB, Command::CommandC,
|
|
Command::CommandD, Command::CommandE, Command::CommandF };
|
|
|
|
return find(begin(recoverableCommands), end(recoverableCommands), command) != end(recoverableCommands);
|
|
}
|
|
|
|
size_t StandardCalculatorViewModel::LengthWithoutPadding(wstring str)
|
|
{
|
|
return str.length() - count(str.begin(), str.end(), L' ');
|
|
}
|
|
|
|
wstring StandardCalculatorViewModel::AddPadding(wstring binaryString)
|
|
{
|
|
if (LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(StringReference(binaryString.c_str())) == L"0")
|
|
{
|
|
return binaryString;
|
|
}
|
|
size_t pad = 4 - LengthWithoutPadding(binaryString) % 4;
|
|
if (pad == 4)
|
|
{
|
|
pad = 0;
|
|
}
|
|
return wstring(pad, L'0') + binaryString;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay()
|
|
{
|
|
constexpr int32_t precision = 64;
|
|
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
|
|
if ((hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision, true)) == L"")
|
|
{
|
|
hexDisplayString = DisplayValue->Data();
|
|
decimalDisplayString = DisplayValue->Data();
|
|
octalDisplayString = DisplayValue->Data();
|
|
binaryDisplayString = DisplayValue->Data();
|
|
}
|
|
else
|
|
{
|
|
decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision, true);
|
|
octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision, true);
|
|
binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision, true);
|
|
}
|
|
}
|
|
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));
|
|
|
|
auto binaryValueArray = ref new Vector<bool>(64, false);
|
|
auto binaryValue = m_standardCalculatorManager.GetResultForRadix(2, precision, false);
|
|
int i = 0;
|
|
|
|
// To get bit 0, grab from opposite end of string.
|
|
for (std::wstring::reverse_iterator it = binaryValue.rbegin(); it != binaryValue.rend(); ++it)
|
|
{
|
|
binaryValueArray->SetAt(i++, *it == L'1');
|
|
}
|
|
BinaryDigits = binaryValueArray;
|
|
}
|
|
|
|
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->at(pos);
|
|
|
|
String ^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(text);
|
|
p.first = englishString->Data();
|
|
|
|
int commandPos = p.second;
|
|
const shared_ptr<IExpressionCommand>& exprCmd = m_commands->at(commandPos);
|
|
auto operandCommand = std::dynamic_pointer_cast<IOpndCommand>(exprCmd);
|
|
|
|
if (operandCommand != nullptr)
|
|
{
|
|
shared_ptr<vector<int>> commands = make_shared<vector<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->push_back(num);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
commands->push_back(0);
|
|
}
|
|
operandCommand->SetCommands(commands);
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::UpdateCommandsInRecordingMode()
|
|
{
|
|
shared_ptr<vector<int>> commands = make_shared<vector<int>>();
|
|
bool isDecimal = false;
|
|
bool isNegative = false;
|
|
bool isExpMode = false;
|
|
bool ePlusMode = false;
|
|
bool eMinusMode = false;
|
|
|
|
for (const auto savedCommand : m_standardCalculatorManager.GetSavedCommands())
|
|
{
|
|
const Command val = static_cast<Command>(savedCommand);
|
|
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->push_back(static_cast<int>(val));
|
|
}
|
|
|
|
if (!commands->empty())
|
|
{
|
|
shared_ptr<IOpndCommand> sp = make_shared<COpndCommand>(commands, isNegative, isDecimal, isExpMode);
|
|
m_commands->push_back(sp);
|
|
}
|
|
Recalculate();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMaxDigitsReached()
|
|
{
|
|
if (m_localizedMaxDigitsReachedAutomationFormat == nullptr)
|
|
{
|
|
m_localizedMaxDigitsReachedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MaxDigitsReachedFormat);
|
|
}
|
|
String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedMaxDigitsReachedAutomationFormat, m_CalculationResultAutomationName);
|
|
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
|
|
{
|
|
if (m_localizedButtonPressFeedbackAutomationFormat == nullptr)
|
|
{
|
|
m_localizedButtonPressFeedbackAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::ButtonPressFeedbackFormat);
|
|
}
|
|
announcement = LocalizationStringUtil::GetLocalizedString(
|
|
m_localizedButtonPressFeedbackAutomationFormat,
|
|
m_CalculationResultAutomationName,
|
|
m_feedbackForButtonPress);
|
|
}
|
|
|
|
// Make sure we don't accidentally repeat an announcement.
|
|
m_feedbackForButtonPress = nullptr;
|
|
|
|
return CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(announcement);
|
|
}
|
|
|
|
ViewMode StandardCalculatorViewModel::GetCalculatorMode()
|
|
{
|
|
if (IsStandard)
|
|
{
|
|
return ViewMode::Standard;
|
|
}
|
|
else if (IsScientific)
|
|
{
|
|
return ViewMode::Scientific;
|
|
}
|
|
return ViewMode::Programmer;
|
|
}
|
|
|
|
void StandardCalculatorViewModel::ValueBitLength::set(CalculatorApp::Common::BitLength value)
|
|
{
|
|
if (m_valueBitLength != value)
|
|
{
|
|
m_valueBitLength = value;
|
|
RaisePropertyChanged(L"ValueBitLength");
|
|
|
|
switch (value)
|
|
{
|
|
case BitLength::BitLengthQWord:
|
|
ButtonPressed->Execute(NumbersAndOperatorsEnum::Qword);
|
|
break;
|
|
case BitLength::BitLengthDWord:
|
|
ButtonPressed->Execute(NumbersAndOperatorsEnum::Dword);
|
|
break;
|
|
case BitLength::BitLengthWord:
|
|
ButtonPressed->Execute(NumbersAndOperatorsEnum::Word);
|
|
break;
|
|
case BitLength::BitLengthByte:
|
|
ButtonPressed->Execute(NumbersAndOperatorsEnum::Byte);
|
|
break;
|
|
}
|
|
|
|
// update memory list according to bit length
|
|
SetMemorizedNumbersString();
|
|
}
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SelectHistoryItem(HistoryItemViewModel ^ item)
|
|
{
|
|
SetHistoryExpressionDisplay(item->GetTokens(), item->GetCommands());
|
|
SetExpressionDisplay(item->GetTokens(), item->GetCommands());
|
|
SetPrimaryDisplay(item->Result, false);
|
|
IsFToEEnabled = false;
|
|
}
|