calculator/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.cpp

277 lines
9.6 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// CalculatorProgrammerBitFlipPanel.xaml.cpp
// Implementation of the CalculatorProgrammerBitFlipPanel class
//
#include "pch.h"
#include "CalculatorProgrammerBitFlipPanel.xaml.h"
#include "CalcViewModel/Common/TraceLogger.h"
#include "CalcViewModel/Common/LocalizationSettings.h"
#include "Converters/BooleanToVisibilityConverter.h"
#include <CalcViewModel/Common/AppResourceProvider.h>
#include "CalcViewModel/Common/LocalizationStringUtil.h"
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::Controls;
using namespace CalculatorApp::ViewModel;
using namespace Platform;
using namespace std;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
CalculatorProgrammerBitFlipPanel::CalculatorProgrammerBitFlipPanel()
: m_updatingCheckedStates(false)
{
InitializeComponent();
AssignFlipButtons();
}
void CalculatorProgrammerBitFlipPanel::OnLoaded(Object ^ sender, RoutedEventArgs ^ e)
{
SubscribePropertyChanged();
}
void CalculatorProgrammerBitFlipPanel::OnUnloaded(Object ^ sender, RoutedEventArgs ^ e)
{
UnsubscribePropertyChanged();
}
void CalculatorProgrammerBitFlipPanel::SubscribePropertyChanged()
{
if (Model != nullptr)
{
m_propertyChangedToken = Model->PropertyChanged += ref new PropertyChangedEventHandler(this, &CalculatorProgrammerBitFlipPanel::OnPropertyChanged);
m_currentValueBitLength = Model->ValueBitLength;
UpdateCheckedStates(true);
}
}
void CalculatorProgrammerBitFlipPanel::UnsubscribePropertyChanged()
{
if (Model != nullptr && m_propertyChangedToken.Value != 0)
{
Model->PropertyChanged -= m_propertyChangedToken;
m_propertyChangedToken.Value = 0;
}
}
void CalculatorProgrammerBitFlipPanel::OnPropertyChanged(Object ^ sender, PropertyChangedEventArgs ^ e)
{
if (e->PropertyName == StandardCalculatorViewModel::BinaryDigitsPropertyName)
{
UpdateCheckedStates(false);
m_currentValueBitLength = Model->ValueBitLength;
}
else if (
e->PropertyName == StandardCalculatorViewModel::IsBitFlipCheckedPropertyName
|| e->PropertyName == StandardCalculatorViewModel::IsProgrammerPropertyName)
{
if (Model->IsBitFlipChecked && Model->IsProgrammer)
{
// OnBitToggle won't update the automation properties when this control isn't displayed
// We need to update all automation properties names manually when the BitFlipPanel is displayed again
UpdateAutomationPropertiesNames();
}
}
}
StandardCalculatorViewModel ^ CalculatorProgrammerBitFlipPanel::Model::get()
{
return static_cast<StandardCalculatorViewModel ^>(this->DataContext);
}
void CalculatorProgrammerBitFlipPanel::AssignFlipButtons()
{
assert(m_flipButtons.size() == 64);
m_flipButtons[0] = this->Bit0;
m_flipButtons[1] = this->Bit1;
m_flipButtons[2] = this->Bit2;
m_flipButtons[3] = this->Bit3;
m_flipButtons[4] = this->Bit4;
m_flipButtons[5] = this->Bit5;
m_flipButtons[6] = this->Bit6;
m_flipButtons[7] = this->Bit7;
m_flipButtons[8] = this->Bit8;
m_flipButtons[9] = this->Bit9;
m_flipButtons[10] = this->Bit10;
m_flipButtons[11] = this->Bit11;
m_flipButtons[12] = this->Bit12;
m_flipButtons[13] = this->Bit13;
m_flipButtons[14] = this->Bit14;
m_flipButtons[15] = this->Bit15;
m_flipButtons[16] = this->Bit16;
m_flipButtons[17] = this->Bit17;
m_flipButtons[18] = this->Bit18;
m_flipButtons[19] = this->Bit19;
m_flipButtons[20] = this->Bit20;
m_flipButtons[21] = this->Bit21;
m_flipButtons[22] = this->Bit22;
m_flipButtons[23] = this->Bit23;
m_flipButtons[24] = this->Bit24;
m_flipButtons[25] = this->Bit25;
m_flipButtons[26] = this->Bit26;
m_flipButtons[27] = this->Bit27;
m_flipButtons[28] = this->Bit28;
m_flipButtons[29] = this->Bit29;
m_flipButtons[30] = this->Bit30;
m_flipButtons[31] = this->Bit31;
m_flipButtons[32] = this->Bit32;
m_flipButtons[33] = this->Bit33;
m_flipButtons[34] = this->Bit34;
m_flipButtons[35] = this->Bit35;
m_flipButtons[36] = this->Bit36;
m_flipButtons[37] = this->Bit37;
m_flipButtons[38] = this->Bit38;
m_flipButtons[39] = this->Bit39;
m_flipButtons[40] = this->Bit40;
m_flipButtons[41] = this->Bit41;
m_flipButtons[42] = this->Bit42;
m_flipButtons[43] = this->Bit43;
m_flipButtons[44] = this->Bit44;
m_flipButtons[45] = this->Bit45;
m_flipButtons[46] = this->Bit46;
m_flipButtons[47] = this->Bit47;
m_flipButtons[48] = this->Bit48;
m_flipButtons[49] = this->Bit49;
m_flipButtons[50] = this->Bit50;
m_flipButtons[51] = this->Bit51;
m_flipButtons[52] = this->Bit52;
m_flipButtons[53] = this->Bit53;
m_flipButtons[54] = this->Bit54;
m_flipButtons[55] = this->Bit55;
m_flipButtons[56] = this->Bit56;
m_flipButtons[57] = this->Bit57;
m_flipButtons[58] = this->Bit58;
m_flipButtons[59] = this->Bit59;
m_flipButtons[60] = this->Bit60;
m_flipButtons[61] = this->Bit61;
m_flipButtons[62] = this->Bit62;
m_flipButtons[63] = this->Bit63;
}
void CalculatorProgrammerBitFlipPanel::OnBitToggled(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e)
{
if (m_updatingCheckedStates)
{
return;
}
// Handle this the bit toggled event only if it is coming from BitFlip mode.
// Any input from the Numpad may also result in toggling the bit as their state is bound to the BinaryDisplayValue.
// Also, if the mode is switched to other Calculator modes when the BitFlip panel is open,
// a race condition exists in which the IsProgrammerMode property is still true and the UpdatePrimaryResult() is called,
// which continuously alters the Display Value and the state of the Bit Flip buttons.
if (Model->IsBitFlipChecked && Model->IsProgrammer)
{
auto flipButton = static_cast<FlipButtons ^>(sender);
int index = static_cast<int>(flipButton->Tag);
flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, flipButton->IsChecked->Value));
Model->ButtonPressed->Execute(flipButton->ButtonId);
}
}
void CalculatorProgrammerBitFlipPanel::UpdateCheckedStates(bool updateAutomationPropertiesNames)
{
assert(!m_updatingCheckedStates);
assert(m_flipButtons.size() == s_numBits);
if (Model == nullptr)
{
return;
}
m_updatingCheckedStates = true;
auto it = m_flipButtons.begin();
int index = 0;
bool mustUpdateTextOfMostSignificantDigits = m_currentValueBitLength != Model->ValueBitLength;
int previousMSDPosition = GetIndexOfLastBit(m_currentValueBitLength);
int newMSDPosition = GetIndexOfLastBit(Model->ValueBitLength);
for (bool val : Model->BinaryDigits)
{
FlipButtons ^ flipButton = *it;
bool hasValueChanged = flipButton->IsChecked->Value != val;
flipButton->IsChecked = val;
if (updateAutomationPropertiesNames
|| hasValueChanged
|| (mustUpdateTextOfMostSignificantDigits && (index == previousMSDPosition || index == newMSDPosition)))
{
flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, val));
}
++it;
++index;
}
m_updatingCheckedStates = false;
}
void CalculatorProgrammerBitFlipPanel::UpdateAutomationPropertiesNames()
{
for (FlipButtons ^ flipButton : m_flipButtons)
{
int index = static_cast<int>(flipButton->Tag);
flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, flipButton->IsChecked->Value));
}
}
bool CalculatorProgrammerBitFlipPanel::ShouldEnableBit(BitLength length, int index)
{
return index <= GetIndexOfLastBit(length);
}
int CalculatorProgrammerBitFlipPanel::GetIndexOfLastBit(BitLength length) const
{
switch (length)
{
case BitLength::BitLengthQWord:
return 63;
case BitLength::BitLengthDWord:
return 31;
case BitLength::BitLengthWord:
return 15;
case BitLength::BitLengthByte:
return 7;
}
return -1;
}
String ^ CalculatorProgrammerBitFlipPanel::GenerateAutomationPropertiesName(int position, bool value)
{
auto resourceLoader = AppResourceProvider::GetInstance();
String ^ automationNameTemplate = resourceLoader.GetResourceString(L"BitFlipItemAutomationName");
wstring bitPosition;
if (position == 0)
{
bitPosition = wstring(resourceLoader.GetResourceString(L"LeastSignificantBit")->Data());
}
else
{
int lastPosition = -1;
if (Model != nullptr)
{
lastPosition = GetIndexOfLastBit(Model->ValueBitLength);
}
if (position == lastPosition)
{
bitPosition = wstring(resourceLoader.GetResourceString(L"MostSignificantBit")->Data());
}
else
{
String ^ indexName = resourceLoader.GetResourceString(ref new Platform::String(to_wstring(position).c_str()));
String ^ bitPositionTemplate = resourceLoader.GetResourceString(L"BitPosition");
bitPosition = LocalizationStringUtil::GetLocalizedString(bitPositionTemplate->Data(), indexName->Data());
}
}
return ref new String(LocalizationStringUtil::GetLocalizedString(automationNameTemplate->Data(), bitPosition.c_str(), value ? L"1" : L"0").c_str());
}