mirror of
https://github.com/Microsoft/calculator.git
synced 2025-07-13 08:42:53 -07:00
* Replacing CalculatorVector usage with std::vector Assumptions made here are that memory allocations are not recoverable. If it can be proved that an index will be in range, then the indexing operation is used. If not (without manual checks) the std::vector::at function is used to throw an exception in case of a programmer bug. * Changes based on PR feedback Using auto& in CalculatorCollector::UpdateHistoryExpression so the token.first value is properly updated. Using range for loop to GenerateExpressions. Setting isEditable directly to the result of boolean expression. Using token.second directly instead of creating a separate tokenCommandIndex variable. * Fixing issue with generating expressions strings. A space should not be added before the first item.
1884 lines
64 KiB
C++
1884 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 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
|
|
{
|
|
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(RADIX_TYPE::DEC_RADIX)
|
|
, 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)
|
|
{
|
|
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);
|
|
|
|
// 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);
|
|
|
|
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);
|
|
|
|
String ^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::OpenParenthesisCountAutomationFormat,
|
|
m_localizedOpenParenthesisCountChangedAutomationFormat,
|
|
localizedParenthesisCount.c_str());
|
|
|
|
Announcement = CalculatorAnnouncement::GetOpenParenthesisCountChangedAnnouncement(announcement);
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnNoRightParenAdded()
|
|
{
|
|
SetNoParenAddedNarratorAnnouncement();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::SetNoParenAddedNarratorAnnouncement()
|
|
{
|
|
String ^ announcement =
|
|
LocalizationStringUtil::GetLocalizedNarratorAnnouncement(CalculatorResourceKeys::NoParenthesisAdded, m_localizedNoRightParenthesisAddedFormat);
|
|
|
|
Announcement = CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(announcement);
|
|
}
|
|
|
|
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 = ref new String(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((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::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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
auto that(this);
|
|
ViewMode mode;
|
|
int NumberBase = -1;
|
|
BitLength bitLengthType = BitLength::BitLengthUnknown;
|
|
if (IsScientific)
|
|
{
|
|
mode = ViewMode::Scientific;
|
|
}
|
|
else if (IsProgrammer)
|
|
{
|
|
mode = ViewMode::Programmer;
|
|
NumberBase = GetNumberBase();
|
|
bitLengthType = m_valueBitLength;
|
|
}
|
|
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([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 (pastedString == StringReference(CopyPasteManager::PasteErrorString))
|
|
{
|
|
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;
|
|
bool canSendNegate = false;
|
|
|
|
NumbersAndOperatorsEnum mappedNumOp = MapCharacterToButtonId(*it, 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), canSendNegate))
|
|
{
|
|
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());
|
|
|
|
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 '^':
|
|
if (IsScientific)
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::XPowerY;
|
|
}
|
|
break;
|
|
|
|
case '%':
|
|
if (IsScientific || IsProgrammer)
|
|
{
|
|
mappedValue = NumbersAndOperatorsEnum::Mod;
|
|
}
|
|
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::OnInputChanged()
|
|
{
|
|
IsInputEmpty = m_standardCalculatorManager.IsInputEmpty();
|
|
}
|
|
|
|
void StandardCalculatorViewModel::OnMemoryButtonPressed()
|
|
{
|
|
m_standardCalculatorManager.MemorizeNumber();
|
|
|
|
TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::Memory, GetCalculatorMode());
|
|
|
|
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();
|
|
|
|
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);
|
|
|
|
String ^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(
|
|
CalculatorResourceKeys::MemoryItemCleared, m_localizedMemoryItemClearedAutomationFormat, 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
|
|
{
|
|
wstring rawValue;
|
|
LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue->Data(), DisplayValue->Length(), &rawValue);
|
|
return ref new 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
int nOpCode = static_cast<int>(command);
|
|
wstring updatedToken = L"";
|
|
|
|
const pair<wstring, int>& token = m_tokens->at(tokenPosition);
|
|
const shared_ptr<IExpressionCommand>& tokenCommand = m_commands->at(token.second);
|
|
|
|
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);
|
|
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);
|
|
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)
|
|
{
|
|
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(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 (unsigned int i = 0; i < size(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 (unsigned int i = 0; i < size(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 (unsigned int i = 0; i < size(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 (unsigned int i = 0; i < size(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 (unsigned int i = 0; i < size(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()
|
|
{
|
|
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->Data());
|
|
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()
|
|
{
|
|
vector<unsigned char> savedCommands = m_standardCalculatorManager.GetSavedCommands();
|
|
shared_ptr<vector<int>> commands = make_shared<vector<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->push_back(num);
|
|
}
|
|
|
|
if (!commands->empty())
|
|
{
|
|
shared_ptr<IOpndCommand> sp = make_shared<COpndCommand>(commands, isNegative, isDecimal, isExpMode);
|
|
m_commands->push_back(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);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|