Hello GitHub
This commit is contained in:
206
src/CalcViewModel/ApplicationViewModel.cpp
Normal file
206
src/CalcViewModel/ApplicationViewModel.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ApplicationViewModel.h"
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "Common\AppResourceProvider.h"
|
||||
#include "DataLoaders\CurrencyHttpClient.h"
|
||||
#include "DataLoaders\CurrencyDataLoader.h"
|
||||
#include "DataLoaders\UnitConverterDataLoader.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::DataLoaders;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace CalculationManager;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Utils;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::UI::ViewManagement;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
using namespace Windows::UI::Xaml::Input;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
|
||||
namespace CalculatorApp::ViewModel::ApplicationViewModelProperties
|
||||
{
|
||||
StringReference Mode(L"Mode");
|
||||
StringReference PreviousMode(L"PreviousMode");
|
||||
StringReference ClearMemoryVisibility(L"ClearMemoryVisibility");
|
||||
StringReference AppBarVisibility(L"AppBarVisibility");
|
||||
StringReference CategoryName(L"CategoryName");
|
||||
StringReference Categories(L"Categories");
|
||||
}
|
||||
|
||||
ApplicationViewModel::ApplicationViewModel() :
|
||||
m_CalculatorViewModel(nullptr),
|
||||
m_DateCalcViewModel(nullptr),
|
||||
m_ConverterViewModel(nullptr),
|
||||
m_PreviousMode(ViewMode::None),
|
||||
m_mode(ViewMode::None),
|
||||
m_categories(nullptr)
|
||||
{
|
||||
SetMenuCategories();
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Mode::set(ViewMode value)
|
||||
{
|
||||
if (m_mode != value)
|
||||
{
|
||||
PreviousMode = m_mode;
|
||||
m_mode = value;
|
||||
OnModeChanged();
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::Mode);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Categories::set(IObservableVector<NavCategoryGroup^>^ value)
|
||||
{
|
||||
if (m_categories != value)
|
||||
{
|
||||
m_categories = value;
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::Categories);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::Initialize(ViewMode mode)
|
||||
{
|
||||
if (!NavCategory::IsValidViewMode(mode))
|
||||
{
|
||||
mode = ViewMode::Standard;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Mode = mode;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e);
|
||||
if (!TryRecoverFromNavigationModeFailure())
|
||||
{
|
||||
// Could not navigate to standard mode either.
|
||||
// Throw the original exception so we have a good stack to debug.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (Exception^ e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, e);
|
||||
if (!TryRecoverFromNavigationModeFailure())
|
||||
{
|
||||
// Could not navigate to standard mode either.
|
||||
// Throw the original exception so we have a good stack to debug.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationViewModel::TryRecoverFromNavigationModeFailure()
|
||||
{
|
||||
// Here we are simply trying to recover from being unable to navigate to a mode.
|
||||
// Try falling back to standard mode and if there are *any* exceptions, we should
|
||||
// fail because something is seriously wrong.
|
||||
try
|
||||
{
|
||||
Mode = ViewMode::Standard;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnModeChanged()
|
||||
{
|
||||
assert(NavCategory::IsValidViewMode(m_mode));
|
||||
TraceLogger::GetInstance().LogModeChangeBegin(m_PreviousMode, m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (NavCategory::IsCalculatorViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_CalculatorViewModel)
|
||||
{
|
||||
m_CalculatorViewModel = ref new StandardCalculatorViewModel();
|
||||
}
|
||||
m_CalculatorViewModel->SetCalculatorType(m_mode);
|
||||
}
|
||||
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_DateCalcViewModel)
|
||||
{
|
||||
m_DateCalcViewModel = ref new DateCalculatorViewModel();
|
||||
}
|
||||
}
|
||||
else if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
TraceLogger::GetInstance().LogConverterModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
if (!m_ConverterViewModel)
|
||||
{
|
||||
auto dataLoader = make_shared<UnitConverterDataLoader>(ref new GeographicRegion());
|
||||
auto currencyDataLoader = make_shared<CurrencyDataLoader>(make_unique<CurrencyHttpClient>());
|
||||
m_ConverterViewModel = ref new UnitConverterViewModel(make_shared<UnitConversionManager::UnitConverter>(dataLoader, currencyDataLoader));
|
||||
}
|
||||
|
||||
m_ConverterViewModel->Mode = m_mode;
|
||||
}
|
||||
|
||||
auto resProvider = AppResourceProvider::GetInstance();
|
||||
CategoryName = resProvider.GetResourceString(NavCategory::GetNameResourceKey(m_mode));
|
||||
|
||||
// This is the only place where a ViewMode enum should be cast to an int.
|
||||
//
|
||||
// Save the changed mode, so that the new window launches in this mode.
|
||||
// Don't save until after we have adjusted to the new mode, so we don't save a mode that fails to load.
|
||||
ApplicationData::Current->LocalSettings->Values->Insert(ApplicationViewModelProperties::Mode, NavCategory::Serialize(m_mode));
|
||||
|
||||
TraceLogger::GetInstance().LogModeChangeEnd(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::ClearMemoryVisibility);
|
||||
RaisePropertyChanged(ApplicationViewModelProperties::AppBarVisibility);
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnCopyCommand(Object^ parameter)
|
||||
{
|
||||
if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
ConverterViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
|
||||
{
|
||||
DateCalcViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatorViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::OnPasteCommand(Object^ parameter)
|
||||
{
|
||||
if (NavCategory::IsConverterViewMode(m_mode))
|
||||
{
|
||||
ConverterViewModel->OnPasteCommand(parameter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculatorViewModel->OnPasteCommand(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationViewModel::SetMenuCategories()
|
||||
{
|
||||
// Use the Categories property instead of the backing variable
|
||||
// because we want to take advantage of binding updates and
|
||||
// property setter logic.
|
||||
Categories = NavCategoryGroup::CreateMenuOptions();
|
||||
}
|
||||
96
src/CalcViewModel/ApplicationViewModel.h
Normal file
96
src/CalcViewModel/ApplicationViewModel.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "UnitConverterViewModel.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
namespace ApplicationViewModelProperties
|
||||
{
|
||||
extern Platform::StringReference Mode;
|
||||
extern Platform::StringReference PreviousMode;
|
||||
extern Platform::StringReference ClearMemoryVisibility;
|
||||
extern Platform::StringReference AppBarVisibility;
|
||||
extern Platform::StringReference CategoryName;
|
||||
extern Platform::StringReference Categories;
|
||||
}
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
ApplicationViewModel();
|
||||
|
||||
void Initialize(CalculatorApp::Common::ViewMode mode); // Use for first init, use deserialize for rehydration
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel^, CalculatorViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel^, DateCalcViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::ViewModel::UnitConverterViewModel^, ConverterViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CategoryName);
|
||||
|
||||
COMMAND_FOR_METHOD(CopyCommand, ApplicationViewModel::OnCopyCommand);
|
||||
COMMAND_FOR_METHOD(PasteCommand, ApplicationViewModel::OnPasteCommand);
|
||||
|
||||
property CalculatorApp::Common::ViewMode Mode
|
||||
{
|
||||
CalculatorApp::Common::ViewMode get()
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void set(CalculatorApp::Common::ViewMode value);
|
||||
}
|
||||
|
||||
property Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ Categories
|
||||
{
|
||||
Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ get()
|
||||
{
|
||||
return m_categories;
|
||||
}
|
||||
|
||||
void set(Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ value);
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility ClearMemoryVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode)
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility AppBarVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode)
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool TryRecoverFromNavigationModeFailure();
|
||||
|
||||
void OnModeChanged();
|
||||
|
||||
void OnCopyCommand(Platform::Object^ parameter);
|
||||
void OnPasteCommand(Platform::Object^ parameter);
|
||||
|
||||
void SetMenuCategories();
|
||||
|
||||
CalculatorApp::Common::ViewMode m_mode;
|
||||
Windows::Foundation::Collections::IObservableVector<CalculatorApp::Common::NavCategoryGroup^>^ m_categories;
|
||||
};
|
||||
}
|
||||
}
|
||||
409
src/CalcViewModel/CalcViewModel.vcxproj
Normal file
409
src/CalcViewModel/CalcViewModel.vcxproj
Normal file
@@ -0,0 +1,409 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{90e9761d-9262-4773-942d-caeae75d7140}</ProjectGuid>
|
||||
<Keyword>StaticLibrary</Keyword>
|
||||
<RootNamespace>CalcViewModel</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<CompileAsWinRT>true</CompileAsWinRT>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DisableSpecificWarnings>4453</DisableSpecificWarnings>
|
||||
<AdditionalOptions>/bigobj /await /std:c++17 %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalOptions>/ignore:4264 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(IsStoreBuild)' == 'True'">
|
||||
<ClCompile>
|
||||
<AdditionalOptions>/DSEND_TELEMETRY %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ApplicationViewModel.h" />
|
||||
<ClInclude Include="Common\AlwaysSelectedCollectionView.h" />
|
||||
<ClInclude Include="Common\AppResourceProvider.h" />
|
||||
<ClInclude Include="Common\Automation\INarratorAnnouncementHost.h" />
|
||||
<ClInclude Include="Common\Automation\LiveRegionHost.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncement.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncementHostFactory.h" />
|
||||
<ClInclude Include="Common\Automation\NarratorNotifier.h" />
|
||||
<ClInclude Include="Common\Automation\NotificationHost.h" />
|
||||
<ClInclude Include="Common\BindableBase.h" />
|
||||
<ClInclude Include="Common\CalculatorButtonPressedEventArgs.h" />
|
||||
<ClInclude Include="Common\CalculatorButtonUser.h" />
|
||||
<ClInclude Include="Common\CalculatorDisplay.h" />
|
||||
<ClInclude Include="Common\ConversionResultTaskHelper.h" />
|
||||
<ClInclude Include="Common\CopyPasteManager.h" />
|
||||
<ClInclude Include="Common\DateCalculator.h" />
|
||||
<ClInclude Include="Common\DelegateCommand.h" />
|
||||
<ClInclude Include="Common\DisplayExpressionToken.h" />
|
||||
<ClInclude Include="Common\EngineResourceProvider.h" />
|
||||
<ClInclude Include="Common\ExpressionCommandDeserializer.h" />
|
||||
<ClInclude Include="Common\ExpressionCommandSerializer.h" />
|
||||
<ClInclude Include="Common\KeyboardShortcutManager.h" />
|
||||
<ClInclude Include="Common\LocalizationService.h" />
|
||||
<ClInclude Include="Common\LocalizationSettings.h" />
|
||||
<ClInclude Include="Common\LocalizationStringUtil.h" />
|
||||
<ClInclude Include="Common\MyVirtualKey.h" />
|
||||
<ClInclude Include="Common\NavCategory.h" />
|
||||
<ClInclude Include="Common\NetworkManager.h" />
|
||||
<ClInclude Include="Common\TraceActivity.h" />
|
||||
<ClInclude Include="Common\TraceLogger.h" />
|
||||
<ClInclude Include="Common\Utils.h" />
|
||||
<ClInclude Include="Common\ValidatingConverters.h" />
|
||||
<ClInclude Include="DataLoaders\CurrencyDataLoader.h" />
|
||||
<ClInclude Include="DataLoaders\CurrencyHttpClient.h" />
|
||||
<ClInclude Include="DataLoaders\ICurrencyHttpClient.h" />
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
|
||||
<ClInclude Include="DateCalculatorViewModel.h" />
|
||||
<ClInclude Include="HistoryItemViewModel.h" />
|
||||
<ClInclude Include="HistoryViewModel.h" />
|
||||
<ClInclude Include="MemoryItemViewModel.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="StandardCalculatorViewModel.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="UnitConverterViewModel.h" />
|
||||
<ClInclude Include="ViewState.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ApplicationViewModel.cpp" />
|
||||
<ClCompile Include="Common\AppResourceProvider.cpp" />
|
||||
<ClCompile Include="Common\Automation\LiveRegionHost.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncementHostFactory.cpp" />
|
||||
<ClCompile Include="Common\Automation\NarratorNotifier.cpp" />
|
||||
<ClCompile Include="Common\Automation\NotificationHost.cpp" />
|
||||
<ClCompile Include="Common\BindableBase.cpp" />
|
||||
<ClCompile Include="Common\CalculatorButtonPressedEventArgs.cpp" />
|
||||
<ClCompile Include="Common\CalculatorDisplay.cpp" />
|
||||
<ClCompile Include="Common\ConversionResultTaskHelper.cpp" />
|
||||
<ClCompile Include="Common\CopyPasteManager.cpp" />
|
||||
<ClCompile Include="Common\DateCalculator.cpp" />
|
||||
<ClCompile Include="Common\EngineResourceProvider.cpp" />
|
||||
<ClCompile Include="Common\ExpressionCommandDeserializer.cpp" />
|
||||
<ClCompile Include="Common\ExpressionCommandSerializer.cpp" />
|
||||
<ClCompile Include="Common\KeyboardShortcutManager.cpp" />
|
||||
<ClCompile Include="Common\LocalizationService.cpp" />
|
||||
<ClCompile Include="Common\NavCategory.cpp" />
|
||||
<ClCompile Include="Common\NetworkManager.cpp" />
|
||||
<ClCompile Include="Common\TraceLogger.cpp" />
|
||||
<ClCompile Include="Common\Utils.cpp" />
|
||||
<ClCompile Include="DataLoaders\CurrencyDataLoader.cpp" />
|
||||
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
|
||||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
|
||||
<ClCompile Include="DateCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="HistoryItemViewModel.cpp" />
|
||||
<ClCompile Include="HistoryViewModel.cpp" />
|
||||
<ClCompile Include="MemoryItemViewModel.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StandardCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="UnitConverterViewModel.cpp" />
|
||||
<ClCompile Include="ViewState.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CalcManager\CalcManager.vcxproj">
|
||||
<Project>{311e866d-8b93-4609-a691-265941fee101}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DataLoaders\DefaultFromToCurrency.json" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.UI.Xaml.2.0.181018003.1\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
227
src/CalcViewModel/CalcViewModel.vcxproj.filters
Normal file
227
src/CalcViewModel/CalcViewModel.vcxproj.filters
Normal file
@@ -0,0 +1,227 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Common">
|
||||
<UniqueIdentifier>{1daab7c4-63f6-4266-a259-f34acad66d09}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Common\Automation">
|
||||
<UniqueIdentifier>{8d4edf06-c312-4312-978a-b6c2beb8295a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="DataLoaders">
|
||||
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="ApplicationViewModel.cpp" />
|
||||
<ClCompile Include="DateCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="HistoryItemViewModel.cpp" />
|
||||
<ClCompile Include="HistoryViewModel.cpp" />
|
||||
<ClCompile Include="MemoryItemViewModel.cpp" />
|
||||
<ClCompile Include="StandardCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="UnitConverterViewModel.cpp" />
|
||||
<ClCompile Include="ViewState.cpp" />
|
||||
<ClCompile Include="Common\AppResourceProvider.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\BindableBase.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CalculatorButtonPressedEventArgs.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CalculatorDisplay.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ConversionResultTaskHelper.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\CopyPasteManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\DateCalculator.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\EngineResourceProvider.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ExpressionCommandDeserializer.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\ExpressionCommandSerializer.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\KeyboardShortcutManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\LocalizationService.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\NavCategory.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\NetworkManager.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\TraceLogger.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Utils.cpp">
|
||||
<Filter>Common</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\LiveRegionHost.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncementHostFactory.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorNotifier.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NotificationHost.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\CurrencyDataLoader.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="ApplicationViewModel.h" />
|
||||
<ClInclude Include="DateCalculatorViewModel.h" />
|
||||
<ClInclude Include="HistoryItemViewModel.h" />
|
||||
<ClInclude Include="HistoryViewModel.h" />
|
||||
<ClInclude Include="MemoryItemViewModel.h" />
|
||||
<ClInclude Include="StandardCalculatorViewModel.h" />
|
||||
<ClInclude Include="UnitConverterViewModel.h" />
|
||||
<ClInclude Include="ViewState.h" />
|
||||
<ClInclude Include="Common\AlwaysSelectedCollectionView.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\AppResourceProvider.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\BindableBase.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorButtonPressedEventArgs.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorButtonUser.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CalculatorDisplay.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ConversionResultTaskHelper.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\CopyPasteManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DateCalculator.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DelegateCommand.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\DisplayExpressionToken.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\EngineResourceProvider.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ExpressionCommandDeserializer.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ExpressionCommandSerializer.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\KeyboardShortcutManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationService.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationSettings.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\LocalizationStringUtil.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\MyVirtualKey.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\NavCategory.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\NetworkManager.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\TraceLogger.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Utils.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\ValidatingConverters.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\INarratorAnnouncementHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\LiveRegionHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncement.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorAnnouncementHostFactory.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NarratorNotifier.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\Automation\NotificationHost.h">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\CurrencyDataLoader.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\CurrencyHttpClient.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\ICurrencyHttpClient.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Common\TraceActivity.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DataLoaders\DefaultFromToCurrency.json">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
287
src/CalcViewModel/Common/AlwaysSelectedCollectionView.h
Normal file
287
src/CalcViewModel/Common/AlwaysSelectedCollectionView.h
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
ref class AlwaysSelectedCollectionView sealed:
|
||||
public Windows::UI::Xaml::DependencyObject,
|
||||
public Windows::UI::Xaml::Data::ICollectionView
|
||||
{
|
||||
internal:
|
||||
AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source):
|
||||
m_currentPosition(-1)
|
||||
{
|
||||
m_source = source;
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast<Windows::UI::Xaml::Interop::IBindableObservableVector^>(source);
|
||||
if (observable)
|
||||
{
|
||||
observable->VectorChanged +=
|
||||
ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// ICollectionView
|
||||
// Not implemented methods
|
||||
virtual WF::IAsyncOperation<Windows::UI::Xaml::Data::LoadMoreItemsResult>^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
property Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ CollectionGroups
|
||||
{
|
||||
virtual Windows::Foundation::Collections::IObservableVector<Platform::Object^>^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get
|
||||
{
|
||||
return ref new Platform::Collections::Vector<Platform::Object^>();
|
||||
}
|
||||
}
|
||||
property bool HasMoreItems
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Implementented methods
|
||||
virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo
|
||||
{
|
||||
if (item)
|
||||
{
|
||||
unsigned int newCurrentPosition = 0;
|
||||
bool result = m_source->IndexOf(item, &newCurrentPosition);
|
||||
if (result)
|
||||
{
|
||||
m_currentPosition = newCurrentPosition;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The item is not in the collection
|
||||
// We're going to schedule a call back later so we
|
||||
// restore the selection to the way we wanted it to begin with
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
|
||||
ref new Windows::UI::Core::DispatchedHandler(
|
||||
[this]()
|
||||
{
|
||||
m_currentChanged(this, nullptr);
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition
|
||||
{
|
||||
if (index < 0 || index >= static_cast<int>(m_source->Size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentPosition = index;
|
||||
m_currentChanged(this, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
property Platform::Object^ CurrentItem
|
||||
{
|
||||
virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get
|
||||
{
|
||||
if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
|
||||
{
|
||||
return m_source->GetAt(m_currentPosition);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
property int CurrentPosition
|
||||
{
|
||||
virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get
|
||||
{
|
||||
return m_currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentAfterLast
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get
|
||||
{
|
||||
return m_currentPosition >= static_cast<int>(m_source->Size);
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsCurrentBeforeFirst
|
||||
{
|
||||
virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get
|
||||
{
|
||||
return m_currentPosition < 0;
|
||||
}
|
||||
}
|
||||
|
||||
event WF::EventHandler<Platform::Object^>^ CurrentChanged
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(WF::EventHandler<Platform::Object^>^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add
|
||||
{
|
||||
return m_currentChanged += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::remove
|
||||
{
|
||||
m_currentChanged -= token;
|
||||
}
|
||||
}
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ CurrentChanging
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(Windows::UI::Xaml::Data::CurrentChangingEventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::add
|
||||
{
|
||||
return m_currentChanging += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::remove
|
||||
{
|
||||
m_currentChanging -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IVector<Object^>
|
||||
// Not implemented methods
|
||||
virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::Append
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void Clear() = Windows::Foundation::Collections::IVector<Platform::Object^>::Clear
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetMany
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual Windows::Foundation::Collections::IVectorView<Platform::Object^>^ GetView() = Windows::Foundation::Collections::IVector<Platform::Object^>::GetView
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::InsertAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector<Platform::Object^>::RemoveAtEnd
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void ReplaceAll(const Platform::Array<Platform::Object^>^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::ReplaceAll
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::SetAt
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Implemented methods
|
||||
virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector<Platform::Object^>::GetAt
|
||||
{
|
||||
return m_source->GetAt(index);
|
||||
}
|
||||
|
||||
virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector<Platform::Object^>::IndexOf
|
||||
{
|
||||
return m_source->IndexOf(item, index);
|
||||
}
|
||||
|
||||
property unsigned int Size
|
||||
{
|
||||
virtual unsigned int get() = Windows::Foundation::Collections::IVector<Platform::Object^>::Size::get
|
||||
{
|
||||
return m_source->Size;
|
||||
}
|
||||
}
|
||||
|
||||
// IObservableVector<Object^>
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ VectorChanged
|
||||
{
|
||||
virtual WF::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ handler) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::add
|
||||
{
|
||||
return m_vectorChanged += handler;
|
||||
}
|
||||
virtual void remove(WF::EventRegistrationToken token) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::remove
|
||||
{
|
||||
m_vectorChanged -= token;
|
||||
}
|
||||
}
|
||||
|
||||
// IIterable<Object^>
|
||||
// Not implemented
|
||||
virtual Windows::Foundation::Collections::IIterator<Platform::Object^>^ First() = Windows::Foundation::Collections::IIterable<Platform::Object^>::First
|
||||
{
|
||||
throw ref new Platform::NotImplementedException();
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e)
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast<Windows::Foundation::Collections::IVectorChangedEventArgs^>(e);
|
||||
m_vectorChanged(this, args);
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Interop::IBindableVector^ m_source;
|
||||
int m_currentPosition;
|
||||
event WF::EventHandler<Platform::Object^>^ m_currentChanged;
|
||||
event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging;
|
||||
event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ m_vectorChanged;
|
||||
};
|
||||
|
||||
public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
AlwaysSelectedCollectionViewConverter()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual Platform::Object^ Convert(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
|
||||
{
|
||||
auto result = dynamic_cast<Windows::UI::Xaml::Interop::IBindableVector^>(value);
|
||||
if (result)
|
||||
{
|
||||
return ref new AlwaysSelectedCollectionView(result);
|
||||
}
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert
|
||||
}
|
||||
|
||||
virtual Platform::Object^ ConverBack(
|
||||
Platform::Object^ /*value*/,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
|
||||
{
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue;
|
||||
}
|
||||
};
|
||||
}}
|
||||
33
src/CalcViewModel/Common/AppResourceProvider.cpp
Normal file
33
src/CalcViewModel/Common/AppResourceProvider.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace CalculatorApp;
|
||||
|
||||
AppResourceProvider::AppResourceProvider()
|
||||
{
|
||||
m_stringResLoader = ResourceLoader::GetForViewIndependentUse();
|
||||
m_cEngineStringResLoader = ResourceLoader::GetForViewIndependentUse(L"CEngineStrings");
|
||||
}
|
||||
|
||||
AppResourceProvider & AppResourceProvider::GetInstance()
|
||||
{
|
||||
static AppResourceProvider s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
String^ AppResourceProvider::GetResourceString(_In_ String^ key)
|
||||
{
|
||||
return m_stringResLoader->GetString(key);
|
||||
}
|
||||
|
||||
String^ AppResourceProvider::GetCEngineString(_In_ String^ key)
|
||||
{
|
||||
return m_cEngineStringResLoader->GetString(key);
|
||||
}
|
||||
20
src/CalcViewModel/Common/AppResourceProvider.h
Normal file
20
src/CalcViewModel/Common/AppResourceProvider.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
class AppResourceProvider
|
||||
{
|
||||
public:
|
||||
static AppResourceProvider & GetInstance();
|
||||
Platform::String^ GetResourceString(_In_ Platform::String^ key);
|
||||
Platform::String^ GetCEngineString(_In_ Platform::String^ key);
|
||||
|
||||
private:
|
||||
AppResourceProvider();
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_stringResLoader;
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_cEngineStringResLoader;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "NarratorAnnouncement.h"
|
||||
|
||||
// Declaration of the INarratorAnnouncementHost interface.
|
||||
// This interface exists to hide the concrete announcement host
|
||||
// being used. Depending on the version of the OS the app is running on,
|
||||
// the app may need a host that uses LiveRegionChanged or RaiseNotification.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public interface class INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
// Is the host available on this OS.
|
||||
bool IsHostAvailable();
|
||||
|
||||
// Make a new instance of a concrete host.
|
||||
INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
// Make an announcement using the concrete host's preferred method.
|
||||
void Announce(NarratorAnnouncement^ announcement);
|
||||
};
|
||||
}
|
||||
41
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
41
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
LiveRegionHost::LiveRegionHost() :
|
||||
m_host(nullptr)
|
||||
{}
|
||||
|
||||
bool LiveRegionHost::IsHostAvailable()
|
||||
{
|
||||
// LiveRegion is always available.
|
||||
return true;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ LiveRegionHost::MakeHost()
|
||||
{
|
||||
return ref new LiveRegionHost();
|
||||
}
|
||||
|
||||
void LiveRegionHost::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
AutomationProperties::SetLiveSetting(m_host, AutomationLiveSetting::Assertive);
|
||||
}
|
||||
|
||||
AutomationProperties::SetName(m_host, announcement->Announcement);
|
||||
AutomationPeer^ peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
|
||||
}
|
||||
}
|
||||
32
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
32
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the LiveRegionHost class.
|
||||
// This class announces NarratorAnnouncements using the LiveRegionChanged event.
|
||||
// This event is unreliable and should be deprecated in favor of the new
|
||||
// RaiseNotification API in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
// This class exists so that the app can run on RS2 and use LiveRegions
|
||||
// to host notifiactions on those builds.
|
||||
// When the app switches to min version RS3, this class can be removed
|
||||
// and the app will switch to using the Notification API.
|
||||
// TODO - MSFT 12735088
|
||||
public ref class LiveRegionHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
LiveRegionHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement^ m_host;
|
||||
};
|
||||
}
|
||||
144
src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp
Normal file
144
src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorAnnouncement.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Platform;
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
namespace CalculatorActivityIds
|
||||
{
|
||||
StringReference DisplayUpdated(L"DisplayUpdated");
|
||||
StringReference MaxDigitsReached(L"MaxDigitsReached");
|
||||
StringReference MemoryCleared(L"MemoryCleared");
|
||||
StringReference MemoryItemChanged(L"MemorySlotChanged");
|
||||
StringReference MemoryItemAdded(L"MemorySlotAdded");
|
||||
StringReference HistoryCleared(L"HistoryCleared");
|
||||
StringReference CategoryNameChanged(L"CategoryNameChanged");
|
||||
StringReference UpdateCurrencyRates(L"UpdateCurrencyRates");
|
||||
StringReference DisplayCopied(L"DisplayCopied");
|
||||
}
|
||||
}
|
||||
|
||||
NarratorAnnouncement::NarratorAnnouncement(
|
||||
String^ announcement,
|
||||
String^ activityId,
|
||||
AutomationNotificationKind kind,
|
||||
AutomationNotificationProcessing processing)
|
||||
:
|
||||
m_announcement(announcement),
|
||||
m_activityId(activityId),
|
||||
m_kind(kind),
|
||||
m_processing(processing)
|
||||
{}
|
||||
|
||||
String^ NarratorAnnouncement::Announcement::get()
|
||||
{
|
||||
return m_announcement;
|
||||
}
|
||||
|
||||
String^ NarratorAnnouncement::ActivityId::get()
|
||||
{
|
||||
return m_activityId;
|
||||
}
|
||||
|
||||
AutomationNotificationKind NarratorAnnouncement::Kind::get()
|
||||
{
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
AutomationNotificationProcessing NarratorAnnouncement::Processing::get()
|
||||
{
|
||||
return m_processing;
|
||||
}
|
||||
|
||||
bool NarratorAnnouncement::IsValid(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
return announcement != nullptr
|
||||
&& announcement->Announcement != nullptr
|
||||
&& !announcement->Announcement->IsEmpty();
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::DisplayUpdated,
|
||||
AutomationNotificationKind::Other,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MaxDigitsReached,
|
||||
AutomationNotificationKind::Other,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryClearedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryCleared,
|
||||
AutomationNotificationKind::ItemRemoved,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryItemChanged,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::MemoryItemAdded,
|
||||
AutomationNotificationKind::ItemAdded,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::HistoryCleared,
|
||||
AutomationNotificationKind::ItemRemoved,
|
||||
AutomationNotificationProcessing::MostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::CategoryNameChanged,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::UpdateCurrencyRates,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayCopiedAnnouncement(String^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::DisplayCopied,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
94
src/CalcViewModel/Common/Automation/NarratorAnnouncement.h
Normal file
94
src/CalcViewModel/Common/Automation/NarratorAnnouncement.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
// These enum types are copied from the types available in
|
||||
// Windows::UI::Xaml::Automation::Peers in the RS3 SDK.
|
||||
// When this app switches to min version RS3, these custom
|
||||
// enums should be removed and the Windows types should be used
|
||||
// instead.
|
||||
// TODO - MSFT 12735088
|
||||
public enum class AutomationNotificationKind
|
||||
{
|
||||
ItemAdded = 0,
|
||||
ItemRemoved = 1,
|
||||
ActionCompleted = 2,
|
||||
ActionAborted = 3,
|
||||
Other = 4
|
||||
};
|
||||
|
||||
public enum class AutomationNotificationProcessing
|
||||
{
|
||||
ImportantAll = 0,
|
||||
ImportantMostRecent = 1,
|
||||
All = 2,
|
||||
MostRecent = 3,
|
||||
CurrentThenMostRecent = 4
|
||||
};
|
||||
|
||||
public ref class NarratorAnnouncement sealed
|
||||
{
|
||||
public:
|
||||
property Platform::String^ Announcement
|
||||
{
|
||||
Platform::String^ get();
|
||||
}
|
||||
|
||||
property Platform::String^ ActivityId
|
||||
{
|
||||
Platform::String^ get();
|
||||
}
|
||||
|
||||
property AutomationNotificationKind Kind
|
||||
{
|
||||
AutomationNotificationKind get();
|
||||
}
|
||||
|
||||
property AutomationNotificationProcessing Processing
|
||||
{
|
||||
AutomationNotificationProcessing get();
|
||||
}
|
||||
|
||||
static bool IsValid(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
// Make CalculatorAnnouncement a friend class so it is the only
|
||||
// class that can access the private constructor.
|
||||
friend class CalculatorAnnouncement;
|
||||
|
||||
NarratorAnnouncement(
|
||||
Platform::String^ announcement,
|
||||
Platform::String^ activityId,
|
||||
AutomationNotificationKind kind,
|
||||
AutomationNotificationProcessing processing);
|
||||
|
||||
Platform::String^ m_announcement;
|
||||
Platform::String^ m_activityId;
|
||||
AutomationNotificationKind m_kind;
|
||||
AutomationNotificationProcessing m_processing;
|
||||
};
|
||||
|
||||
// CalculatorAnnouncement is intended to contain only static methods
|
||||
// that return announcements made for the Calculator app.
|
||||
class CalculatorAnnouncement
|
||||
{
|
||||
public:
|
||||
static NarratorAnnouncement^ GetDisplayUpdatedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMaxDigitsReachedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetMemoryClearedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMemoryItemChangedAnnouncement(Platform::String^ announcement);
|
||||
static NarratorAnnouncement^ GetMemoryItemAddedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetHistoryClearedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetCategoryNameChangedAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetUpdateCurrencyRatesAnnouncement(Platform::String^ announcement);
|
||||
|
||||
static NarratorAnnouncement^ GetDisplayCopiedAnnouncement(Platform::String^ announcement);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorAnnouncementHostFactory.h"
|
||||
#include "NotificationHost.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace std;
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::s_hostProducer;
|
||||
vector<INarratorAnnouncementHost^> NarratorAnnouncementHostFactory::s_hosts;
|
||||
|
||||
// This static variable is used only to call the initialization function, to initialize the other static variables.
|
||||
int NarratorAnnouncementHostFactory::s_init = NarratorAnnouncementHostFactory::Initialize();
|
||||
int NarratorAnnouncementHostFactory::Initialize()
|
||||
{
|
||||
RegisterHosts();
|
||||
NarratorAnnouncementHostFactory::s_hostProducer = GetHostProducer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For now, there are two type of announcement hosts.
|
||||
// We'd prefer to use Notification if it's available and fallback to LiveRegion
|
||||
// if not. The availabilty of the host depends on the version of the OS the app is running on.
|
||||
// When the app switches to min version RS3, the LiveRegionHost can be removed and we will always
|
||||
// use NotificationHost.
|
||||
// TODO - MSFT 12735088
|
||||
void NarratorAnnouncementHostFactory::RegisterHosts()
|
||||
{
|
||||
// The host that will be used is the first available host,
|
||||
// therefore, order of hosts is important here.
|
||||
NarratorAnnouncementHostFactory::s_hosts = {
|
||||
ref new NotificationHost(),
|
||||
ref new LiveRegionHost()
|
||||
};
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::GetHostProducer()
|
||||
{
|
||||
for (INarratorAnnouncementHost^ host : NarratorAnnouncementHostFactory::s_hosts)
|
||||
{
|
||||
if (host->IsHostAvailable())
|
||||
{
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
assert(false && L"No suitable AnnouncementHost was found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::MakeHost()
|
||||
{
|
||||
if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr)
|
||||
{
|
||||
assert(false && L"No host producer has been assigned.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NarratorAnnouncementHostFactory::s_hostProducer->MakeHost();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NarratorAnnouncementHostFactory class.
|
||||
// This class exists to hide the construction of a concrete INarratorAnnouncementHost.
|
||||
// Depending on the version of the OS the app is running on, the factory will return
|
||||
// an announcement host appropriate for that version.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
class NarratorAnnouncementHostFactory
|
||||
{
|
||||
public:
|
||||
static INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
private:
|
||||
NarratorAnnouncementHostFactory() {}
|
||||
|
||||
static int Initialize();
|
||||
static void RegisterHosts();
|
||||
static INarratorAnnouncementHost^ GetHostProducer();
|
||||
|
||||
private:
|
||||
static int s_init;
|
||||
static INarratorAnnouncementHost^ s_hostProducer;
|
||||
static std::vector<INarratorAnnouncementHost^> s_hosts;
|
||||
};
|
||||
}
|
||||
50
src/CalcViewModel/Common/Automation/NarratorNotifier.cpp
Normal file
50
src/CalcViewModel/Common/Automation/NarratorNotifier.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Implementation of the NarratorNotifier class.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorNotifier.h"
|
||||
#include "NarratorAnnouncementHostFactory.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
DependencyProperty^ NarratorNotifier::s_announcementProperty;
|
||||
|
||||
NarratorNotifier::NarratorNotifier()
|
||||
{
|
||||
m_announcementHost = NarratorAnnouncementHostFactory::MakeHost();
|
||||
}
|
||||
|
||||
void NarratorNotifier::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (NarratorAnnouncement::IsValid(announcement)
|
||||
&& m_announcementHost != nullptr)
|
||||
{
|
||||
m_announcementHost->Announce(announcement);
|
||||
}
|
||||
}
|
||||
|
||||
void NarratorNotifier::RegisterDependencyProperties()
|
||||
{
|
||||
s_announcementProperty = DependencyProperty::Register(
|
||||
L"Announcement", // The name of the dependency property.
|
||||
NarratorAnnouncement::typeid, // The type of the dependency property.
|
||||
NarratorNotifier::typeid, // The owner of the dependency property.
|
||||
ref new PropertyMetadata(
|
||||
nullptr, // Default value of the dependency property.
|
||||
ref new PropertyChangedCallback(OnAnnouncementChanged)));
|
||||
}
|
||||
|
||||
void NarratorNotifier::OnAnnouncementChanged(_In_ DependencyObject^ dependencyObject, _In_ DependencyPropertyChangedEventArgs^ e)
|
||||
{
|
||||
auto instance = safe_cast<NarratorNotifier^>(dependencyObject);
|
||||
if (instance != nullptr)
|
||||
{
|
||||
instance->Announce(safe_cast<NarratorAnnouncement^>(e->NewValue));
|
||||
}
|
||||
}
|
||||
57
src/CalcViewModel/Common/Automation/NarratorNotifier.h
Normal file
57
src/CalcViewModel/Common/Automation/NarratorNotifier.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// Declaration of the NarratorNotifier class.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public ref class NarratorNotifier sealed : public Windows::UI::Xaml::DependencyObject
|
||||
{
|
||||
public:
|
||||
NarratorNotifier();
|
||||
|
||||
void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
property NarratorAnnouncement^ Announcement
|
||||
{
|
||||
NarratorAnnouncement^ get() { return GetAnnouncement(this); }
|
||||
void set(NarratorAnnouncement^ value)
|
||||
{
|
||||
SetAnnouncement(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void RegisterDependencyProperties();
|
||||
|
||||
static property Windows::UI::Xaml::DependencyProperty^ AnnouncementProperty
|
||||
{
|
||||
Windows::UI::Xaml::DependencyProperty^ get()
|
||||
{
|
||||
return s_announcementProperty;
|
||||
}
|
||||
}
|
||||
|
||||
static NarratorAnnouncement^ GetAnnouncement(Windows::UI::Xaml::DependencyObject^ element)
|
||||
{
|
||||
return safe_cast<NarratorAnnouncement^>(element->GetValue(s_announcementProperty));
|
||||
}
|
||||
|
||||
static void SetAnnouncement(Windows::UI::Xaml::DependencyObject^ element, NarratorAnnouncement^ value)
|
||||
{
|
||||
element->SetValue(s_announcementProperty, value);
|
||||
}
|
||||
|
||||
private:
|
||||
static void OnAnnouncementChanged(
|
||||
_In_ Windows::UI::Xaml::DependencyObject^ dependencyObject,
|
||||
_In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ eventArgs);
|
||||
|
||||
static Windows::UI::Xaml::DependencyProperty^ s_announcementProperty;
|
||||
|
||||
private:
|
||||
INarratorAnnouncementHost^ m_announcementHost;
|
||||
};
|
||||
}
|
||||
99
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
99
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NotificationHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::Foundation::Metadata;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
NotificationHost::NotificationHost() :
|
||||
m_host(nullptr)
|
||||
{}
|
||||
|
||||
bool NotificationHost::IsHostAvailable()
|
||||
{
|
||||
return ApiInformation::IsMethodPresent(
|
||||
L"Windows.UI.Xaml.Automation.Peers.AutomationPeer",
|
||||
L"RaiseNotificationEvent");
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost^ NotificationHost::MakeHost()
|
||||
{
|
||||
return ref new NotificationHost();
|
||||
}
|
||||
|
||||
void NotificationHost::Announce(NarratorAnnouncement^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
}
|
||||
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseNotificationEvent(
|
||||
GetWindowsNotificationKind(announcement->Kind),
|
||||
GetWindowsNotificationProcessing(announcement->Processing),
|
||||
announcement->Announcement,
|
||||
announcement->ActivityId);
|
||||
}
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(
|
||||
CustomPeers::AutomationNotificationKind customKindType)
|
||||
{
|
||||
switch (customKindType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationKind::ItemAdded:
|
||||
return StandardPeers::AutomationNotificationKind::ItemAdded;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ItemRemoved:
|
||||
return StandardPeers::AutomationNotificationKind::ItemRemoved;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionCompleted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionCompleted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionAborted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionAborted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::Other:
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationKind");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationProcessing NotificationHost::GetWindowsNotificationProcessing(
|
||||
CustomPeers::AutomationNotificationProcessing customProcessingType)
|
||||
{
|
||||
switch (customProcessingType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantAll:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantAll;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::All:
|
||||
return StandardPeers::AutomationNotificationProcessing::All;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::MostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::MostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::CurrentThenMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::CurrentThenMostRecent;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationProcessing");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
}
|
||||
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NotificationHost class.
|
||||
// This class announces NarratorAnnouncements using the RaiseNotification API
|
||||
// available in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public ref class NotificationHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
NotificationHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement^ announcement);
|
||||
|
||||
private:
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind GetWindowsNotificationKind(
|
||||
CalculatorApp::Common::Automation::AutomationNotificationKind customKindType);
|
||||
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing GetWindowsNotificationProcessing(
|
||||
CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement^ m_host;
|
||||
};
|
||||
}
|
||||
|
||||
34
src/CalcViewModel/Common/BindableBase.cpp
Normal file
34
src/CalcViewModel/Common/BindableBase.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "BindableBase.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
/// <summary>
|
||||
/// Notifies listeners that a property value has changed.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property used to notify listeners.</param>
|
||||
void BindableBase::OnPropertyChanged(String^ propertyName)
|
||||
{
|
||||
PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetCustomProperty(Platform::String^ name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Data::ICustomProperty^ BindableBase::GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Platform::String^ BindableBase::GetStringRepresentation()
|
||||
{
|
||||
return this->ToString();
|
||||
}
|
||||
35
src/CalcViewModel/Common/BindableBase.h
Normal file
35
src/CalcViewModel/Common/BindableBase.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class BindableBase : Windows::UI::Xaml::DependencyObject, Windows::UI::Xaml::Data::INotifyPropertyChanged, Windows::UI::Xaml::Data::ICustomPropertyProvider
|
||||
{
|
||||
public:
|
||||
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
|
||||
|
||||
public:
|
||||
// ICustomPropertyProvider
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name);
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type);
|
||||
virtual Platform::String^ GetStringRepresentation();
|
||||
|
||||
property Windows::UI::Xaml::Interop::TypeName Type
|
||||
{
|
||||
virtual Windows::UI::Xaml::Interop::TypeName get() { return this->GetType(); }
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual void OnPropertyChanged(Platform::String^ propertyName);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CalculatorButtonPressedEventArgs.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
|
||||
NumbersAndOperatorsEnum CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(_In_ Object^ commandParameter)
|
||||
{
|
||||
auto eventArgs = dynamic_cast<CalculatorButtonPressedEventArgs^>(commandParameter);
|
||||
if (eventArgs != nullptr)
|
||||
{
|
||||
return eventArgs->Operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
return safe_cast<NumbersAndOperatorsEnum>(commandParameter);
|
||||
}
|
||||
}
|
||||
|
||||
String^ CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(_In_ Object^ commandParameter)
|
||||
{
|
||||
auto eventArgs = dynamic_cast<CalculatorButtonPressedEventArgs^>(commandParameter);
|
||||
if (eventArgs != nullptr)
|
||||
{
|
||||
return eventArgs->AuditoryFeedback;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h
Normal file
24
src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
public ref class CalculatorButtonPressedEventArgs sealed
|
||||
{
|
||||
public:
|
||||
PROPERTY_R(Platform::String^, AuditoryFeedback);
|
||||
PROPERTY_R(CalculatorApp::NumbersAndOperatorsEnum, Operation);
|
||||
|
||||
CalculatorButtonPressedEventArgs(
|
||||
Platform::String^ feedback, CalculatorApp::NumbersAndOperatorsEnum operation) :
|
||||
m_AuditoryFeedback(feedback), m_Operation(operation) {}
|
||||
|
||||
static CalculatorApp::NumbersAndOperatorsEnum GetOperationFromCommandParameter(_In_ Platform::Object^ commandParameter);
|
||||
static Platform::String^ GetAuditoryFeedbackFromCommandParameter(_In_ Platform::Object^ commandParameter);
|
||||
};
|
||||
}
|
||||
}
|
||||
211
src/CalcViewModel/Common/CalculatorButtonUser.h
Normal file
211
src/CalcViewModel/Common/CalculatorButtonUser.h
Normal file
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace CM = CalculationManager;
|
||||
|
||||
public enum class NumbersAndOperatorsEnum
|
||||
{
|
||||
Zero = (int) CM::Command::Command0,
|
||||
One = (int) CM::Command::Command1,
|
||||
Two = (int) CM::Command::Command2,
|
||||
Three = (int) CM::Command::Command3,
|
||||
Four = (int) CM::Command::Command4,
|
||||
Five = (int) CM::Command::Command5,
|
||||
Six = (int) CM::Command::Command6,
|
||||
Seven = (int) CM::Command::Command7,
|
||||
Eight = (int) CM::Command::Command8,
|
||||
Nine = (int) CM::Command::Command9,
|
||||
Add = (int) CM::Command::CommandADD,
|
||||
Subtract = (int) CM::Command::CommandSUB,
|
||||
Multiply = (int) CM::Command::CommandMUL,
|
||||
Divide = (int) CM::Command::CommandDIV,
|
||||
Invert = (int) CM::Command::CommandREC,
|
||||
Equals = (int) CM::Command::CommandEQU,
|
||||
Decimal = (int) CM::Command::CommandPNT,
|
||||
Sqrt = (int) CM::Command::CommandSQRT,
|
||||
Percent = (int) CM::Command::CommandPERCENT,
|
||||
Negate = (int) CM::Command::CommandSIGN,
|
||||
Backspace = (int) CM::Command::CommandBACK,
|
||||
ClearEntry = (int) CM::Command::CommandCENTR,
|
||||
Clear = (int) CM::Command::CommandCLEAR,
|
||||
Degree = (int) CM::Command::CommandDEG,
|
||||
Radians = (int) CM::Command::CommandRAD,
|
||||
Grads = (int) CM::Command::CommandGRAD,
|
||||
Degrees = (int) CM::Command::CommandDegrees,
|
||||
OpenParenthesis = (int) CM::Command::CommandOPENP,
|
||||
CloseParenthesis = (int) CM::Command::CommandCLOSEP,
|
||||
Pi = (int) CM::Command::CommandPI,
|
||||
Sin = (int) CM::Command::CommandSIN,
|
||||
Cos = (int) CM::Command::CommandCOS,
|
||||
Tan = (int) CM::Command::CommandTAN,
|
||||
Factorial = (int) CM::Command::CommandFAC,
|
||||
XPower2 = (int) CM::Command::CommandSQR,
|
||||
Mod = (int) CM::Command::CommandMOD,
|
||||
FToE = (int) CM::Command::CommandFE,
|
||||
LogBaseE = (int) CM::Command::CommandLN,
|
||||
InvSin = (int) CM::Command::CommandASIN,
|
||||
InvCos = (int) CM::Command::CommandACOS,
|
||||
InvTan = (int) CM::Command::CommandATAN,
|
||||
LogBase10 = (int) CM::Command::CommandLOG,
|
||||
XPowerY = (int) CM::Command::CommandPWR,
|
||||
YRootX = (int) CM::Command::CommandROOT,
|
||||
TenPowerX = (int) CM::Command::CommandPOW10,
|
||||
EPowerX = (int) CM::Command::CommandPOWE,
|
||||
Exp = (int) CM::Command::CommandEXP,
|
||||
IsScientificMode = (int) CM::Command::ModeScientific,
|
||||
IsStandardMode = (int) CM::Command::ModeBasic,
|
||||
None = (int) CM::Command::CommandNULL,
|
||||
IsProgrammerMode = (int) CM::Command::ModeProgrammer,
|
||||
DecButton = (int) CM::Command::CommandDec,
|
||||
OctButton = (int) CM::Command::CommandOct,
|
||||
HexButton = (int) CM::Command::CommandHex,
|
||||
BinButton = (int) CM::Command::CommandBin,
|
||||
And = (int) CM::Command::CommandAnd,
|
||||
Ror = (int) CM::Command::CommandROR,
|
||||
Rol = (int) CM::Command::CommandROL,
|
||||
Or = (int) CM::Command::CommandOR,
|
||||
Lsh = (int) CM::Command::CommandLSHF,
|
||||
Rsh = (int) CM::Command::CommandRSHF,
|
||||
Xor = (int) CM::Command::CommandXor,
|
||||
Not = (int) CM::Command::CommandNot,
|
||||
A = (int) CM::Command::CommandA,
|
||||
B = (int) CM::Command::CommandB,
|
||||
C = (int) CM::Command::CommandC,
|
||||
D = (int) CM::Command::CommandD,
|
||||
E = (int) CM::Command::CommandE,
|
||||
F = (int) CM::Command::CommandF,
|
||||
Memory, // This is the memory button. Doesn't have a direct mapping to the CalcEngine.
|
||||
Sinh = (int) CM::Command::CommandSINH,
|
||||
Cosh = (int) CM::Command::CommandCOSH,
|
||||
Tanh = (int) CM::Command::CommandTANH,
|
||||
InvSinh = (int) CM::Command::CommandASINH,
|
||||
InvCosh = (int) CM::Command::CommandACOSH,
|
||||
InvTanh = (int) CM::Command::CommandATANH,
|
||||
Qword = (int) CM::Command::CommandQword,
|
||||
Dword = (int) CM::Command::CommandDword,
|
||||
Word = (int) CM::Command::CommandWord,
|
||||
Byte = (int) CM::Command::CommandByte,
|
||||
Cube = (int) CM::Command::CommandCUB,
|
||||
DMS = (int) CM::Command::CommandDMS,
|
||||
|
||||
BINSTART = (int) CM::Command::CommandBINEDITSTART,
|
||||
BINPOS0 = (int) CM::Command::CommandBINPOS0,
|
||||
BINPOS1 = (int) CM::Command::CommandBINPOS1,
|
||||
BINPOS2 = (int) CM::Command::CommandBINPOS2,
|
||||
BINPOS3 = (int) CM::Command::CommandBINPOS3,
|
||||
BINPOS4 = (int) CM::Command::CommandBINPOS4,
|
||||
BINPOS5 = (int) CM::Command::CommandBINPOS5,
|
||||
BINPOS6 = (int) CM::Command::CommandBINPOS6,
|
||||
BINPOS7 = (int) CM::Command::CommandBINPOS7,
|
||||
BINPOS8 = (int) CM::Command::CommandBINPOS8,
|
||||
BINPOS9 = (int) CM::Command::CommandBINPOS9,
|
||||
BINPOS10 = (int) CM::Command::CommandBINPOS10,
|
||||
BINPOS11 = (int) CM::Command::CommandBINPOS11,
|
||||
BINPOS12 = (int) CM::Command::CommandBINPOS12,
|
||||
BINPOS13 = (int) CM::Command::CommandBINPOS13,
|
||||
BINPOS14 = (int) CM::Command::CommandBINPOS14,
|
||||
BINPOS15 = (int) CM::Command::CommandBINPOS15,
|
||||
BINPOS16 = (int) CM::Command::CommandBINPOS16,
|
||||
BINPOS17 = (int) CM::Command::CommandBINPOS17,
|
||||
BINPOS18 = (int) CM::Command::CommandBINPOS18,
|
||||
BINPOS19 = (int) CM::Command::CommandBINPOS19,
|
||||
BINPOS20 = (int) CM::Command::CommandBINPOS20,
|
||||
BINPOS21 = (int) CM::Command::CommandBINPOS21,
|
||||
BINPOS22 = (int) CM::Command::CommandBINPOS22,
|
||||
BINPOS23 = (int) CM::Command::CommandBINPOS23,
|
||||
BINPOS24 = (int) CM::Command::CommandBINPOS24,
|
||||
BINPOS25 = (int) CM::Command::CommandBINPOS25,
|
||||
BINPOS26 = (int) CM::Command::CommandBINPOS26,
|
||||
BINPOS27 = (int) CM::Command::CommandBINPOS27,
|
||||
BINPOS28 = (int) CM::Command::CommandBINPOS28,
|
||||
BINPOS29 = (int) CM::Command::CommandBINPOS29,
|
||||
BINPOS30 = (int) CM::Command::CommandBINPOS30,
|
||||
BINPOS31 = (int) CM::Command::CommandBINPOS31,
|
||||
BINPOS32 = (int) CM::Command::CommandBINPOS32,
|
||||
BINPOS33 = (int) CM::Command::CommandBINPOS33,
|
||||
BINPOS34 = (int) CM::Command::CommandBINPOS34,
|
||||
BINPOS35 = (int) CM::Command::CommandBINPOS35,
|
||||
BINPOS36 = (int) CM::Command::CommandBINPOS36,
|
||||
BINPOS37 = (int) CM::Command::CommandBINPOS37,
|
||||
BINPOS38 = (int) CM::Command::CommandBINPOS38,
|
||||
BINPOS39 = (int) CM::Command::CommandBINPOS39,
|
||||
BINPOS40 = (int) CM::Command::CommandBINPOS40,
|
||||
BINPOS41 = (int) CM::Command::CommandBINPOS41,
|
||||
BINPOS42 = (int) CM::Command::CommandBINPOS42,
|
||||
BINPOS43 = (int) CM::Command::CommandBINPOS43,
|
||||
BINPOS44 = (int) CM::Command::CommandBINPOS44,
|
||||
BINPOS45 = (int) CM::Command::CommandBINPOS45,
|
||||
BINPOS46 = (int) CM::Command::CommandBINPOS46,
|
||||
BINPOS47 = (int) CM::Command::CommandBINPOS47,
|
||||
BINPOS48 = (int) CM::Command::CommandBINPOS48,
|
||||
BINPOS49 = (int) CM::Command::CommandBINPOS49,
|
||||
BINPOS50 = (int) CM::Command::CommandBINPOS50,
|
||||
BINPOS51 = (int) CM::Command::CommandBINPOS51,
|
||||
BINPOS52 = (int) CM::Command::CommandBINPOS52,
|
||||
BINPOS53 = (int) CM::Command::CommandBINPOS53,
|
||||
BINPOS54 = (int) CM::Command::CommandBINPOS54,
|
||||
BINPOS55 = (int) CM::Command::CommandBINPOS55,
|
||||
BINPOS56 = (int) CM::Command::CommandBINPOS56,
|
||||
BINPOS57 = (int) CM::Command::CommandBINPOS57,
|
||||
BINPOS58 = (int) CM::Command::CommandBINPOS58,
|
||||
BINPOS59 = (int) CM::Command::CommandBINPOS59,
|
||||
BINPOS60 = (int) CM::Command::CommandBINPOS60,
|
||||
BINPOS61 = (int) CM::Command::CommandBINPOS61,
|
||||
BINPOS62 = (int) CM::Command::CommandBINPOS62,
|
||||
BINPOS63 = (int) CM::Command::CommandBINPOS63,
|
||||
BINEND = (int) CM::Command::CommandBINEDITEND,
|
||||
Hyp = (int) CM::Command::CommandHYP
|
||||
};
|
||||
|
||||
// This contains list of functions whose usage we are tracelogging
|
||||
public enum class FunctionLogEnum
|
||||
{
|
||||
Invert = (int) CM::Command::CommandREC,
|
||||
Sqrt = (int) CM::Command::CommandSQRT,
|
||||
Percent = (int) CM::Command::CommandPERCENT,
|
||||
Negate = (int) CM::Command::CommandSIGN,
|
||||
Degrees = (int) CM::Command::CommandDegrees,
|
||||
Pi = (int) CM::Command::CommandPI,
|
||||
Sin = (int) CM::Command::CommandSIN,
|
||||
Cos = (int) CM::Command::CommandCOS,
|
||||
Tan = (int) CM::Command::CommandTAN,
|
||||
Factorial = (int) CM::Command::CommandFAC,
|
||||
XPower2 = (int) CM::Command::CommandSQR,
|
||||
Mod = (int) CM::Command::CommandMOD,
|
||||
FToE = (int) CM::Command::CommandFE,
|
||||
LogBaseE = (int) CM::Command::CommandLN,
|
||||
InvSin = (int) CM::Command::CommandASIN,
|
||||
InvCos = (int) CM::Command::CommandACOS,
|
||||
InvTan = (int) CM::Command::CommandATAN,
|
||||
LogBase10 = (int) CM::Command::CommandLOG,
|
||||
XPowerY = (int) CM::Command::CommandPWR,
|
||||
YRootX = (int) CM::Command::CommandROOT,
|
||||
TenPowerX = (int) CM::Command::CommandPOW10,
|
||||
EPowerX = (int) CM::Command::CommandPOWE,
|
||||
Exp = (int) CM::Command::CommandEXP,
|
||||
DecButton = (int) CM::Command::CommandDec,
|
||||
OctButton = (int) CM::Command::CommandOct,
|
||||
HexButton = (int) CM::Command::CommandHex,
|
||||
BinButton = (int) CM::Command::CommandBin,
|
||||
And = (int) CM::Command::CommandAnd,
|
||||
Ror = (int) CM::Command::CommandROR,
|
||||
Rol = (int) CM::Command::CommandROL,
|
||||
Or = (int) CM::Command::CommandOR,
|
||||
Lsh = (int) CM::Command::CommandLSHF,
|
||||
Rsh = (int) CM::Command::CommandRSHF,
|
||||
Xor = (int) CM::Command::CommandXor,
|
||||
Not = (int) CM::Command::CommandNot,
|
||||
Sinh = (int) CM::Command::CommandSINH,
|
||||
Cosh = (int) CM::Command::CommandCOSH,
|
||||
Tanh = (int) CM::Command::CommandTANH,
|
||||
InvSinh = (int) CM::Command::CommandASINH,
|
||||
InvCosh = (int) CM::Command::CommandACOSH,
|
||||
InvTanh = (int) CM::Command::CommandATANH,
|
||||
Cube = (int) CM::Command::CommandCUB,
|
||||
DMS = (int) CM::Command::CommandDMS,
|
||||
};
|
||||
}
|
||||
134
src/CalcViewModel/Common/CalculatorDisplay.cpp
Normal file
134
src/CalcViewModel/Common/CalculatorDisplay.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// This class provides the concrete implemenation for the ICalcDisplay interface
|
||||
// that is declared in the Calculation Manager Library.
|
||||
#include "pch.h"
|
||||
#include "CalculatorDisplay.h"
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculationManager;
|
||||
using namespace std;
|
||||
|
||||
CalculatorDisplay::CalculatorDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetCallback(Platform::WeakReference callbackReference)
|
||||
{
|
||||
m_callbackReference = callbackReference;
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetHistoryCallback(Platform::WeakReference callbackReference)
|
||||
{
|
||||
m_historyCallbackReference = callbackReference;
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetPrimaryDisplay(_In_ const wstring& displayStringValue, _In_ bool isError)
|
||||
{
|
||||
if (m_callbackReference)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetPrimaryDisplay(displayStringValue, isError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetParenDisplayText(_In_ const std::wstring& parenthesisCount)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetParenthesisCount(parenthesisCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetIsInError(bool isError)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->IsInError = isError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector <std::shared_ptr<IExpressionCommand>>> const &commands)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetExpressionDisplay(tokens, commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::SetMemorizedNumbers(_In_ const vector<std::wstring>& newMemorizedNumbers)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->SetMemorizedNumbers(newMemorizedNumbers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::OnHistoryItemAdded(_In_ unsigned int addedItemIndex)
|
||||
{
|
||||
if (m_historyCallbackReference != nullptr)
|
||||
{
|
||||
auto historyVM = m_historyCallbackReference.Resolve<ViewModel::HistoryViewModel>();
|
||||
if (historyVM)
|
||||
{
|
||||
historyVM->OnHistoryItemAdded(addedItemIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::MaxDigitsReached()
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnMaxDigitsReached();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::BinaryOperatorReceived()
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnBinaryOperatorReceived();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatorDisplay::MemoryItemChanged(unsigned int indexOfMemory)
|
||||
{
|
||||
if (m_callbackReference != nullptr)
|
||||
{
|
||||
auto calcVM = m_callbackReference.Resolve<ViewModel::StandardCalculatorViewModel>();
|
||||
if (calcVM)
|
||||
{
|
||||
calcVM->OnMemoryItemChanged(indexOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/CalcViewModel/Common/CalculatorDisplay.h
Normal file
31
src/CalcViewModel/Common/CalculatorDisplay.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
// Callback interface to be implemented by the CalculatorManager
|
||||
class CalculatorDisplay: public ICalcDisplay
|
||||
{
|
||||
public:
|
||||
CalculatorDisplay();
|
||||
void SetCallback(Platform::WeakReference callbackReference);
|
||||
void SetHistoryCallback(Platform::WeakReference callbackReference);
|
||||
|
||||
private:
|
||||
void SetPrimaryDisplay(_In_ const std::wstring& displayString, _In_ bool isError) override;
|
||||
void SetIsInError(bool isError) override;
|
||||
void SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands) override;
|
||||
void SetMemorizedNumbers(_In_ const std::vector<std::wstring>& memorizedNumbers) override;
|
||||
void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override;
|
||||
void SetParenDisplayText(_In_ const std::wstring& parenthesisCount) override;
|
||||
void MaxDigitsReached() override;
|
||||
void BinaryOperatorReceived() override;
|
||||
void MemoryItemChanged(unsigned int indexOfMemory) override;
|
||||
|
||||
private:
|
||||
Platform::WeakReference m_callbackReference;
|
||||
Platform::WeakReference m_historyCallbackReference;
|
||||
};
|
||||
}
|
||||
40
src/CalcViewModel/Common/ConversionResultTaskHelper.cpp
Normal file
40
src/CalcViewModel/Common/ConversionResultTaskHelper.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ConversionResultTaskHelper.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace concurrency;
|
||||
using namespace std;
|
||||
|
||||
ConversionResultTaskHelper::ConversionResultTaskHelper(unsigned int delay, const function<void()> functionToRun) :
|
||||
m_delay{ delay },
|
||||
m_storedFunction{ functionToRun }
|
||||
{
|
||||
auto token = m_cts.get_token();
|
||||
auto delayTask = CompleteAfter(delay);
|
||||
delayTask.then([this, token]()
|
||||
{
|
||||
if (!token.is_canceled())
|
||||
{
|
||||
m_storedFunction();
|
||||
}
|
||||
}, task_continuation_context::use_current());
|
||||
}
|
||||
|
||||
ConversionResultTaskHelper::~ConversionResultTaskHelper()
|
||||
{
|
||||
m_cts.cancel();
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
// Creates a task that completes after the specified delay.
|
||||
//
|
||||
// Taken from: How to: Create a Task that Completes After a Delay
|
||||
// https://msdn.microsoft.com/en-us/library/hh873170.aspx
|
||||
task<void> ConversionResultTaskHelper::CompleteAfter(unsigned int timeout)
|
||||
{
|
||||
co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{ std::chrono::duration<uint32_t, std::milli>(timeout) });
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
24
src/CalcViewModel/Common/ConversionResultTaskHelper.h
Normal file
24
src/CalcViewModel/Common/ConversionResultTaskHelper.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class ConversionResultTaskHelper
|
||||
{
|
||||
public:
|
||||
ConversionResultTaskHelper(unsigned int delay, const std::function<void()> functionToRun);
|
||||
~ConversionResultTaskHelper();
|
||||
|
||||
private:
|
||||
concurrency::task<void> CompleteAfter(unsigned int timeout);
|
||||
|
||||
unsigned int m_delay;
|
||||
concurrency::cancellation_token_source m_cts;
|
||||
const std::function<void()> m_storedFunction;
|
||||
};
|
||||
}
|
||||
}
|
||||
569
src/CalcViewModel/Common/CopyPasteManager.cpp
Normal file
569
src/CalcViewModel/Common/CopyPasteManager.cpp
Normal file
@@ -0,0 +1,569 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CopyPasteManager.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace concurrency;
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::System;
|
||||
using namespace Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
size_t maxOperandLength;
|
||||
unsigned long long maxOperandNumber;
|
||||
|
||||
String^ CopyPasteManager::supportedFormats[] =
|
||||
{
|
||||
StandardDataFormats::Text
|
||||
};
|
||||
|
||||
constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" };
|
||||
// [\s\x85] means white-space characters
|
||||
static const wstring c_wspc = L"[\\s\\x85]*";
|
||||
static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc;
|
||||
static const wstring c_wspcRParens = c_wspc + L"[)]*" + c_wspc;
|
||||
static const wstring c_signedDecFloat = L"[-+]?\\d*(\\d|[.])\\d*";
|
||||
|
||||
// Programmer Mode Integer patterns
|
||||
// Support digit separators ` (WinDbg/MASM), ' (C++), and _ (C# and other languages)
|
||||
static const wstring c_hexProgrammerChars = L"([a-f]|[A-F]|\\d)+((_|'|`)([a-f]|[A-F]|\\d)+)*";
|
||||
static const wstring c_decProgrammerChars = L"\\d+((_|'|`)\\d+)*";
|
||||
static const wstring c_octProgrammerChars = L"[0-7]+((_|'|`)[0-7]+)*";
|
||||
static const wstring c_binProgrammerChars = L"[0-1]+((_|'|`)[0-1]+)*";
|
||||
static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}";
|
||||
|
||||
// RegEx Patterns used by various modes
|
||||
static const array<wregex, 1> standardModePatterns =
|
||||
{
|
||||
wregex(c_wspc + c_signedDecFloat + c_wspc)
|
||||
};
|
||||
static const array<wregex, 2> scientificModePatterns =
|
||||
{
|
||||
wregex(c_wspcLParens + c_signedDecFloat + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_signedDecFloat + L"[e]([+]|[-])+\\d+" + c_wspcRParens)
|
||||
};
|
||||
static const array<array<wregex, 5>, 4> programmerModePatterns =
|
||||
{ {
|
||||
// Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens)
|
||||
},
|
||||
// Decimal numbers like -145, 145, 0n145, 123ull etc
|
||||
{
|
||||
wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" +c_wspcRParens),
|
||||
wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens)
|
||||
},
|
||||
// Octal numbers like 06, 010, 0t77, 0o77, 077ull etc
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens)
|
||||
},
|
||||
// Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull
|
||||
{
|
||||
wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens),
|
||||
wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens)
|
||||
}
|
||||
} };
|
||||
static const array<wregex, 1> unitConverterPatterns =
|
||||
{
|
||||
wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc)
|
||||
};
|
||||
|
||||
void CopyPasteManager::CopyToClipboard(String^ stringToCopy)
|
||||
{
|
||||
// Copy the string to the clipboard
|
||||
auto dataPackage = ref new DataPackage();
|
||||
dataPackage->SetText(stringToCopy);
|
||||
Clipboard::SetContent(dataPackage);
|
||||
}
|
||||
|
||||
task<String^> CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
// Retrieve the text in the clipboard
|
||||
auto dataPackageView = Clipboard::GetContent();
|
||||
|
||||
// TODO: Suport all formats supported by ClipboardHasText
|
||||
//-- add support to avoid pasting of expressions like 12 34(as of now we allow 1234)
|
||||
//-- add support to allow pasting for expressions like .2 , -.2
|
||||
//-- add support to allow pasting for expressions like 1.3e12(as of now we allow 1.3e+12)
|
||||
|
||||
return create_task((dataPackageView->GetTextAsync(::StandardDataFormats::Text)))
|
||||
.then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText)
|
||||
{
|
||||
return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType);
|
||||
}
|
||||
, task_continuation_context::use_arbitrary());
|
||||
}
|
||||
|
||||
int CopyPasteManager::ClipboardTextFormat()
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
auto dataPackageView = Clipboard::GetContent();
|
||||
|
||||
for (int i = 0; i < RTL_NUMBER_OF(supportedFormats); i++)
|
||||
{
|
||||
if (dataPackageView->Contains(supportedFormats[i]))
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType);
|
||||
}
|
||||
|
||||
// return "NoOp" if pastedText is invalid else return pastedText
|
||||
|
||||
String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
if (pastedText->Length() > MaxPasteableLength)
|
||||
{
|
||||
// return NoOp to indicate don't paste anything.
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"PastedExpressionSizeGreaterThanMaxAllowed", L"MoreThanMaxInput", mode, programmerNumberBase, bitLengthType);
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
wstring pasteExpression = pastedText->Data();
|
||||
|
||||
// Get english translated expression
|
||||
String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pasteExpression);
|
||||
|
||||
// Removing the spaces, comma separator from the pasteExpression to allow pasting of expressions like 1 + 2+1,333
|
||||
pasteExpression = Utils::RemoveUnwantedCharsFromWstring(englishString->Data());
|
||||
|
||||
// If the last character is an = sign, remove it from the pasteExpression to allow evaluating the result on paste.
|
||||
if (!pasteExpression.empty() && pasteExpression.back() == L'=')
|
||||
{
|
||||
pasteExpression = pasteExpression.substr(0, pasteExpression.length() - 1);
|
||||
}
|
||||
|
||||
// Extract operands from the expression to make regex comparison easy and quick. For whole expression it was taking too much of time.
|
||||
// Operands vector will have the list of operands in the pasteExpression
|
||||
vector<wstring> operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType);
|
||||
if (operands.empty())
|
||||
{
|
||||
// return NoOp to indicate don't paste anything.
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
operands = { pasteExpression };
|
||||
}
|
||||
|
||||
// validate each operand with patterns for different modes
|
||||
if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType))
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"InvalidExpressionForPresentMode", pastedText->Data(), mode, programmerNumberBase, bitLengthType);
|
||||
return StringReference(PasteErrorString);
|
||||
}
|
||||
|
||||
return ref new String(pastedText->Data());
|
||||
}
|
||||
|
||||
vector<wstring> CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
vector<wstring> operands{};
|
||||
size_t lastIndex = 0;
|
||||
bool haveOperator = false;
|
||||
bool startExpCounting = false;
|
||||
bool startOfExpression = true;
|
||||
bool isPreviousOpenParen = false;
|
||||
bool isPreviousOperator = false;
|
||||
|
||||
// This will have the exponent length
|
||||
size_t expLength = 0;
|
||||
for (size_t i = 0; i < pasteExpression.length(); i++)
|
||||
{
|
||||
// if the current character is not a valid one don't process it
|
||||
if (c_validCharacterSet.find(pasteExpression.at(i)) == wstring_view::npos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operands.size() >= MaxOperandCount)
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"OperandCountGreaterThanMaxCount", pasteExpression.c_str(), mode, programmerNumberBase, bitLengthType);
|
||||
operands.clear();
|
||||
return operands;
|
||||
}
|
||||
|
||||
if (startExpCounting)
|
||||
{
|
||||
if ((pasteExpression.at(i) >= L'0') && (pasteExpression.at(i) <= L'9'))
|
||||
{
|
||||
expLength++;
|
||||
|
||||
// to disallow pasting of 1e+12345 as 1e+1234, max exponent that can be pasted is 9999.
|
||||
if (expLength > MaxExponentLength)
|
||||
{
|
||||
TraceLogger::GetInstance().LogInvalidInputPasted(L"ExponentLengthGreaterThanMaxLength", pasteExpression.c_str(), mode, programmerNumberBase, bitLengthType);
|
||||
operands.clear();
|
||||
return operands;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mode != ViewMode::Programmer) && (pasteExpression.at(i) == L'e'))
|
||||
{
|
||||
startExpCounting = true;
|
||||
}
|
||||
|
||||
if (((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-') || (pasteExpression.at(i) == L'*') || (pasteExpression.at(i) == L'/')))
|
||||
{
|
||||
if ((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-'))
|
||||
{
|
||||
// don't break the expression into operands if the encountered character corresponds to sign command(+-)
|
||||
if (isPreviousOpenParen || startOfExpression || isPreviousOperator || ((mode != ViewMode::Programmer) && !((i != 0) && (pasteExpression.at(i - 1) != L'e'))))
|
||||
{
|
||||
isPreviousOperator = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
startExpCounting = false;
|
||||
expLength = 0;
|
||||
haveOperator = true;
|
||||
isPreviousOperator = true;
|
||||
operands.push_back(pasteExpression.substr(lastIndex, i - lastIndex));
|
||||
lastIndex = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
isPreviousOperator = false;
|
||||
}
|
||||
|
||||
isPreviousOpenParen = (pasteExpression.at(i) == L'(');
|
||||
startOfExpression = false;
|
||||
}
|
||||
|
||||
if (!haveOperator)
|
||||
{
|
||||
operands.clear();
|
||||
operands.push_back(pasteExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
operands.push_back(pasteExpression.substr(lastIndex, pasteExpression.length() - 1));
|
||||
}
|
||||
|
||||
return operands;
|
||||
}
|
||||
|
||||
bool CopyPasteManager::ExpressionRegExMatch(vector<wstring> operands, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
if (operands.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool expMatched = true;
|
||||
vector<wregex> patterns{};
|
||||
|
||||
pair<size_t, uint64_t> operandLimits = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType);
|
||||
size_t maxOperandLength = operandLimits.first;
|
||||
uint64_t maxOperandValue = operandLimits.second;
|
||||
|
||||
if (mode == ViewMode::Standard)
|
||||
{
|
||||
patterns.assign(standardModePatterns.begin(), standardModePatterns.end());
|
||||
}
|
||||
else if (mode == ViewMode::Scientific)
|
||||
{
|
||||
patterns.assign(scientificModePatterns.begin(), scientificModePatterns.end());
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
patterns.assign(programmerModePatterns[programmerNumberBase - HexBase].begin(), programmerModePatterns[programmerNumberBase - HexBase].end());
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
patterns.assign(unitConverterPatterns.begin(), unitConverterPatterns.end());
|
||||
}
|
||||
|
||||
for (const wstring& operand : operands)
|
||||
{
|
||||
// Each operand only needs to match one of the available patterns.
|
||||
bool operandMatched = false;
|
||||
for (const wregex& pattern : patterns)
|
||||
{
|
||||
operandMatched = operandMatched || regex_match(operand, pattern);
|
||||
}
|
||||
|
||||
if (operandMatched)
|
||||
{
|
||||
// Remove characters that are valid in the expression but we do not want to include in length calculations
|
||||
// or which will break conversion from string-to-ULL.
|
||||
wstring operandValue = SanitizeOperand(operand);
|
||||
|
||||
// If an operand exceeds the maximum length allowed, break and return.
|
||||
if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLength)
|
||||
{
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If maxOperandValue is set and the operandValue exceeds it, break and return.
|
||||
if (maxOperandValue != 0)
|
||||
{
|
||||
unsigned long long int operandAsULL = 0;
|
||||
if (!TryOperandToULL(operandValue, programmerNumberBase, operandAsULL))
|
||||
{
|
||||
// Operand was empty, received invalid_argument, or received out_of_range. Input is invalid.
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (operandAsULL > maxOperandValue)
|
||||
{
|
||||
expMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expMatched = expMatched && operandMatched;
|
||||
}
|
||||
|
||||
return expMatched;
|
||||
}
|
||||
|
||||
pair<size_t, uint64_t> CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
size_t maxLength = 0;
|
||||
uint64_t maxValue = 0;
|
||||
|
||||
if (mode == ViewMode::Standard)
|
||||
{
|
||||
maxLength = MaxStandardOperandLength;
|
||||
}
|
||||
else if (mode == ViewMode::Scientific)
|
||||
{
|
||||
maxLength = MaxScientificOperandLength;
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
unsigned int bitLength = 0;
|
||||
switch (bitLengthType)
|
||||
{
|
||||
case QwordType:
|
||||
bitLength = 64;
|
||||
break;
|
||||
case DwordType:
|
||||
bitLength = 32;
|
||||
break;
|
||||
case WordType:
|
||||
bitLength = 16;
|
||||
break;
|
||||
case ByteType:
|
||||
bitLength = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
double bitsPerDigit = 0;
|
||||
switch (programmerNumberBase)
|
||||
{
|
||||
case BinBase:
|
||||
bitsPerDigit = log2(2);
|
||||
break;
|
||||
case OctBase:
|
||||
bitsPerDigit = log2(8);
|
||||
break;
|
||||
case DecBase:
|
||||
bitsPerDigit = log2(10);
|
||||
break;
|
||||
case HexBase:
|
||||
bitsPerDigit = log2(16);
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int signBit = (programmerNumberBase == DecBase) ? 1 : 0;
|
||||
|
||||
maxLength = (size_t)ceil((bitLength - signBit) / bitsPerDigit);
|
||||
maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit));
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
maxLength = MaxConverterInputLength;
|
||||
}
|
||||
|
||||
return make_pair(maxLength, maxValue);
|
||||
}
|
||||
|
||||
wstring CopyPasteManager::SanitizeOperand(const wstring& operand)
|
||||
{
|
||||
wchar_t unWantedChars[] = { L'\'', L'_', L'`', L'(', L')', L'-' };
|
||||
|
||||
return Utils::RemoveUnwantedCharsFromWstring(operand, unWantedChars, ARRAYSIZE(unWantedChars));
|
||||
}
|
||||
|
||||
bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, unsigned long long int& result)
|
||||
{
|
||||
result = 0;
|
||||
|
||||
if (operand.length() == 0 || operand.front() == L'-')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default to base10
|
||||
int intBase = 10;
|
||||
switch (numberBase)
|
||||
{
|
||||
case HexBase:
|
||||
intBase = 16;
|
||||
break;
|
||||
case OctBase:
|
||||
intBase = 8;
|
||||
break;
|
||||
case BinBase:
|
||||
intBase = 2;
|
||||
break;
|
||||
case DecBase:
|
||||
intBase = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
wstring::size_type size = 0;
|
||||
try
|
||||
{
|
||||
result = stoull(operand, &size, intBase);
|
||||
return true;
|
||||
}
|
||||
catch (invalid_argument)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
catch (out_of_range)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::OperandLength(wstring operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (mode == ViewMode::Standard || mode == ViewMode::Scientific)
|
||||
{
|
||||
len = StandardScientificOperandLength(operand);
|
||||
}
|
||||
else if (mode == ViewMode::Programmer)
|
||||
{
|
||||
len = ProgrammerOperandLength(operand, programmerNumberBase);
|
||||
}
|
||||
else if (modeType == CategoryGroupType::Converter)
|
||||
{
|
||||
len = operand.length();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::StandardScientificOperandLength(wstring operand)
|
||||
{
|
||||
bool hasDecimal = false;
|
||||
for (size_t i = 0; i < operand.length(); i++)
|
||||
{
|
||||
if (operand[i] == L'.')
|
||||
{
|
||||
hasDecimal = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDecimal)
|
||||
{
|
||||
if (operand.length() >= 2)
|
||||
{
|
||||
if ((operand[0] == L'0') && (operand[1] == L'.'))
|
||||
{
|
||||
return operand.length() - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return operand.length() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operand.length();
|
||||
}
|
||||
|
||||
size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int numberBase)
|
||||
{
|
||||
size_t len = operand.length();
|
||||
|
||||
vector<wstring> prefixes{};
|
||||
vector<wstring> suffixes{};
|
||||
switch (numberBase)
|
||||
{
|
||||
case BinBase:
|
||||
prefixes = { L"0B", L"0Y" };
|
||||
suffixes = { L"B" };
|
||||
break;
|
||||
case DecBase:
|
||||
prefixes = { L"-", L"0N" };
|
||||
break;
|
||||
case OctBase:
|
||||
prefixes = { L"0T", L"0O" };
|
||||
break;
|
||||
case HexBase:
|
||||
prefixes = { L"0X" };
|
||||
suffixes = { L"H" };
|
||||
break;
|
||||
default:
|
||||
// No defined prefixes/suffixes
|
||||
break;
|
||||
}
|
||||
|
||||
// UInt suffixes are common across all modes
|
||||
const array<wstring, 5> uintSuffixes = { L"ULL", L"UL", L"LL", L"U", L"L" };
|
||||
suffixes.insert(suffixes.end(), uintSuffixes.begin(), uintSuffixes.end());
|
||||
|
||||
wstring operandUpper = operand;
|
||||
transform(operandUpper.begin(), operandUpper.end(), operandUpper.begin(), toupper);
|
||||
|
||||
// Detect if there is a suffix and subtract its length
|
||||
// Check suffixes first to allow e.g. "0b" to result in length 1 (value 0), rather than length 0 (no value).
|
||||
for (const wstring& suffix : suffixes)
|
||||
{
|
||||
if (len < suffix.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operandUpper.compare(operandUpper.length() - suffix.length(), suffix.length(), suffix) == 0)
|
||||
{
|
||||
len -= suffix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect if there is a prefix and subtract its length
|
||||
for (const wstring& prefix : prefixes)
|
||||
{
|
||||
if (len < prefix.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (operandUpper.compare(0, prefix.length(), prefix) == 0)
|
||||
{
|
||||
len -= prefix.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
72
src/CalcViewModel/Common/CopyPasteManager.h
Normal file
72
src/CalcViewModel/Common/CopyPasteManager.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
namespace CalculatorUnitTests
|
||||
{
|
||||
class CopyPasteManagerTest;
|
||||
}
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
|
||||
#define QwordType 1
|
||||
#define DwordType 2
|
||||
#define WordType 3
|
||||
#define ByteType 4
|
||||
#define HexBase 5
|
||||
#define DecBase 6
|
||||
#define OctBase 7
|
||||
#define BinBase 8
|
||||
|
||||
class CopyPasteManager
|
||||
{
|
||||
public:
|
||||
static void CopyToClipboard(Platform::String^ stringToCopy);
|
||||
static concurrency::task<Platform::String^> GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static bool HasStringToPaste()
|
||||
{
|
||||
return ClipboardTextFormat() >= 0;
|
||||
}
|
||||
|
||||
static constexpr auto PasteErrorString = L"NoOp";
|
||||
|
||||
private:
|
||||
static int ClipboardTextFormat();
|
||||
static Platform::String^ ValidatePasteExpression(
|
||||
Platform::String^ pastedText,
|
||||
CalculatorApp::Common::ViewMode mode,
|
||||
int programmerNumberBase,
|
||||
int bitLengthType);
|
||||
static Platform::String^ ValidatePasteExpression(
|
||||
Platform::String^ pastedText,
|
||||
CalculatorApp::Common::ViewMode mode,
|
||||
CalculatorApp::Common::CategoryGroupType modeType,
|
||||
int programmerNumberBase,
|
||||
int bitLengthType);
|
||||
|
||||
static std::vector<std::wstring> ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static bool ExpressionRegExMatch(std::vector<std::wstring> operands, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
|
||||
static std::pair<size_t, uint64_t> GetMaxOperandLengthAndValue(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1);
|
||||
static std::wstring SanitizeOperand(const std::wstring& operand);
|
||||
static bool TryOperandToULL(const std::wstring& operand, int numberBase, unsigned long long int& result);
|
||||
static size_t OperandLength(std::wstring operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1);
|
||||
static size_t StandardScientificOperandLength(std::wstring operand);
|
||||
static size_t ProgrammerOperandLength(const std::wstring& operand, int numberBase);
|
||||
|
||||
static constexpr size_t MaxStandardOperandLength = 16;
|
||||
static constexpr size_t MaxScientificOperandLength = 32;
|
||||
static constexpr size_t MaxConverterInputLength = 16;
|
||||
static constexpr size_t MaxOperandCount = 100;
|
||||
static constexpr size_t MaxPasteableLength = 512;
|
||||
static constexpr size_t MaxExponentLength = 4;
|
||||
static constexpr size_t MaxProgrammerBitLength = 64;
|
||||
|
||||
static Platform::String^ supportedFormats[];
|
||||
|
||||
friend class CalculatorUnitTests::CopyPasteManagerTest;
|
||||
};
|
||||
}
|
||||
292
src/CalcViewModel/Common/DateCalculator.cpp
Normal file
292
src/CalcViewModel/Common/DateCalculator.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DateCalculator.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace CalculatorApp::Common::DateCalculation;
|
||||
|
||||
DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier)
|
||||
{
|
||||
m_calendar = ref new Calendar();
|
||||
m_calendar->ChangeCalendarSystem(calendarIdentifier);
|
||||
}
|
||||
|
||||
// Adding Duration to a Date
|
||||
// Returns: True if function succeeds to calculate the date else returns False
|
||||
bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_calendar->SetDateTime(startDate);
|
||||
|
||||
if (duration.year != 0)
|
||||
{
|
||||
m_calendar->AddYears(duration.year);
|
||||
}
|
||||
if (duration.month != 0)
|
||||
{
|
||||
m_calendar->AddMonths(duration.month);
|
||||
}
|
||||
if (duration.day != 0)
|
||||
{
|
||||
m_calendar->AddDays(duration.day);
|
||||
}
|
||||
|
||||
*endDate = m_calendar->GetDateTime();
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^ ex)
|
||||
{
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Subtracting Duration from a Date
|
||||
// Returns: True if function succeeds to calculate the date else returns False
|
||||
bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate)
|
||||
{
|
||||
// For Subtract the Algorithm is different than Add. Here the smaller units are subtracted first
|
||||
// and then the larger units.
|
||||
try
|
||||
{
|
||||
m_calendar->SetDateTime(startDate);
|
||||
|
||||
if (duration.day != 0)
|
||||
{
|
||||
m_calendar->AddDays(-duration.day);
|
||||
}
|
||||
if (duration.month != 0)
|
||||
{
|
||||
m_calendar->AddMonths(-duration.month);
|
||||
}
|
||||
if (duration.year != 0)
|
||||
{
|
||||
m_calendar->AddYears(-duration.year);
|
||||
}
|
||||
|
||||
*endDate = m_calendar->GetDateTime();
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^ ex)
|
||||
{
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the UniversalTime value is not negative
|
||||
return (endDate->UniversalTime >= 0);
|
||||
}
|
||||
|
||||
// Calculate the difference between two dates
|
||||
void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference)
|
||||
{
|
||||
DateTime startDate;
|
||||
DateTime endDate;
|
||||
DateTime pivotDate;
|
||||
DateTime tempPivotDate;
|
||||
UINT daysDiff = 0;
|
||||
UINT differenceInDates[c_unitsOfDate] = { 0 };
|
||||
|
||||
if (date1.UniversalTime < date2.UniversalTime)
|
||||
{
|
||||
startDate = date1;
|
||||
endDate = date2;
|
||||
}
|
||||
else
|
||||
{
|
||||
startDate = date2;
|
||||
endDate = date1;
|
||||
}
|
||||
|
||||
pivotDate = startDate;
|
||||
|
||||
daysDiff = GetDifferenceInDays(startDate, endDate);
|
||||
|
||||
// If output has units other than days
|
||||
// 0th bit: Year, 1st bit: Month, 2nd bit: Week, 3rd bit: Day
|
||||
if (static_cast<int>(outputFormat) & 7)
|
||||
{
|
||||
UINT daysInMonth;
|
||||
UINT approximateDaysInYear;
|
||||
|
||||
// If we're unable to calculate the days-in-month or days-in-year, we'll leave the values at 0.
|
||||
if (TryGetCalendarDaysInMonth(startDate, daysInMonth)
|
||||
&& TryGetCalendarDaysInYear(endDate, approximateDaysInYear))
|
||||
{
|
||||
UINT daysIn[c_unitsOfDate] = { approximateDaysInYear, daysInMonth, c_daysInWeek, 1 };
|
||||
|
||||
for (int unitIndex = 0; unitIndex < c_unitsGreaterThanDays; unitIndex++)
|
||||
{
|
||||
tempPivotDate = pivotDate;
|
||||
|
||||
// Check if the bit flag is set for the date unit
|
||||
DateUnit dateUnit = static_cast<DateUnit>(1 << unitIndex);
|
||||
|
||||
if (static_cast<int>(outputFormat & dateUnit))
|
||||
{
|
||||
bool isEndDateHit = false;
|
||||
differenceInDates[unitIndex] = (daysDiff / daysIn[unitIndex]);
|
||||
|
||||
if (differenceInDates[unitIndex] != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^)
|
||||
{
|
||||
// Operation failed due to out of bound result
|
||||
// Do nothing
|
||||
differenceInDates[unitIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int tempDaysDiff;
|
||||
|
||||
do
|
||||
{
|
||||
tempDaysDiff = GetDifferenceInDays(pivotDate, endDate);
|
||||
|
||||
if (tempDaysDiff < 0)
|
||||
{
|
||||
// pivotDate has gone over the end date; start from the begining of this unit
|
||||
differenceInDates[unitIndex] -= 1;
|
||||
pivotDate = tempPivotDate;
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
isEndDateHit = true;
|
||||
}
|
||||
else if (tempDaysDiff > 0)
|
||||
{
|
||||
if (isEndDateHit)
|
||||
{
|
||||
// This is the closest the pivot can get to the end date for this unit
|
||||
break;
|
||||
}
|
||||
|
||||
// pivotDate is still below the end date
|
||||
try
|
||||
{
|
||||
pivotDate = AdjustCalendarDate(pivotDate, dateUnit, 1);
|
||||
differenceInDates[unitIndex] += 1;
|
||||
}
|
||||
catch (Platform::InvalidArgumentException^)
|
||||
{
|
||||
// handling for 31st Dec, 9999 last valid date
|
||||
// Do nothing - break out
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (tempDaysDiff != 0); // dates are the same - exit the loop
|
||||
|
||||
tempPivotDate = AdjustCalendarDate(tempPivotDate, dateUnit, static_cast<int>(differenceInDates[unitIndex]));
|
||||
pivotDate = tempPivotDate;
|
||||
daysDiff = GetDifferenceInDays(pivotDate, endDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
differenceInDates[3] = daysDiff;
|
||||
|
||||
difference->year = differenceInDates[0];
|
||||
difference->month = differenceInDates[1];
|
||||
difference->week = differenceInDates[2];
|
||||
difference->day = differenceInDates[3];
|
||||
}
|
||||
|
||||
|
||||
// Private Methods
|
||||
|
||||
// Gets number of days between the two date time values
|
||||
int DateCalculationEngine::GetDifferenceInDays(DateTime date1, DateTime date2)
|
||||
{
|
||||
// A tick is defined as the number of 100 nanoseconds
|
||||
long long ticksDifference = date2.UniversalTime - date1.UniversalTime;
|
||||
return static_cast<int>(ticksDifference / static_cast<long long>(c_day));
|
||||
}
|
||||
|
||||
// Gets number of Calendar days in the month in which this date falls.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DateCalculationEngine::TryGetCalendarDaysInMonth(_In_ DateTime date, _Out_ UINT& daysInMonth)
|
||||
{
|
||||
bool result = false;
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
// NumberOfDaysInThisMonth returns -1 if unknown.
|
||||
int daysInThisMonth = m_calendar->NumberOfDaysInThisMonth;
|
||||
if (daysInThisMonth != -1)
|
||||
{
|
||||
daysInMonth = static_cast<UINT>(daysInThisMonth);
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Gets number of Calendar days in the year in which this date falls.
|
||||
// Returns true if successful, false otherwise.
|
||||
bool DateCalculationEngine::TryGetCalendarDaysInYear(_In_ DateTime date, _Out_ UINT& daysInYear)
|
||||
{
|
||||
bool result = false;
|
||||
UINT days = 0;
|
||||
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
// NumberOfMonthsInThisYear returns -1 if unknown.
|
||||
int monthsInYear = m_calendar->NumberOfMonthsInThisYear;
|
||||
if (monthsInYear != -1)
|
||||
{
|
||||
bool monthResult = true;
|
||||
|
||||
// Not all years begin with Month 1.
|
||||
int firstMonthThisYear = m_calendar->FirstMonthInThisYear;
|
||||
for (int month = 0; month < monthsInYear; month++)
|
||||
{
|
||||
m_calendar->Month = firstMonthThisYear + month;
|
||||
|
||||
// NumberOfDaysInThisMonth returns -1 if unknown.
|
||||
int daysInMonth = m_calendar->NumberOfDaysInThisMonth;
|
||||
if (daysInMonth == -1)
|
||||
{
|
||||
monthResult = false;
|
||||
break;
|
||||
}
|
||||
|
||||
days += daysInMonth;
|
||||
}
|
||||
|
||||
if (monthResult)
|
||||
{
|
||||
daysInYear = days;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Adds/Subtracts certain value for a particular date unit
|
||||
DateTime DateCalculationEngine::AdjustCalendarDate(Windows::Foundation::DateTime date, DateUnit dateUnit, int difference)
|
||||
{
|
||||
m_calendar->SetDateTime(date);
|
||||
|
||||
switch (dateUnit)
|
||||
{
|
||||
case DateUnit::Year:
|
||||
m_calendar->AddYears(difference);
|
||||
break;
|
||||
case DateUnit::Month:
|
||||
m_calendar->AddMonths(difference);
|
||||
break;
|
||||
case DateUnit::Week:
|
||||
m_calendar->AddWeeks(difference);
|
||||
break;
|
||||
}
|
||||
|
||||
return m_calendar->GetDateTime();
|
||||
}
|
||||
62
src/CalcViewModel/Common/DateCalculator.h
Normal file
62
src/CalcViewModel/Common/DateCalculator.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
const ULONGLONG c_millisecond = 10000;
|
||||
const ULONGLONG c_second = 1000 * c_millisecond;
|
||||
const ULONGLONG c_minute = 60 * c_second;
|
||||
const ULONGLONG c_hour = 60 * c_minute;
|
||||
const ULONGLONG c_day = 24 * c_hour;
|
||||
|
||||
const int c_unitsOfDate = 4; // Units Year,Month,Week,Day
|
||||
const int c_unitsGreaterThanDays = 3; // Units Greater than Days (Year/Month/Week) 3
|
||||
const int c_daysInWeek = 7;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace DateCalculation
|
||||
{
|
||||
public enum class _Enum_is_bitflag_ DateUnit
|
||||
{
|
||||
Year = 0x01,
|
||||
Month = 0x02,
|
||||
Week = 0x04,
|
||||
Day = 0x08
|
||||
};
|
||||
|
||||
// Struct to store the difference between two Dates in the form of Years, Months , Weeks
|
||||
struct DateDifference
|
||||
{
|
||||
int year = 0;
|
||||
int month = 0;
|
||||
int week = 0;
|
||||
int day = 0;
|
||||
};
|
||||
|
||||
class DateCalculationEngine
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
DateCalculationEngine(_In_ Platform::String^ calendarIdentifier);
|
||||
|
||||
// Public Methods
|
||||
bool __nothrow AddDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate);
|
||||
bool __nothrow SubtractDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate);
|
||||
void __nothrow GetDateDifference(_In_ Windows::Foundation::DateTime date1, _In_ Windows::Foundation::DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference);
|
||||
|
||||
private:
|
||||
// Private Variables
|
||||
Windows::Globalization::Calendar^ m_calendar;
|
||||
|
||||
// Private Methods
|
||||
int GetDifferenceInDays(Windows::Foundation::DateTime date1, Windows::Foundation::DateTime date2);
|
||||
bool TryGetCalendarDaysInMonth(_In_ Windows::Foundation::DateTime date, _Out_ UINT& daysInMonth);
|
||||
bool TryGetCalendarDaysInYear(_In_ Windows::Foundation::DateTime date, _Out_ UINT& daysInYear);
|
||||
Windows::Foundation::DateTime AdjustCalendarDate(Windows::Foundation::DateTime date, DateUnit dateUnit, int difference);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/CalcViewModel/Common/DelegateCommand.h
Normal file
70
src/CalcViewModel/Common/DelegateCommand.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
template <typename TTarget>
|
||||
ref class DelegateCommand: public Windows::UI::Xaml::Input::ICommand
|
||||
{
|
||||
internal:
|
||||
|
||||
typedef void (TTarget::*CommandHandlerFunc)(Platform::Object^);
|
||||
|
||||
DelegateCommand(TTarget^ target, CommandHandlerFunc func):
|
||||
m_weakTarget(target),
|
||||
m_function(func)
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
||||
// Explicit, and private, implementation of ICommand, this way of programming makes it so
|
||||
// the ICommand methods will only be available if the ICommand interface is requested via a dynamic_cast
|
||||
// The ICommand interface is meant to be consumed by Xaml and not by the app, this is a defensive measure against
|
||||
// code in the app calling Execute.
|
||||
virtual void ExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::Execute
|
||||
{
|
||||
TTarget^ target = m_weakTarget.Resolve<TTarget>();
|
||||
if (target)
|
||||
{
|
||||
(target->*m_function)(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool CanExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::CanExecute
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual event Windows::Foundation::EventHandler<Platform::Object^>^ CanExecuteChangedImpl
|
||||
{
|
||||
virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::EventHandler<Platform::Object^>^ handler) sealed = Windows::UI::Xaml::Input::ICommand::CanExecuteChanged::add
|
||||
{
|
||||
return m_canExecuteChanged += handler;
|
||||
}
|
||||
virtual void remove(Windows::Foundation::EventRegistrationToken token) sealed = Windows::UI::Xaml::Input::ICommand::CanExecuteChanged::remove
|
||||
{
|
||||
m_canExecuteChanged -= token;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
event Windows::Foundation::EventHandler<Platform::Object^>^ m_canExecuteChanged;
|
||||
|
||||
CommandHandlerFunc m_function;
|
||||
Platform::WeakReference m_weakTarget;
|
||||
|
||||
};
|
||||
|
||||
template <typename TTarget, typename TFuncPtr>
|
||||
DelegateCommand<TTarget>^ MakeDelegate(TTarget^ target, TFuncPtr&& function)
|
||||
{
|
||||
return ref new DelegateCommand<TTarget>(target, std::forward<TFuncPtr>(function));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
46
src/CalcViewModel/Common/DisplayExpressionToken.h
Normal file
46
src/CalcViewModel/Common/DisplayExpressionToken.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp::Common
|
||||
{
|
||||
public enum class TokenType
|
||||
{
|
||||
Operator,
|
||||
Operand,
|
||||
Separator
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class DisplayExpressionToken sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
DisplayExpressionToken(Platform::String^ token, int tokenPosition, bool fEditable, TokenType type) :
|
||||
m_Token(token), m_TokenPosition(tokenPosition), m_IsTokenEditable(fEditable), m_Type(type), m_OriginalToken(token), m_InEditMode(false)
|
||||
{}
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Token);
|
||||
OBSERVABLE_PROPERTY_RW(int, TokenPosition);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsTokenEditable);
|
||||
OBSERVABLE_PROPERTY_RW(int, CommandIndex);
|
||||
OBSERVABLE_PROPERTY_R(Platform::String^, OriginalToken);
|
||||
|
||||
property bool IsTokenInEditMode {
|
||||
bool get() { return m_InEditMode; }
|
||||
void set(bool val)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
m_OriginalToken = ref new Platform::String(m_Token->Data());
|
||||
}
|
||||
m_InEditMode = val;
|
||||
}
|
||||
}
|
||||
internal:
|
||||
OBSERVABLE_PROPERTY_RW(TokenType, Type);
|
||||
private:
|
||||
bool m_InEditMode;
|
||||
};
|
||||
}
|
||||
50
src/CalcViewModel/Common/EngineResourceProvider.cpp
Normal file
50
src/CalcViewModel/Common/EngineResourceProvider.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EngineResourceProvider.h"
|
||||
#include "Common/LocalizationSettings.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace std;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
EngineResourceProvider::EngineResourceProvider()
|
||||
{
|
||||
m_resLoader = ResourceLoader::GetForViewIndependentUse("CEngineStrings");
|
||||
}
|
||||
|
||||
wstring EngineResourceProvider::GetCEngineString(const wstring& id)
|
||||
{
|
||||
const auto& localizationSettings = LocalizationSettings::GetInstance();
|
||||
|
||||
if (id.compare(L"sDecimal") == 0)
|
||||
{
|
||||
return localizationSettings.GetDecimalSeparatorStr();
|
||||
}
|
||||
|
||||
if (id.compare(L"sThousand") == 0)
|
||||
{
|
||||
return localizationSettings.GetNumberGroupingSeparatorStr();
|
||||
}
|
||||
|
||||
if (id.compare(L"sGrouping") == 0)
|
||||
{
|
||||
// The following groupings are the onces that CalcEngine supports.
|
||||
// 0;0 0x000 - no grouping
|
||||
// 3;0 0x003 - group every 3 digits
|
||||
// 3;2;0 0x023 - group 1st 3 and then every 2 digits
|
||||
// 4;0 0x004 - group every 4 digits
|
||||
// 5;3;2;0 0x235 - group 5, then 3, then every 2
|
||||
wstring numberGroupingString = localizationSettings.GetNumberGroupingStr();
|
||||
return numberGroupingString;
|
||||
}
|
||||
|
||||
StringReference idRef(id.c_str());
|
||||
String^ str = m_resLoader->GetString(idRef);
|
||||
return str->Begin();
|
||||
}
|
||||
}
|
||||
17
src/CalcViewModel/Common/EngineResourceProvider.h
Normal file
17
src/CalcViewModel/Common/EngineResourceProvider.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
class EngineResourceProvider : public CalculationManager::IResourceProvider
|
||||
{
|
||||
public:
|
||||
EngineResourceProvider();
|
||||
virtual std::wstring GetCEngineString(const std::wstring& id) override;
|
||||
|
||||
private:
|
||||
Windows::ApplicationModel::Resources::ResourceLoader^ m_resLoader;
|
||||
};
|
||||
}
|
||||
87
src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp
Normal file
87
src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ExpressionCommandDeserializer.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Windows::Storage::Streams;
|
||||
|
||||
CommandDeserializer::CommandDeserializer(_In_ DataReader^ dataReader) :m_dataReader(dataReader){}
|
||||
|
||||
std::shared_ptr<IExpressionCommand> CommandDeserializer::Deserialize(_In_ CalculationManager::CommandType cmdType)
|
||||
{
|
||||
switch (cmdType)
|
||||
{
|
||||
case CalculationManager::CommandType::OperandCommand:
|
||||
|
||||
return std::make_shared<COpndCommand>(DeserializeOperand());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::Parentheses:
|
||||
|
||||
return std::make_shared<CParentheses>(DeserializeParentheses());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::UnaryCommand:
|
||||
|
||||
return std::make_shared<CUnaryCommand>(DeserializeUnary());
|
||||
break;
|
||||
|
||||
case CalculationManager::CommandType::BinaryCommand:
|
||||
|
||||
return std::make_shared<CBinaryCommand>(DeserializeBinary());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ref new Platform::Exception(E_INVALIDARG, ref new Platform::String(L"Unknown command type"));
|
||||
}
|
||||
}
|
||||
|
||||
COpndCommand CommandDeserializer::DeserializeOperand()
|
||||
{
|
||||
bool fNegative = m_dataReader->ReadBoolean();
|
||||
bool fDecimal = m_dataReader->ReadBoolean();
|
||||
bool fSciFmt = m_dataReader->ReadBoolean();
|
||||
|
||||
std::shared_ptr<CalculatorVector<int>> cmdVector = std::make_shared<CalculatorVector<int>>();
|
||||
auto cmdVectorSize = m_dataReader->ReadUInt32();
|
||||
|
||||
for (unsigned int j = 0; j < cmdVectorSize; ++j)
|
||||
{
|
||||
int eachOpndcmd = m_dataReader->ReadInt32();
|
||||
cmdVector->Append(eachOpndcmd);
|
||||
}
|
||||
|
||||
return COpndCommand(cmdVector, fNegative, fDecimal, fSciFmt);
|
||||
}
|
||||
|
||||
CParentheses CommandDeserializer::DeserializeParentheses()
|
||||
{
|
||||
int parenthesisCmd = m_dataReader->ReadInt32();
|
||||
return CParentheses(parenthesisCmd);
|
||||
}
|
||||
|
||||
CUnaryCommand CommandDeserializer::DeserializeUnary()
|
||||
{
|
||||
auto cmdSize = m_dataReader->ReadUInt32();
|
||||
std::shared_ptr<CalculatorVector<int>> cmdVector = std::make_shared<CalculatorVector<int>>();
|
||||
|
||||
if (cmdSize == 1)
|
||||
{
|
||||
int eachOpndcmd = m_dataReader->ReadInt32();
|
||||
return CUnaryCommand(eachOpndcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
int eachOpndcmd1 = m_dataReader->ReadInt32();
|
||||
int eachOpndcmd2 = m_dataReader->ReadInt32();
|
||||
return CUnaryCommand(eachOpndcmd1, eachOpndcmd2);
|
||||
}
|
||||
}
|
||||
|
||||
CBinaryCommand CommandDeserializer::DeserializeBinary()
|
||||
{
|
||||
int cmd = m_dataReader->ReadInt32();
|
||||
return CBinaryCommand(cmd);
|
||||
}
|
||||
24
src/CalcViewModel/Common/ExpressionCommandDeserializer.h
Normal file
24
src/CalcViewModel/Common/ExpressionCommandDeserializer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class CommandDeserializer
|
||||
{
|
||||
public:
|
||||
CommandDeserializer(_In_ Windows::Storage::Streams::DataReader^ dataReader);
|
||||
std::shared_ptr<IExpressionCommand> Deserialize(_In_ CalculationManager::CommandType cmdType);
|
||||
|
||||
private:
|
||||
Windows::Storage::Streams::DataReader^ m_dataReader;
|
||||
COpndCommand DeserializeOperand();
|
||||
CParentheses DeserializeParentheses();
|
||||
CUnaryCommand DeserializeUnary();
|
||||
CBinaryCommand DeserializeBinary();
|
||||
};
|
||||
}
|
||||
}
|
||||
56
src/CalcViewModel/Common/ExpressionCommandSerializer.cpp
Normal file
56
src/CalcViewModel/Common/ExpressionCommandSerializer.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Common\ExpressionCommandSerializer.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Windows::Storage::Streams;
|
||||
|
||||
SerializeCommandVisitor::SerializeCommandVisitor(_In_ DataWriter^ dataWriter) :m_dataWriter(dataWriter)
|
||||
{
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ COpndCommand &opndCmd)
|
||||
{
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsNegative());
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsDecimalPresent());
|
||||
m_dataWriter->WriteBoolean(opndCmd.IsSciFmt());
|
||||
|
||||
auto opndCmds = opndCmd.GetCommands();
|
||||
unsigned int opndCmdSize;
|
||||
opndCmds->GetSize(&opndCmdSize);
|
||||
m_dataWriter->WriteUInt32(opndCmdSize);
|
||||
for (unsigned int j = 0; j < opndCmdSize; ++j)
|
||||
{
|
||||
int eachOpndcmd;
|
||||
opndCmds->GetAt(j, &eachOpndcmd);
|
||||
m_dataWriter->WriteInt32(eachOpndcmd);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CUnaryCommand &unaryCmd)
|
||||
{
|
||||
auto cmds = unaryCmd.GetCommands();
|
||||
unsigned int cmdSize;
|
||||
cmds->GetSize(&cmdSize);
|
||||
m_dataWriter->WriteUInt32(cmdSize);
|
||||
for (unsigned int j = 0; j < cmdSize; ++j)
|
||||
{
|
||||
int eachOpndcmd;
|
||||
cmds->GetAt(j, &eachOpndcmd);
|
||||
m_dataWriter->WriteInt32(eachOpndcmd);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CBinaryCommand &binaryCmd)
|
||||
{
|
||||
int cmd = binaryCmd.GetCommand();
|
||||
m_dataWriter->WriteInt32(cmd);
|
||||
}
|
||||
|
||||
void SerializeCommandVisitor::Visit(_In_ CParentheses ¶Cmd)
|
||||
{
|
||||
int parenthesisCmd = paraCmd.GetCommand();
|
||||
m_dataWriter->WriteInt32(parenthesisCmd);
|
||||
}
|
||||
24
src/CalcViewModel/Common/ExpressionCommandSerializer.h
Normal file
24
src/CalcViewModel/Common/ExpressionCommandSerializer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class SerializeCommandVisitor : public ISerializeCommandVisitor
|
||||
{
|
||||
public:
|
||||
SerializeCommandVisitor(_In_ Windows::Storage::Streams::DataWriter^ dataWriter);
|
||||
|
||||
void Visit(_In_ COpndCommand &opndCmd);
|
||||
void Visit(_In_ CUnaryCommand &unaryCmd);
|
||||
void Visit(_In_ CBinaryCommand &binaryCmd);
|
||||
void Visit(_In_ CParentheses ¶Cmd);
|
||||
|
||||
private:
|
||||
Windows::Storage::Streams::DataWriter^ m_dataWriter;
|
||||
};
|
||||
}
|
||||
}
|
||||
876
src/CalcViewModel/Common/KeyboardShortcutManager.cpp
Normal file
876
src/CalcViewModel/Common/KeyboardShortcutManager.cpp
Normal file
@@ -0,0 +1,876 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "KeyboardShortcutManager.h"
|
||||
#include "AppResourceProvider.h"
|
||||
#include "ApplicationViewModel.h"
|
||||
#include "LocalizationSettings.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::System;
|
||||
using namespace Utils;
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
|
||||
namespace MUXC = Microsoft::UI::Xaml::Controls;
|
||||
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, Character);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKey);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyShiftChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyAltChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlShiftChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyInverseChord);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlInverseChord);
|
||||
|
||||
static multimap<int, multimap<wchar_t, WeakReference>> s_CharacterForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeysForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyShiftChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyAltChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlShiftChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyInverseChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlInverseChordsForButtons;
|
||||
|
||||
static const TimeSpan c_lightUpTime = { 500000 }; // Quarter of a second
|
||||
static multimap<int, bool> s_ShiftKeyPressed;
|
||||
static multimap<int, bool> s_ControlKeyPressed;
|
||||
static multimap<int, bool> s_ShiftButtonChecked;
|
||||
static multimap<int, bool> s_IsDropDownOpen;
|
||||
|
||||
static reader_writer_lock s_keyboardShortcutMapLock;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
// Lights up all of the buttons in the given range
|
||||
// The range is defined by a pair of iterators
|
||||
template <typename T>
|
||||
void LightUpButtons(const T& buttons)
|
||||
{
|
||||
auto iterator = buttons.first;
|
||||
for (; iterator != buttons.second; ++iterator)
|
||||
{
|
||||
auto button = iterator->second.Resolve<ButtonBase>();
|
||||
if (button && button->IsEnabled)
|
||||
{
|
||||
LightUpButton(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LightUpButton(ButtonBase^ button)
|
||||
{
|
||||
// If the button is a toggle button then we don't need
|
||||
// to change the UI of the button
|
||||
if (dynamic_cast<ToggleButton^>(button))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The button will go into the visual Pressed state with this call
|
||||
VisualStateManager::GoToState(button, "Pressed", true);
|
||||
|
||||
// This timer will fire after c_lightUpTime and make the button
|
||||
// go back to the normal state.
|
||||
// This timer will only fire once after which it will be destroyed
|
||||
auto timer = ref new DispatcherTimer();
|
||||
timer->Interval = c_lightUpTime;
|
||||
|
||||
WeakReference timerWeakReference(timer);
|
||||
WeakReference buttonWeakReference(button);
|
||||
timer->Tick += ref new EventHandler<Object^>(
|
||||
[buttonWeakReference, timerWeakReference](Object^, Object^)
|
||||
{
|
||||
auto button = buttonWeakReference.Resolve<ButtonBase>();
|
||||
if (button)
|
||||
{
|
||||
VisualStateManager::GoToState(button, "Normal", true);
|
||||
}
|
||||
|
||||
// Cancel the timer after we're done so it only fires once
|
||||
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
|
||||
if (timer)
|
||||
{
|
||||
timer->Stop();
|
||||
}
|
||||
});
|
||||
timer->Start();
|
||||
}
|
||||
|
||||
// Looks for the first button reference that it can resolve
|
||||
// and execute its command.
|
||||
// NOTE: It is assumed that all buttons associated with a particular
|
||||
// key have the same command
|
||||
template <typename T>
|
||||
void RunFirstEnabledButtonCommand(const T& buttons)
|
||||
{
|
||||
auto buttonIterator = buttons.first;
|
||||
for (; buttonIterator != buttons.second; ++buttonIterator)
|
||||
{
|
||||
auto button = buttonIterator->second.Resolve<ButtonBase>();
|
||||
if (button && button->IsEnabled)
|
||||
{
|
||||
RunButtonCommand(button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunButtonCommand(ButtonBase^ button)
|
||||
{
|
||||
if (button->IsEnabled)
|
||||
{
|
||||
auto command = button->Command;
|
||||
auto parameter = button->CommandParameter;
|
||||
if (command && command->CanExecute(parameter))
|
||||
{
|
||||
command->Execute(parameter);
|
||||
}
|
||||
|
||||
auto radio = dynamic_cast<RadioButton^>(button);
|
||||
if (radio)
|
||||
{
|
||||
radio->IsChecked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto toggle = dynamic_cast<ToggleButton^>(button);
|
||||
if (toggle)
|
||||
{
|
||||
toggle->IsChecked = !toggle->IsChecked->Value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static multimap<int, bool> s_ignoreNextEscape;
|
||||
static multimap<int, bool> s_keepIgnoringEscape;
|
||||
static multimap<int, bool> s_fHonorShortcuts;
|
||||
static multimap<int, bool> s_fHandledEnter;
|
||||
static multimap<int, Flyout^> s_AboutFlyout;
|
||||
|
||||
void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HonorEscape()
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnCharacterPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
String^ oldValue,
|
||||
String^ newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_CharacterForButtons.find(viewId);
|
||||
|
||||
if (iterViewMap != s_CharacterForButtons.end())
|
||||
{
|
||||
if (oldValue)
|
||||
{
|
||||
iterViewMap->second.erase(oldValue->Data()[0]);
|
||||
}
|
||||
|
||||
if (newValue)
|
||||
{
|
||||
if (newValue == L".")
|
||||
{
|
||||
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
||||
iterViewMap->second.insert(std::make_pair(decSep, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CharacterForButtons.insert(std::make_pair(viewId, std::multimap<wchar_t, WeakReference>()));
|
||||
|
||||
if (newValue == L".")
|
||||
{
|
||||
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
||||
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(decSep, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = static_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeysForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeysForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeysForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeysForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
Control^ control = dynamic_cast<ButtonBase^>(target);
|
||||
|
||||
if (control == nullptr)
|
||||
{
|
||||
// Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case
|
||||
control = safe_cast<MUXC::NavigationView^>(target);
|
||||
}
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(control)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(control)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyShiftChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyShiftChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyShiftChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyAltChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
MUXC::NavigationView^ navView = safe_cast<MUXC::NavigationView^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyAltChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyAltChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlShiftChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlShiftChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlShiftChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyInverseChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnVirtualKeyControlInverseChordPropertyChanged(
|
||||
DependencyObject^ target,
|
||||
MyVirtualKey /*oldValue*/,
|
||||
MyVirtualKey newValue)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto button = safe_cast<ButtonBase^>(target);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the View Id is not already registered, then register it and make the entry
|
||||
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
||||
}
|
||||
}
|
||||
|
||||
// In the three event handlers bellow we will not mark the event as handled
|
||||
// because this is a sumplemental operation and we don't want to interfere with
|
||||
// the normal keyboard handling.
|
||||
void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, CharacterReceivedEventArgs^ args)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
||||
|
||||
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
||||
{
|
||||
if (currentHonorShortcuts->second)
|
||||
{
|
||||
wchar_t character = static_cast<wchar_t>(args->KeyCode);
|
||||
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
|
||||
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
LightUpButtons(buttons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::multimap<MyVirtualKey, WeakReference>& GetCurrentKeyDictionary(MyVirtualKey key, bool altPressed = false)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (altPressed)
|
||||
{
|
||||
return s_VirtualKeyAltChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if ((s_ShiftKeyPressed.find(viewId)->second) && ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down))
|
||||
{
|
||||
return s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if (s_ShiftKeyPressed.find(viewId)->second)
|
||||
{
|
||||
return s_VirtualKeyShiftChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else if (s_ShiftButtonChecked.find(viewId)->second)
|
||||
{
|
||||
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
||||
{
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
||||
{
|
||||
if (key == iterator->first)
|
||||
{
|
||||
return s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
||||
{
|
||||
if (key == iterator->first)
|
||||
{
|
||||
return s_VirtualKeyInverseChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
||||
{
|
||||
return s_VirtualKeyControlChordsForButtons.find(viewId)->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return s_VirtualKeysForButtons.find(viewId)->second;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
||||
{
|
||||
// If keyboard shortcuts like Ctrl+C or Ctrl+V are not handled
|
||||
if (!args->Handled)
|
||||
{
|
||||
auto key = args->VirtualKey;
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
bool isControlKeyPressed = (currentControlKeyPressed != s_ControlKeyPressed.end()) && (currentControlKeyPressed->second);
|
||||
bool isShiftKeyPressed = (currentShiftKeyPressed != s_ShiftKeyPressed.end()) && (currentShiftKeyPressed->second);
|
||||
|
||||
// Handle Ctrl + E for DateCalculator
|
||||
if ((key == VirtualKey::E) &&
|
||||
isControlKeyPressed &&
|
||||
!isShiftKeyPressed)
|
||||
{
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
||||
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
auto navView = buttons.first->second.Resolve<MUXC::NavigationView>();
|
||||
auto appViewModel = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
||||
appViewModel->Mode = ViewMode::Date;
|
||||
auto categoryName = AppResourceProvider::GetInstance().GetResourceString(L"DateCalculationModeText");
|
||||
appViewModel->CategoryName = categoryName;
|
||||
|
||||
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
||||
auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date);
|
||||
navView->SelectedItem = menuItems->GetAt(flatIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
||||
|
||||
auto currentIgnoreNextEscape = s_ignoreNextEscape.find(viewId);
|
||||
|
||||
if (currentIgnoreNextEscape != s_ignoreNextEscape.end())
|
||||
{
|
||||
if (currentIgnoreNextEscape->second && key == VirtualKey::Escape)
|
||||
{
|
||||
auto currentKeepIgnoringEscape = s_keepIgnoringEscape.find(viewId);
|
||||
|
||||
if (currentKeepIgnoringEscape != s_keepIgnoringEscape.end())
|
||||
{
|
||||
if (!currentKeepIgnoringEscape->second)
|
||||
{
|
||||
HonorEscape();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key == VirtualKey::Control)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
|
||||
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (key == VirtualKey::Shift)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
||||
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
|
||||
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
|
||||
|
||||
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
||||
{
|
||||
if (currentHonorShortcuts->second)
|
||||
{
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed.
|
||||
// When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open.
|
||||
// Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
|
||||
if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second)
|
||||
{
|
||||
// Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed
|
||||
if (!(isControlKeyPressed && (key == VirtualKey::C || key == VirtualKey::V || key == VirtualKey::Insert))
|
||||
&& !(isShiftKeyPressed && (key == VirtualKey::Insert)))
|
||||
{
|
||||
LightUpButtons(buttons);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto key = args->VirtualKey;
|
||||
|
||||
if (args->VirtualKey == VirtualKey::Shift)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
||||
|
||||
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
else if (args->VirtualKey == VirtualKey::Control)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
||||
|
||||
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher^, AcceleratorKeyEventArgs^ args)
|
||||
{
|
||||
if (args->KeyStatus.IsKeyReleased)
|
||||
{
|
||||
auto key = args->VirtualKey;
|
||||
bool altPressed = args->KeyStatus.IsMenuKeyDown;
|
||||
|
||||
// If the Alt/Menu key is not pressed then we don't care about the key anymore
|
||||
if (!altPressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key), altPressed);
|
||||
auto listItems = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator)
|
||||
{
|
||||
auto item = listIterator->second.Resolve<MUXC::NavigationView>();
|
||||
if (item != nullptr)
|
||||
{
|
||||
auto navView = safe_cast<MUXC::NavigationView^> (item);
|
||||
|
||||
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
||||
if (menuItems != nullptr)
|
||||
{
|
||||
auto vm = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
||||
if (nullptr != vm)
|
||||
{
|
||||
ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast<MyVirtualKey>(key));
|
||||
if (NavCategory::IsValidViewMode(toMode))
|
||||
{
|
||||
vm->Mode = toMode;
|
||||
navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args->VirtualKey == VirtualKey::Escape)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
auto iterViewMap = s_AboutFlyout.find(viewId);
|
||||
|
||||
if ((iterViewMap != s_AboutFlyout.end()) && (iterViewMap->second != nullptr))
|
||||
{
|
||||
iterViewMap->second->Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::Initialize()
|
||||
{
|
||||
auto coreWindow = Window::Current->CoreWindow;
|
||||
coreWindow->CharacterReceived +=
|
||||
ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(&KeyboardShortcutManager::OnCharacterReceivedHandler);
|
||||
coreWindow->KeyDown +=
|
||||
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyDownHandler);
|
||||
coreWindow->KeyUp +=
|
||||
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyUpHandler);
|
||||
coreWindow->Dispatcher->AcceleratorKeyActivated +=
|
||||
ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(&KeyboardShortcutManager::OnAcceleratorKeyActivated);
|
||||
|
||||
KeyboardShortcutManager::RegisterNewAppViewId();
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::ShiftButtonChecked(bool checked)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end())
|
||||
{
|
||||
s_ShiftButtonChecked.erase(viewId);
|
||||
s_ShiftButtonChecked.insert(std::make_pair(viewId, checked));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::UpdateDropDownState(bool isOpen)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end())
|
||||
{
|
||||
s_IsDropDownOpen.erase(viewId);
|
||||
s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::UpdateDropDownState(Flyout^ aboutPageFlyout)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end())
|
||||
{
|
||||
s_AboutFlyout.erase(viewId);
|
||||
s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HonorShortcuts(bool allow)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end())
|
||||
{
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHonorShortcuts.insert(std::make_pair(viewId, allow));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HandledEnter(bool ishandled)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end())
|
||||
{
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_fHandledEnter.insert(std::make_pair(viewId, ishandled));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::RegisterNewAppViewId()
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
int appViewId = Utils::GetWindowId();
|
||||
|
||||
// Check if the View Id has already been registered
|
||||
if (s_CharacterForButtons.find(appViewId) == s_CharacterForButtons.end())
|
||||
{
|
||||
s_CharacterForButtons.insert(std::make_pair(appViewId, std::multimap<wchar_t, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeysForButtons.find(appViewId) == s_VirtualKeysForButtons.end())
|
||||
{
|
||||
s_VirtualKeysForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyInverseChordsForButtons.find(appViewId) == s_VirtualKeyInverseChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
if (s_VirtualKeyControlInverseChordsForButtons.find(appViewId) == s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
{
|
||||
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
s_ShiftKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ControlKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ShiftButtonChecked.insert(std::make_pair(appViewId, false));
|
||||
s_IsDropDownOpen.insert(std::make_pair(appViewId, false));
|
||||
s_ignoreNextEscape.insert(std::make_pair(appViewId, false));
|
||||
s_keepIgnoringEscape.insert(std::make_pair(appViewId, false));
|
||||
s_fHonorShortcuts.insert(std::make_pair(appViewId, true));
|
||||
s_fHandledEnter.insert(std::make_pair(appViewId, true));
|
||||
s_AboutFlyout.insert(std::make_pair(appViewId, nullptr));
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnWindowClosed(int viewId)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
||||
|
||||
s_CharacterForButtons.erase(viewId);
|
||||
|
||||
s_VirtualKeysForButtons.erase(viewId);
|
||||
s_VirtualKeyControlChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyShiftChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyAltChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyControlShiftChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyInverseChordsForButtons.erase(viewId);
|
||||
s_VirtualKeyControlInverseChordsForButtons.erase(viewId);
|
||||
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ShiftButtonChecked.erase(viewId);
|
||||
s_IsDropDownOpen.erase(viewId);
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_AboutFlyout.erase(viewId);
|
||||
}
|
||||
95
src/CalcViewModel/Common/KeyboardShortcutManager.h
Normal file
95
src/CalcViewModel/Common/KeyboardShortcutManager.h
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
public ref class KeyboardShortcutManager sealed
|
||||
{
|
||||
public:
|
||||
KeyboardShortcutManager() {}
|
||||
|
||||
DEPENDENCY_PROPERTY_OWNER(KeyboardShortcutManager);
|
||||
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(Platform::String^, Character);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKey);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyShiftChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyAltChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlShiftChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyInverseChord);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlInverseChord);
|
||||
|
||||
internal:
|
||||
|
||||
static void Initialize();
|
||||
|
||||
// Sometimes, like with popups, escape is treated as special and even
|
||||
// though it is handled we get it passed through to us. In those cases
|
||||
// we need to be able to ignore it (looking at e->Hanlded isn't sufficient
|
||||
// because that always returns true).
|
||||
// The onlyOnce flag is used to indicate whether we should only ignore the
|
||||
// next escape, or keep ignoring until you explicitly HonorEscape.
|
||||
static void IgnoreEscape(bool onlyOnce);
|
||||
static void HonorEscape();
|
||||
static void HonorShortcuts(bool allow);
|
||||
static void HandledEnter(bool ishandled);
|
||||
static void UpdateDropDownState(bool);
|
||||
static void ShiftButtonChecked(bool checked);
|
||||
static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout^ aboutPageFlyout);
|
||||
|
||||
static void RegisterNewAppViewId();
|
||||
static void OnWindowClosed(int viewId);
|
||||
|
||||
private:
|
||||
|
||||
static void OnCharacterPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
Platform::String^ oldValue,
|
||||
Platform::String^ newValue);
|
||||
|
||||
static void OnVirtualKeyPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyShiftChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyInverseChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlInverseChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyAltChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnVirtualKeyControlShiftChordPropertyChanged(
|
||||
Windows::UI::Xaml::DependencyObject^ target,
|
||||
MyVirtualKey oldValue,
|
||||
MyVirtualKey newValue);
|
||||
|
||||
static void OnCharacterReceivedHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args);
|
||||
static void OnKeyDownHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
|
||||
static void OnKeyUpHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
|
||||
static void OnAcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher^, Windows::UI::Core::AcceleratorKeyEventArgs^ args);
|
||||
};
|
||||
}
|
||||
}
|
||||
554
src/CalcViewModel/Common/LocalizationService.cpp
Normal file
554
src/CalcViewModel/Common/LocalizationService.cpp
Normal file
@@ -0,0 +1,554 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LocalizationService.h"
|
||||
#include "LocalizationSettings.h"
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::LocalizationServiceProperties;
|
||||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::ApplicationModel::Resources::Core;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::Globalization::DateTimeFormatting;
|
||||
using namespace Windows::Globalization::Fonts;
|
||||
using namespace Windows::Globalization::NumberFormatting;
|
||||
using namespace Windows::System::UserProfile;
|
||||
using namespace Windows::UI::Text;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::UI::Xaml::Documents;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontType);
|
||||
DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontSize);
|
||||
|
||||
static reader_writer_lock s_locServiceInstanceLock;
|
||||
|
||||
LocalizationService^ LocalizationService::s_singletonInstance = nullptr;
|
||||
|
||||
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
||||
// change given that the engine heavily relies on perfect ordering of certain elements.
|
||||
// The key for open parenthesis, '(', is "48".
|
||||
static constexpr auto s_openParenResourceKey = L"48";
|
||||
|
||||
LocalizationService^ LocalizationService::GetInstance()
|
||||
{
|
||||
|
||||
if (s_singletonInstance == nullptr)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
reader_writer_lock::scoped_lock lock(s_locServiceInstanceLock);
|
||||
|
||||
if (s_singletonInstance == nullptr)
|
||||
{
|
||||
s_singletonInstance = ref new LocalizationService();
|
||||
}
|
||||
}
|
||||
return s_singletonInstance;
|
||||
}
|
||||
|
||||
LocalizationService::LocalizationService()
|
||||
{
|
||||
m_language = ApplicationLanguages::Languages->GetAt(0);
|
||||
m_flowDirection = ResourceContext::GetForCurrentView()->QualifierValues->Lookup(L"LayoutDirection")
|
||||
!= L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight;
|
||||
|
||||
auto resourceLoader = AppResourceProvider::GetInstance();
|
||||
m_fontFamilyOverride = resourceLoader.GetResourceString(L"LocalizedFontFamilyOverride");
|
||||
|
||||
String^ reserved = L"RESERVED_FOR_FONTLOC";
|
||||
|
||||
m_overrideFontApiValues = ((m_fontFamilyOverride != nullptr) && (m_fontFamilyOverride != reserved));
|
||||
if (m_overrideFontApiValues)
|
||||
{
|
||||
String^ localizedUICaptionFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUICaptionFontSizeFactorOverride");
|
||||
String^ localizedUITextFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUITextFontSizeFactorOverride");
|
||||
String^ localizedFontWeightOverride = resourceLoader.GetResourceString(L"LocalizedFontWeightOverride");
|
||||
|
||||
// If any of the font overrides are modified then all of them need to be modified
|
||||
assert(localizedFontWeightOverride != reserved);
|
||||
assert(localizedUITextFontSizeFactorOverride != reserved);
|
||||
assert(localizedUICaptionFontSizeFactorOverride != reserved);
|
||||
|
||||
m_fontWeightOverride = ParseFontWeight(localizedFontWeightOverride);
|
||||
m_uiTextFontScaleFactorOverride = _wtof(localizedUITextFontSizeFactorOverride->Data());
|
||||
m_uiCaptionFontScaleFactorOverride = _wtof(localizedUICaptionFontSizeFactorOverride->Data());
|
||||
}
|
||||
|
||||
m_fontGroup = ref new LanguageFontGroup(m_language);
|
||||
}
|
||||
|
||||
FontWeight LocalizationService::ParseFontWeight(String^ fontWeight)
|
||||
{
|
||||
wstring weight = fontWeight->Data();
|
||||
transform(weight.begin(), weight.end(), weight.begin(), towlower);
|
||||
fontWeight = ref new String(weight.c_str());
|
||||
|
||||
if (fontWeight == "black")
|
||||
{
|
||||
return FontWeights::Black;
|
||||
}
|
||||
else if (fontWeight == "bold")
|
||||
{
|
||||
return FontWeights::Bold;
|
||||
}
|
||||
else if (fontWeight == "extrablack")
|
||||
{
|
||||
return FontWeights::ExtraBlack;
|
||||
}
|
||||
else if (fontWeight == "extrabold")
|
||||
{
|
||||
return FontWeights::ExtraBold;
|
||||
}
|
||||
else if (fontWeight == "extralight")
|
||||
{
|
||||
return FontWeights::ExtraLight;
|
||||
}
|
||||
else if (fontWeight == "light")
|
||||
{
|
||||
return FontWeights::Light;
|
||||
}
|
||||
else if (fontWeight == "medium")
|
||||
{
|
||||
return FontWeights::Medium;
|
||||
}
|
||||
else if (fontWeight == "normal")
|
||||
{
|
||||
return FontWeights::Normal;
|
||||
}
|
||||
else if (fontWeight == "semibold")
|
||||
{
|
||||
return FontWeights::SemiBold;
|
||||
}
|
||||
else if (fontWeight == "semilight")
|
||||
{
|
||||
return FontWeights::SemiLight;
|
||||
}
|
||||
else if (fontWeight == "thin")
|
||||
{
|
||||
return FontWeights::Thin;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw invalid_argument("Invalid argument: fontWeight");
|
||||
}
|
||||
}
|
||||
|
||||
FlowDirection LocalizationService::GetFlowDirection()
|
||||
{
|
||||
return m_flowDirection;
|
||||
}
|
||||
|
||||
bool LocalizationService::IsRtlLayout()
|
||||
{
|
||||
return m_flowDirection == FlowDirection::RightToLeft;
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetLanguage()
|
||||
{
|
||||
return m_language;
|
||||
}
|
||||
|
||||
bool LocalizationService::GetOverrideFontApiValues()
|
||||
{
|
||||
return m_overrideFontApiValues;
|
||||
}
|
||||
|
||||
FontFamily^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType fontType)
|
||||
{
|
||||
if (m_overrideFontApiValues)
|
||||
{
|
||||
return ref new FontFamily(m_fontFamilyOverride);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ref new FontFamily(GetLanguageFont(fontType)->FontFamily);
|
||||
}
|
||||
}
|
||||
|
||||
LanguageFont^ LocalizationService::GetLanguageFont(LanguageFontType fontType)
|
||||
{
|
||||
assert(!m_overrideFontApiValues);
|
||||
assert(m_fontGroup);
|
||||
|
||||
switch (fontType)
|
||||
{
|
||||
case LanguageFontType::UIText:
|
||||
return m_fontGroup->UITextFont;
|
||||
case LanguageFontType::UICaption:
|
||||
return m_fontGroup->UICaptionFont;
|
||||
default:
|
||||
throw std::invalid_argument("fontType");
|
||||
}
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetFontFamilyOverride()
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
return m_fontFamilyOverride;
|
||||
}
|
||||
|
||||
FontWeight LocalizationService::GetFontWeightOverride()
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
return m_fontWeightOverride;
|
||||
}
|
||||
|
||||
double LocalizationService::GetFontScaleFactorOverride(LanguageFontType fontType)
|
||||
{
|
||||
assert(m_overrideFontApiValues);
|
||||
|
||||
switch (fontType)
|
||||
{
|
||||
case LanguageFontType::UIText:
|
||||
return m_uiTextFontScaleFactorOverride;
|
||||
case LanguageFontType::UICaption:
|
||||
return m_uiCaptionFontScaleFactorOverride;
|
||||
default:
|
||||
throw invalid_argument("Invalid argument: fontType");
|
||||
}
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontTypePropertyChanged(DependencyObject^ target, LanguageFontType /*oldValue*/, LanguageFontType /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontWeightPropertyChanged(DependencyObject^ target, FontWeight /*oldValue*/, FontWeight /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::OnFontSizePropertyChanged(DependencyObject^ target, double /*oldValue*/, double /*newValue*/)
|
||||
{
|
||||
UpdateFontFamilyAndSize(target);
|
||||
}
|
||||
|
||||
void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target)
|
||||
{
|
||||
FontFamily^ fontFamily;
|
||||
FontWeight fontWeight;
|
||||
bool fOverrideFontWeight = false;
|
||||
double scaleFactor;
|
||||
|
||||
auto service = LocalizationService::GetInstance();
|
||||
auto fontType = LocalizationService::GetFontType(target);
|
||||
|
||||
if (service->GetOverrideFontApiValues())
|
||||
{
|
||||
fontFamily = ref new FontFamily(service->GetFontFamilyOverride());
|
||||
scaleFactor = service->GetFontScaleFactorOverride(fontType) / 100.0;
|
||||
fontWeight = service->GetFontWeightOverride();
|
||||
fOverrideFontWeight = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto languageFont = service->GetLanguageFont(fontType);
|
||||
fontFamily = ref new FontFamily(languageFont->FontFamily);
|
||||
scaleFactor = languageFont->ScaleFactor / 100.0;
|
||||
}
|
||||
|
||||
double sizeToUse = LocalizationService::GetFontSize(target) * scaleFactor;
|
||||
|
||||
auto control = dynamic_cast<Control^>(target);
|
||||
if (control)
|
||||
{
|
||||
control->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
control->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
control->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
control->ClearValue(Control::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto textBlock = dynamic_cast<TextBlock^>(target);
|
||||
if (textBlock)
|
||||
{
|
||||
textBlock->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
textBlock->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
textBlock->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock->ClearValue(TextBlock::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RichTextBlock^ richTextBlock = dynamic_cast<RichTextBlock^>(target);
|
||||
if (richTextBlock)
|
||||
{
|
||||
richTextBlock->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
richTextBlock->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
richTextBlock->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
richTextBlock->ClearValue(RichTextBlock::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TextElement^ textElement = dynamic_cast<TextElement^>(target);
|
||||
if (textElement)
|
||||
{
|
||||
textElement->FontFamily = fontFamily;
|
||||
if (fOverrideFontWeight)
|
||||
{
|
||||
textElement->FontWeight = fontWeight;
|
||||
}
|
||||
if (sizeToUse != 0.0)
|
||||
{
|
||||
textElement->FontSize = sizeToUse;
|
||||
}
|
||||
else
|
||||
{
|
||||
textElement->ClearValue(TextElement::FontSizeProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter()
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers != nullptr)
|
||||
{
|
||||
return ref new DecimalFormatter(languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion);
|
||||
}
|
||||
|
||||
return ref new DecimalFormatter();
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
//
|
||||
// This helper function creates a DateTimeFormatter with a TwentyFour hour clock
|
||||
DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format)
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
return ref new DateTimeFormatter(format, languageIdentifiers);
|
||||
}
|
||||
|
||||
// If successful, returns a formatter that respects the user's regional format settings,
|
||||
// as configured by running intl.cpl.
|
||||
DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(
|
||||
_In_ String^ format,
|
||||
_In_ String^ calendarIdentifier,
|
||||
_In_ String^ clockIdentifier)
|
||||
{
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
return ref new DateTimeFormatter(
|
||||
format,
|
||||
languageIdentifiers,
|
||||
GlobalizationPreferences::HomeGeographicRegion,
|
||||
calendarIdentifier,
|
||||
clockIdentifier);
|
||||
}
|
||||
|
||||
CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter()
|
||||
{
|
||||
String^ userCurrency = (GlobalizationPreferences::Currencies->Size > 0)
|
||||
? GlobalizationPreferences::Currencies->GetAt(0)
|
||||
: StringReference(DefaultCurrencyCode.data());
|
||||
|
||||
IIterable<String^>^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers();
|
||||
if (languageIdentifiers == nullptr)
|
||||
{
|
||||
languageIdentifiers = ApplicationLanguages::Languages;
|
||||
}
|
||||
|
||||
auto currencyFormatter = ref new CurrencyFormatter(
|
||||
userCurrency,
|
||||
languageIdentifiers,
|
||||
GlobalizationPreferences::HomeGeographicRegion);
|
||||
|
||||
int fractionDigits = LocalizationSettings::GetInstance().GetCurrencyTrailingDigits();
|
||||
currencyFormatter->FractionDigits = fractionDigits;
|
||||
|
||||
return currencyFormatter;
|
||||
}
|
||||
|
||||
IIterable<String^>^ LocalizationService::GetLanguageIdentifiers()
|
||||
{
|
||||
WCHAR currentLocale[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
int result = GetUserDefaultLocaleName(currentLocale, LOCALE_NAME_MAX_LENGTH);
|
||||
if (result != 0)
|
||||
{
|
||||
// GetUserDefaultLocaleName may return an invalid bcp47 language tag with trailing non-BCP47 friendly characters,
|
||||
// which if present would start with an underscore, for example sort order
|
||||
// (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx).
|
||||
// Therefore, if there is an underscore in the locale name, trim all characters from the underscore onwards.
|
||||
WCHAR* underscore = wcschr(currentLocale, L'_');
|
||||
if (underscore != nullptr)
|
||||
{
|
||||
*underscore = L'\0';
|
||||
}
|
||||
|
||||
String^ localeString = ref new String(currentLocale);
|
||||
// validate if the locale we have is valid
|
||||
// otherwise we fallback to the default.
|
||||
if (Language::IsWellFormed(localeString))
|
||||
{
|
||||
auto languageList = ref new Vector<String^>();
|
||||
languageList->Append(localeString);
|
||||
return languageList;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unordered_map<wstring, wstring> LocalizationService::GetTokenToReadableNameMap()
|
||||
{
|
||||
// Resources for the engine use numbers as keys. It's inconvenient, but also difficult to
|
||||
// change given that the engine heavily relies on perfect ordering of certain elements.
|
||||
// To compromise, we'll declare a map from engine resource key to automation name from the
|
||||
// standard project resources.
|
||||
static vector<pair<wstring, wstring>> s_parenEngineKeyResourceMap = {
|
||||
// Sine permutations
|
||||
make_pair<wstring, wstring>(L"67", L"SineDegrees"),
|
||||
make_pair<wstring, wstring>(L"73", L"SineRadians"),
|
||||
make_pair<wstring, wstring>(L"79", L"SineGradians"),
|
||||
make_pair<wstring, wstring>(L"70", L"InverseSineDegrees"),
|
||||
make_pair<wstring, wstring>(L"76", L"InverseSineRadians"),
|
||||
make_pair<wstring, wstring>(L"82", L"InverseSineGradians"),
|
||||
make_pair<wstring, wstring>(L"25", L"HyperbolicSine"),
|
||||
make_pair<wstring, wstring>(L"85", L"InverseHyperbolicSine"),
|
||||
|
||||
// Cosine permutations
|
||||
make_pair<wstring, wstring>(L"68", L"CosineDegrees"),
|
||||
make_pair<wstring, wstring>(L"74", L"CosineRadians"),
|
||||
make_pair<wstring, wstring>(L"80", L"CosineGradians"),
|
||||
make_pair<wstring, wstring>(L"71", L"InverseCosineDegrees"),
|
||||
make_pair<wstring, wstring>(L"77", L"InverseCosineRadians"),
|
||||
make_pair<wstring, wstring>(L"83", L"InverseCosineGradians"),
|
||||
make_pair<wstring, wstring>(L"26", L"HyperbolicCosine"),
|
||||
make_pair<wstring, wstring>(L"86", L"InverseHyperbolicCosine"),
|
||||
|
||||
// Tangent permutations
|
||||
make_pair<wstring, wstring>(L"69", L"TangentDegrees"),
|
||||
make_pair<wstring, wstring>(L"75", L"TangentRadians"),
|
||||
make_pair<wstring, wstring>(L"81", L"TangentGradians"),
|
||||
make_pair<wstring, wstring>(L"72", L"InverseTangentDegrees"),
|
||||
make_pair<wstring, wstring>(L"78", L"InverseTangentRadians"),
|
||||
make_pair<wstring, wstring>(L"84", L"InverseTangentGradians"),
|
||||
make_pair<wstring, wstring>(L"27", L"HyperbolicTangent"),
|
||||
make_pair<wstring, wstring>(L"87", L"InverseHyperbolicTangent"),
|
||||
|
||||
// Miscellaneous Scientific functions
|
||||
make_pair<wstring, wstring>(L"94", L"Factorial"),
|
||||
make_pair<wstring, wstring>(L"35", L"DegreeMinuteSecond"),
|
||||
make_pair<wstring, wstring>(L"28", L"NaturalLog"),
|
||||
make_pair<wstring, wstring>(L"91", L"Square")
|
||||
};
|
||||
|
||||
static vector<pair<wstring, wstring>> s_noParenEngineKeyResourceMap = {
|
||||
// Programmer mode functions
|
||||
make_pair<wstring, wstring>(L"9", L"LeftShift"),
|
||||
make_pair<wstring, wstring>(L"10", L"RightShift"),
|
||||
|
||||
// Y Root scientific function
|
||||
make_pair<wstring, wstring>(L"16", L"YRoot")
|
||||
};
|
||||
|
||||
unordered_map<wstring, wstring> tokenToReadableNameMap{};
|
||||
auto resProvider = AppResourceProvider::GetInstance();
|
||||
|
||||
static const wstring openParen = resProvider.GetCEngineString(StringReference(s_openParenResourceKey))->Data();
|
||||
|
||||
for (const auto& keyPair : s_parenEngineKeyResourceMap)
|
||||
{
|
||||
wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
||||
wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
||||
|
||||
tokenToReadableNameMap.emplace(engineStr + openParen, automationName);
|
||||
}
|
||||
s_parenEngineKeyResourceMap.clear();
|
||||
|
||||
for (const auto& keyPair : s_noParenEngineKeyResourceMap)
|
||||
{
|
||||
wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data();
|
||||
wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data();
|
||||
|
||||
tokenToReadableNameMap.emplace(engineStr, automationName);
|
||||
}
|
||||
s_noParenEngineKeyResourceMap.clear();
|
||||
|
||||
// Also replace hyphens with "minus"
|
||||
wstring minusText = resProvider.GetResourceString(L"minus")->Data();
|
||||
tokenToReadableNameMap.emplace(L"-", minusText);
|
||||
|
||||
return tokenToReadableNameMap;
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetNarratorReadableToken(String^ rawToken)
|
||||
{
|
||||
static unordered_map<wstring, wstring> s_tokenToReadableNameMap = GetTokenToReadableNameMap();
|
||||
|
||||
auto itr = s_tokenToReadableNameMap.find(rawToken->Data());
|
||||
if (itr == s_tokenToReadableNameMap.end())
|
||||
{
|
||||
return rawToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const String^ openParen = AppResourceProvider::GetInstance().GetCEngineString(StringReference(s_openParenResourceKey));
|
||||
return ref new String(itr->second.c_str()) + L" " + openParen;
|
||||
}
|
||||
}
|
||||
|
||||
String^ LocalizationService::GetNarratorReadableString(String^ rawString)
|
||||
{
|
||||
wstringstream readableString{};
|
||||
readableString << L"";
|
||||
|
||||
wstring asWstring = rawString->Data();
|
||||
for (const auto& c : asWstring)
|
||||
{
|
||||
readableString << LocalizationService::GetNarratorReadableToken(L"" + c)->Data();
|
||||
}
|
||||
|
||||
return ref new String(readableString.str().c_str());
|
||||
}
|
||||
83
src/CalcViewModel/Common/LocalizationService.h
Normal file
83
src/CalcViewModel/Common/LocalizationService.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
namespace LocalizationServiceProperties
|
||||
{
|
||||
static constexpr std::wstring_view DefaultCurrencyCode{ L"USD" };
|
||||
}
|
||||
|
||||
public enum class LanguageFontType
|
||||
{
|
||||
UIText,
|
||||
UICaption,
|
||||
};
|
||||
|
||||
public ref class LocalizationService sealed
|
||||
{
|
||||
public:
|
||||
|
||||
DEPENDENCY_PROPERTY_OWNER(LocalizationService);
|
||||
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(LanguageFontType, FontType, LanguageFontType::UIText);
|
||||
DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize);
|
||||
|
||||
internal:
|
||||
static LocalizationService^ GetInstance();
|
||||
|
||||
Windows::UI::Xaml::FlowDirection GetFlowDirection();
|
||||
bool IsRtlLayout();
|
||||
bool GetOverrideFontApiValues();
|
||||
Platform::String^ GetLanguage();
|
||||
Windows::UI::Xaml::Media::FontFamily^ GetLanguageFontFamilyForType(LanguageFontType fontType);
|
||||
Platform::String^ GetFontFamilyOverride();
|
||||
Windows::UI::Text::FontWeight GetFontWeightOverride();
|
||||
double GetFontScaleFactorOverride(LanguageFontType fontType);
|
||||
|
||||
static Windows::Globalization::NumberFormatting::DecimalFormatter^ GetRegionalSettingsAwareDecimalFormatter();
|
||||
static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String^ format);
|
||||
static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(
|
||||
_In_ Platform::String^ format,
|
||||
_In_ Platform::String^ calendarIdentifier,
|
||||
_In_ Platform::String^ clockIdentifier);
|
||||
|
||||
static Windows::Globalization::NumberFormatting::CurrencyFormatter^ GetRegionalSettingsAwareCurrencyFormatter();
|
||||
|
||||
static Platform::String^ GetNarratorReadableToken(Platform::String^ rawToken);
|
||||
static Platform::String^ GetNarratorReadableString(Platform::String^ rawString);
|
||||
|
||||
private:
|
||||
Windows::Globalization::Fonts::LanguageFont^ GetLanguageFont(LanguageFontType fontType);
|
||||
Windows::UI::Text::FontWeight ParseFontWeight(Platform::String^ fontWeight);
|
||||
|
||||
static Windows::Foundation::Collections::IIterable<Platform::String^>^ GetLanguageIdentifiers();
|
||||
|
||||
// Attached property callbacks
|
||||
static void OnFontTypePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, LanguageFontType oldValue, LanguageFontType newValue);
|
||||
static void OnFontWeightPropertyChanged(Windows::UI::Xaml::DependencyObject^ target, Windows::UI::Text::FontWeight oldValue, Windows::UI::Text::FontWeight newValue);
|
||||
static void OnFontSizePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, double oldValue, double newValue);
|
||||
|
||||
static void UpdateFontFamilyAndSize(Windows::UI::Xaml::DependencyObject^ target);
|
||||
|
||||
static std::unordered_map<std::wstring, std::wstring> GetTokenToReadableNameMap();
|
||||
|
||||
private:
|
||||
LocalizationService();
|
||||
|
||||
static LocalizationService^ s_singletonInstance;
|
||||
|
||||
Windows::Globalization::Fonts::LanguageFontGroup^ m_fontGroup;
|
||||
Platform::String^ m_language;
|
||||
Windows::UI::Xaml::FlowDirection m_flowDirection;
|
||||
bool m_overrideFontApiValues;
|
||||
Platform::String^ m_fontFamilyOverride;
|
||||
Windows::UI::Text::FontWeight m_fontWeightOverride;
|
||||
double m_uiTextFontScaleFactorOverride;
|
||||
double m_uiCaptionFontScaleFactorOverride;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
385
src/CalcViewModel/Common/LocalizationSettings.h
Normal file
385
src/CalcViewModel/Common/LocalizationSettings.h
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "LocalizationService.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class LocalizationSettings
|
||||
{
|
||||
private:
|
||||
LocalizationSettings()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
// Use DecimalFormatter as it respects the locale and the user setting
|
||||
Windows::Globalization::NumberFormatting::DecimalFormatter^ formatter;
|
||||
formatter = CalculatorApp::Common::LocalizationService::GetRegionalSettingsAwareDecimalFormatter();
|
||||
formatter->FractionDigits = 0;
|
||||
formatter->IsDecimalPointAlwaysDisplayed = false;
|
||||
|
||||
for (unsigned int i = 0; i < 10; i++)
|
||||
{
|
||||
m_digitSymbols.at(i) = formatter->FormatUInt(i)->Data()[0];
|
||||
}
|
||||
|
||||
result = ResolveLocaleName(formatter->ResolvedLanguage->Data(),
|
||||
m_resolvedName,
|
||||
LOCALE_NAME_MAX_LENGTH);
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error resolving locale name");
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t decimalString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_SDECIMAL,
|
||||
decimalString,
|
||||
ARRAYSIZE(decimalString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
wchar_t groupingSymbolString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_STHOUSAND,
|
||||
groupingSymbolString,
|
||||
ARRAYSIZE(groupingSymbolString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
wchar_t numberGroupingString[LocaleSettingBufferSize] = L"";
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_SGROUPING,
|
||||
numberGroupingString,
|
||||
ARRAYSIZE(numberGroupingString));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
// Get locale info for List Separator, eg. comma is used in many locales
|
||||
wchar_t listSeparatorString[4] = L"";
|
||||
result = ::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_SLIST,
|
||||
listSeparatorString,
|
||||
ARRAYSIZE(listSeparatorString)); // Max length of the expected return value is 4
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
int currencyTrailingDigits = 0;
|
||||
result = GetLocaleInfoEx(m_resolvedName,
|
||||
LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER,
|
||||
(LPWSTR)¤cyTrailingDigits,
|
||||
sizeof(currencyTrailingDigits) / sizeof(WCHAR));
|
||||
if (result == 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected error while getting locale info");
|
||||
}
|
||||
|
||||
// Currency symbol precedence is either 0 or 1.
|
||||
// A value of 0 indicates the symbol follows the currency value.
|
||||
int currencySymbolPrecedence = 1;
|
||||
result = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_IPOSSYMPRECEDES | LOCALE_RETURN_NUMBER,
|
||||
(LPWSTR)¤cySymbolPrecedence,
|
||||
sizeof(currencySymbolPrecedence) / sizeof(WCHAR));
|
||||
|
||||
// As CalcEngine only supports the first character of the decimal separator,
|
||||
// Only first character of the decimal separator string is supported.
|
||||
m_decimalSeparator = decimalString[0];
|
||||
m_numberGroupSeparator = groupingSymbolString[0];
|
||||
m_numberGrouping = numberGroupingString;
|
||||
m_listSeparator = listSeparatorString;
|
||||
m_currencyTrailingDigits = currencyTrailingDigits;
|
||||
m_currencySymbolPrecedence = currencySymbolPrecedence;
|
||||
}
|
||||
|
||||
// Get the system calendar type
|
||||
// Note: This function returns 0 on failure.
|
||||
// We'll ignore the failure in that case and the CalendarIdentifier would get set to GregorianCalendar.
|
||||
CALID calId;
|
||||
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER,
|
||||
reinterpret_cast<PWSTR>(&calId),
|
||||
sizeof(calId));
|
||||
|
||||
m_calendarIdentifier = GetCalendarIdentifierFromCalid(calId);
|
||||
|
||||
// Get FirstDayOfWeek Date and Time setting
|
||||
wchar_t day[80] = L"";
|
||||
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
|
||||
LOCALE_IFIRSTDAYOFWEEK, // The first day in a week
|
||||
reinterpret_cast<PWSTR>(day), // Argument is of type PWSTR
|
||||
ARRAYSIZE(day)); // Max return size are 80 characters
|
||||
|
||||
// The LOCALE_IFIRSTDAYOFWEEK integer value varies from 0, 1, .. 6 for Monday, Tuesday, ... Sunday
|
||||
// DayOfWeek enum value varies from 0, 1, .. 6 for Sunday, Monday, ... Saturday
|
||||
// Hence, DayOfWeek = (valueof(LOCALE_IFIRSTDAYOFWEEK) + 1) % 7
|
||||
m_firstDayOfWeek = static_cast<Windows::Globalization::DayOfWeek>((_wtoi(day) + 1) % 7); // static cast int to DayOfWeek enum
|
||||
}
|
||||
|
||||
public:
|
||||
// A LocalizationSettings object is not copyable.
|
||||
LocalizationSettings(const LocalizationSettings&) = delete;
|
||||
LocalizationSettings& operator=(const LocalizationSettings&) = delete;
|
||||
|
||||
// A LocalizationSettings object is not moveable.
|
||||
LocalizationSettings(LocalizationSettings&&) = delete;
|
||||
LocalizationSettings& operator=(LocalizationSettings&&) = delete;
|
||||
|
||||
// Provider of the singleton LocalizationSettings instance.
|
||||
static const LocalizationSettings& GetInstance()
|
||||
{
|
||||
static const LocalizationSettings localizationSettings;
|
||||
|
||||
return localizationSettings;
|
||||
}
|
||||
|
||||
Platform::String^ GetLocaleName() const
|
||||
{
|
||||
return ref new Platform::String(m_resolvedName);
|
||||
}
|
||||
|
||||
bool IsDigitEnUsSetting() const
|
||||
{
|
||||
if (this->GetDigitSymbolFromEnUsDigit('0') == L'0')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalizeDisplayValue(_Inout_ std::wstring* stringToLocalize) const
|
||||
{
|
||||
if (IsDigitEnUsSetting())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (wchar_t& ch : *stringToLocalize)
|
||||
{
|
||||
if (IsEnUsDigit(ch))
|
||||
{
|
||||
ch = GetDigitSymbolFromEnUsDigit(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ GetEnglishValueFromLocalizedDigits(const std::wstring& localizedString) const
|
||||
{
|
||||
if (m_resolvedName == L"en-US")
|
||||
{
|
||||
return ref new Platform::String(localizedString.c_str());
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
size_t length = localizedString.size();
|
||||
std::unique_ptr<wchar_t[]> englishString(new wchar_t[length + 1]); // +1 for the null termination
|
||||
|
||||
for (; i < length; ++i)
|
||||
{
|
||||
wchar_t ch = localizedString[i];
|
||||
if (!IsEnUsDigit(ch))
|
||||
{
|
||||
for (int j = 0; j < 10; ++j)
|
||||
{
|
||||
if (ch == m_digitSymbols[j])
|
||||
{
|
||||
ch = j.ToString()->Data()[0];
|
||||
break;
|
||||
//ch = val - L'0';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch == m_decimalSeparator)
|
||||
{
|
||||
ch = L'.';
|
||||
}
|
||||
englishString[i] = ch;
|
||||
}
|
||||
englishString[i] = '\0';
|
||||
|
||||
return ref new Platform::String(englishString.get());
|
||||
}
|
||||
|
||||
bool IsEnUsDigit(const wchar_t digit) const
|
||||
{
|
||||
if (digit >= L'0' && digit <= L'9')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLocalizedDigit(const wchar_t digit) const
|
||||
{
|
||||
for (auto dig : m_digitSymbols)
|
||||
{
|
||||
if (digit == dig)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLocalizedHexDigit(const wchar_t digit) const
|
||||
{
|
||||
if (IsLocalizedDigit(digit))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto dig : s_hexSymbols)
|
||||
{
|
||||
if (digit == dig)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t GetDigitSymbolFromEnUsDigit(wchar_t digitSymbol) const
|
||||
{
|
||||
assert(digitSymbol >= L'0' && digitSymbol <= L'9');
|
||||
int digit = digitSymbol - L'0';
|
||||
return m_digitSymbols.at(digit); // throws on out of range
|
||||
}
|
||||
|
||||
wchar_t GetDecimalSeparator() const
|
||||
{
|
||||
return m_decimalSeparator;
|
||||
}
|
||||
|
||||
wchar_t GetNumberGroupSeparator() const
|
||||
{
|
||||
return m_numberGroupSeparator;
|
||||
}
|
||||
|
||||
std::wstring GetDecimalSeparatorStr() const
|
||||
{
|
||||
std::wstring result;
|
||||
result.push_back(m_decimalSeparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring GetNumberGroupingSeparatorStr() const
|
||||
{
|
||||
std::wstring result;
|
||||
result.push_back(m_numberGroupSeparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring GetNumberGroupingStr() const
|
||||
{
|
||||
return m_numberGrouping;
|
||||
}
|
||||
|
||||
void RemoveGroupSeparators(const wchar_t* value, const size_t length, std::wstring* rawValue) const
|
||||
{
|
||||
rawValue->clear();
|
||||
rawValue->reserve(length);
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
if (value[i] != L' ' && value[i] != m_numberGroupSeparator)
|
||||
{
|
||||
rawValue->append(1, value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ GetCalendarIdentifier() const
|
||||
{
|
||||
return m_calendarIdentifier;
|
||||
}
|
||||
|
||||
std::wstring GetListSeparator() const
|
||||
{
|
||||
return m_listSeparator;
|
||||
}
|
||||
|
||||
Windows::Globalization::DayOfWeek GetFirstDayOfWeek() const
|
||||
{
|
||||
return m_firstDayOfWeek;
|
||||
}
|
||||
|
||||
int GetCurrencyTrailingDigits() const
|
||||
{
|
||||
return m_currencyTrailingDigits;
|
||||
}
|
||||
|
||||
int GetCurrencySymbolPrecedence() const
|
||||
{
|
||||
return m_currencySymbolPrecedence;
|
||||
}
|
||||
|
||||
private:
|
||||
static Platform::String^ GetCalendarIdentifierFromCalid(CALID calId)
|
||||
{
|
||||
switch (calId)
|
||||
{
|
||||
case CAL_GREGORIAN:
|
||||
case CAL_GREGORIAN_ARABIC:
|
||||
case CAL_GREGORIAN_ME_FRENCH:
|
||||
case CAL_GREGORIAN_US:
|
||||
case CAL_GREGORIAN_XLIT_ENGLISH:
|
||||
case CAL_GREGORIAN_XLIT_FRENCH:
|
||||
return Windows::Globalization::CalendarIdentifiers::Gregorian;
|
||||
|
||||
case CAL_HEBREW:
|
||||
return Windows::Globalization::CalendarIdentifiers::Hebrew;
|
||||
|
||||
case CAL_HIJRI:
|
||||
case CAL_PERSIAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Hijri;
|
||||
|
||||
case CAL_JAPAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Japanese;
|
||||
|
||||
case CAL_KOREA:
|
||||
return Windows::Globalization::CalendarIdentifiers::Korean;
|
||||
|
||||
case CAL_TAIWAN:
|
||||
return Windows::Globalization::CalendarIdentifiers::Taiwan;
|
||||
|
||||
case CAL_THAI:
|
||||
return Windows::Globalization::CalendarIdentifiers::Thai;
|
||||
|
||||
case CAL_UMALQURA:
|
||||
return Windows::Globalization::CalendarIdentifiers::UmAlQura;
|
||||
|
||||
// Gregorian will be the default Calendar Type
|
||||
default:
|
||||
return Windows::Globalization::CalendarIdentifiers::Gregorian;
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t m_decimalSeparator;
|
||||
wchar_t m_numberGroupSeparator;
|
||||
std::wstring m_numberGrouping;
|
||||
std::array<wchar_t, 10> m_digitSymbols;
|
||||
// Hexadecimal characters are not currently localized
|
||||
static constexpr std::array<wchar_t, 6> s_hexSymbols{ L'A', L'B', L'C', L'D', L'E', L'F' };
|
||||
std::wstring m_listSeparator;
|
||||
Platform::String^ m_calendarIdentifier;
|
||||
Windows::Globalization::DayOfWeek m_firstDayOfWeek;
|
||||
int m_currencySymbolPrecedence;
|
||||
wchar_t m_resolvedName[LOCALE_NAME_MAX_LENGTH];
|
||||
int m_currencyTrailingDigits;
|
||||
static const unsigned int LocaleSettingBufferSize = 16;
|
||||
};
|
||||
}
|
||||
}
|
||||
64
src/CalcViewModel/Common/LocalizationStringUtil.h
Normal file
64
src/CalcViewModel/Common/LocalizationStringUtil.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AppResourceProvider.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
class LocalizationStringUtil
|
||||
{
|
||||
public:
|
||||
static std::wstring GetLocalizedString(const wchar_t* pMessage, ...)
|
||||
{
|
||||
std::wstring returnString = L"";
|
||||
const UINT32 length = 1024;
|
||||
std::unique_ptr<wchar_t[]> spBuffer = std::unique_ptr<wchar_t[]>(new wchar_t[length]);
|
||||
va_list args = NULL;
|
||||
va_start(args, pMessage);
|
||||
DWORD fmtReturnVal = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
|
||||
pMessage,
|
||||
0,
|
||||
0,
|
||||
spBuffer.get(),
|
||||
length,
|
||||
&args);
|
||||
va_end(args);
|
||||
|
||||
if (fmtReturnVal != 0)
|
||||
{
|
||||
returnString = spBuffer.get();
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
static Platform::String^ GetLocalizedNarratorAnnouncement(Platform::String^ resourceKey, Platform::String^& formatVariable, T*... params)
|
||||
{
|
||||
EnsureInitialization(resourceKey, formatVariable);
|
||||
return StringReference(GetLocalizedString(formatVariable->Data(), params...).c_str());
|
||||
}
|
||||
|
||||
private:
|
||||
static void EnsureInitialization(Platform::String^ resourceKey, Platform::String^& formatVariable)
|
||||
{
|
||||
if (resourceKey == nullptr || resourceKey->IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the formatVariable already has a value, we don't need to set it again. Simply return.
|
||||
if (formatVariable != nullptr && !formatVariable->IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
formatVariable = AppResourceProvider::GetInstance().GetResourceString(resourceKey);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
145
src/CalcViewModel/Common/MyVirtualKey.h
Normal file
145
src/CalcViewModel/Common/MyVirtualKey.h
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
public enum class MyVirtualKey
|
||||
{
|
||||
None = 0,
|
||||
LeftButton = 1,
|
||||
RightButton = 2,
|
||||
Cancel = 3,
|
||||
MiddleButton = 4,
|
||||
XButton1 = 5,
|
||||
XButton2 = 6,
|
||||
Back = 8,
|
||||
Tab = 9,
|
||||
Clear = 12,
|
||||
Enter = 13,
|
||||
Shift = 16,
|
||||
Control = 17,
|
||||
Menu = 18,
|
||||
Pause = 19,
|
||||
CapitalLock = 20,
|
||||
Kana = 21,
|
||||
Hangul = 21,
|
||||
Junja = 23,
|
||||
Final = 24,
|
||||
Hanja = 25,
|
||||
Kanji = 25,
|
||||
Escape = 27,
|
||||
Convert = 28,
|
||||
NonConvert = 29,
|
||||
Accept = 30,
|
||||
ModeChange = 31,
|
||||
Space = 32,
|
||||
PageUp = 33,
|
||||
PageDown = 34,
|
||||
End = 35,
|
||||
Home = 36,
|
||||
Left = 37,
|
||||
Up = 38,
|
||||
Right = 39,
|
||||
Down = 40,
|
||||
Select = 41,
|
||||
Print = 42,
|
||||
Execute = 43,
|
||||
Snapshot = 44,
|
||||
Insert = 45,
|
||||
Delete = 46,
|
||||
Help = 47,
|
||||
Number0 = 48,
|
||||
Number1 = 49,
|
||||
Number2 = 50,
|
||||
Number3 = 51,
|
||||
Number4 = 52,
|
||||
Number5 = 53,
|
||||
Number6 = 54,
|
||||
Number7 = 55,
|
||||
Number8 = 56,
|
||||
Number9 = 57,
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LeftWindows = 91,
|
||||
RightWindows = 92,
|
||||
Application = 93,
|
||||
Sleep = 95,
|
||||
NumberPad0 = 96,
|
||||
NumberPad1 = 97,
|
||||
NumberPad2 = 98,
|
||||
NumberPad3 = 99,
|
||||
NumberPad4 = 100,
|
||||
NumberPad5 = 101,
|
||||
NumberPad6 = 102,
|
||||
NumberPad7 = 103,
|
||||
NumberPad8 = 104,
|
||||
NumberPad9 = 105,
|
||||
Multiply = 106,
|
||||
Add = 107,
|
||||
Separator = 108,
|
||||
Subtract = 109,
|
||||
Decimal = 110,
|
||||
Divide = 111,
|
||||
F1 = 112,
|
||||
F2 = 113,
|
||||
F3 = 114,
|
||||
F4 = 115,
|
||||
F5 = 116,
|
||||
F6 = 117,
|
||||
F7 = 118,
|
||||
F8 = 119,
|
||||
F9 = 120,
|
||||
F10 = 121,
|
||||
F11 = 122,
|
||||
F12 = 123,
|
||||
F13 = 124,
|
||||
F14 = 125,
|
||||
F15 = 126,
|
||||
F16 = 127,
|
||||
F17 = 128,
|
||||
F18 = 129,
|
||||
F19 = 130,
|
||||
F20 = 131,
|
||||
F21 = 132,
|
||||
F22 = 133,
|
||||
F23 = 134,
|
||||
F24 = 135,
|
||||
NumberKeyLock = 144,
|
||||
Scroll = 145,
|
||||
LeftShift = 160,
|
||||
RightShift = 161,
|
||||
LeftControl = 162,
|
||||
RightControl = 163,
|
||||
LeftMenu = 164,
|
||||
RightMenu = 165
|
||||
};
|
||||
}
|
||||
}
|
||||
379
src/CalcViewModel/Common/NavCategory.cpp
Normal file
379
src/CalcViewModel/Common/NavCategory.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NavCategory.h"
|
||||
#include "AppResourceProvider.h"
|
||||
#include "Common\LocalizationStringUtil.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
namespace UCM = UnitConversionManager;
|
||||
|
||||
// Calculator categories always support negative and positive.
|
||||
static constexpr bool SUPPORTS_ALL = true;
|
||||
|
||||
// Converter categories usually only support positive.
|
||||
static constexpr bool SUPPORTS_NEGATIVE = true;
|
||||
static constexpr bool POSITIVE_ONLY = false;
|
||||
|
||||
// The order of items in this list determines the order of groups in the menu.
|
||||
static constexpr array<const NavCategoryGroupInitializer, 2> s_categoryGroupManifest = {
|
||||
NavCategoryGroupInitializer { CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText"},
|
||||
NavCategoryGroupInitializer { CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" }
|
||||
};
|
||||
|
||||
// vvv THESE CONSTANTS SHOULD NEVER CHANGE vvv
|
||||
static constexpr int STANDARD_ID = 0;
|
||||
static constexpr int SCIENTIFIC_ID = 1;
|
||||
static constexpr int PROGRAMMER_ID = 2;
|
||||
static constexpr int DATE_ID = 3;
|
||||
static constexpr int VOLUME_ID = 4;
|
||||
static constexpr int LENGTH_ID = 5;
|
||||
static constexpr int WEIGHT_ID = 6;
|
||||
static constexpr int TEMPERATURE_ID = 7;
|
||||
static constexpr int ENERGY_ID = 8;
|
||||
static constexpr int AREA_ID = 9;
|
||||
static constexpr int SPEED_ID = 10;
|
||||
static constexpr int TIME_ID = 11;
|
||||
static constexpr int POWER_ID = 12;
|
||||
static constexpr int DATA_ID = 13;
|
||||
static constexpr int PRESSURE_ID = 14;
|
||||
static constexpr int ANGLE_ID = 15;
|
||||
static constexpr int CURRENCY_ID = 16;
|
||||
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
|
||||
|
||||
// The order of items in this list determines the order of items in the menu.
|
||||
static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
|
||||
NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL },
|
||||
NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL },
|
||||
NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL },
|
||||
NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL },
|
||||
NavCategoryInitializer { ViewMode::Currency, CURRENCY_ID, L"Currency", L"CategoryName_Currency", L"\uEB0D", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Volume, VOLUME_ID, L"Volume", L"CategoryName_Volume", L"\uF1AA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Length, LENGTH_ID, L"Length", L"CategoryName_Length", L"\uECC6", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Weight, WEIGHT_ID, L"Weight and Mass", L"CategoryName_Weight", L"\uF4C1", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Temperature, TEMPERATURE_ID, L"Temperature", L"CategoryName_Temperature", L"\uE7A3", CategoryGroupType::Converter, MyVirtualKey::None, SUPPORTS_NEGATIVE },
|
||||
NavCategoryInitializer { ViewMode::Energy, ENERGY_ID, L"Energy", L"CategoryName_Energy", L"\uECAD", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Area, AREA_ID, L"Area", L"CategoryName_Area", L"\uE809", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Speed, SPEED_ID, L"Speed", L"CategoryName_Speed", L"\uEADA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Time, TIME_ID, L"Time", L"CategoryName_Time", L"\uE917", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Power, POWER_ID, L"Power", L"CategoryName_Power", L"\uE945", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Data, DATA_ID, L"Data", L"CategoryName_Data", L"\uF20F", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Pressure, PRESSURE_ID, L"Pressure", L"CategoryName_Pressure", L"\uEC4A", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY },
|
||||
NavCategoryInitializer { ViewMode::Angle, ANGLE_ID, L"Angle", L"CategoryName_Angle", L"\uF515", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }
|
||||
};
|
||||
|
||||
// This function should only be used when storing the mode to app data.
|
||||
int NavCategory::Serialize(ViewMode mode)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? iter->serializationId
|
||||
: -1;
|
||||
}
|
||||
|
||||
// This function should only be used when restoring the mode from app data.
|
||||
ViewMode NavCategory::Deserialize(Platform::Object^ obj)
|
||||
{
|
||||
// If we cast directly to ViewMode we will fail
|
||||
// because we technically store an int.
|
||||
// Need to cast to int, then ViewMode.
|
||||
auto boxed = dynamic_cast<Box<int>^>(obj);
|
||||
if (boxed != nullptr)
|
||||
{
|
||||
int serializationId = boxed->Value;
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[serializationId](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.serializationId == serializationId;
|
||||
});
|
||||
|
||||
if (iter != s_categoryManifest.end())
|
||||
{
|
||||
return iter->viewMode;
|
||||
}
|
||||
}
|
||||
|
||||
return ViewMode::None;
|
||||
}
|
||||
|
||||
bool NavCategory::IsValidViewMode(ViewMode mode)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return iter != s_categoryManifest.end();
|
||||
}
|
||||
|
||||
bool NavCategory::IsCalculatorViewMode(ViewMode mode)
|
||||
{
|
||||
// Historically, Date Calculator is not a Calculator mode
|
||||
// even though it is in the Calculator category.
|
||||
return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
|
||||
}
|
||||
|
||||
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)
|
||||
{
|
||||
return mode == ViewMode::Date;
|
||||
}
|
||||
|
||||
bool NavCategory::IsConverterViewMode(ViewMode mode)
|
||||
{
|
||||
return IsModeInCategoryGroup(mode, CategoryGroupType::Converter);
|
||||
}
|
||||
|
||||
bool NavCategory::IsModeInCategoryGroup(ViewMode mode, CategoryGroupType type)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode, type](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode && initializer.groupType == type;
|
||||
});
|
||||
|
||||
return iter != s_categoryManifest.end();
|
||||
}
|
||||
|
||||
String^ NavCategory::GetFriendlyName(ViewMode mode)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? StringReference(iter->friendlyName)
|
||||
: L"None";
|
||||
}
|
||||
|
||||
ViewMode NavCategory::GetViewModeForFriendlyName(String^ name)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[name](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return wcscmp(initializer.friendlyName, name->Data()) == 0;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? iter->viewMode
|
||||
: ViewMode::None;
|
||||
}
|
||||
|
||||
String^ NavCategory::GetNameResourceKey(ViewMode mode)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? StringReference(iter->nameResourceKey) + "Text"
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
CategoryGroupType NavCategory::GetGroupType(ViewMode mode)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? iter->groupType
|
||||
: CategoryGroupType::None;
|
||||
}
|
||||
|
||||
// GetIndex is 0-based, GetPostion is 1-based
|
||||
int NavCategory::GetIndex(ViewMode mode)
|
||||
{
|
||||
int position = NavCategory::GetPosition(mode);
|
||||
return max(-1, position - 1);
|
||||
}
|
||||
|
||||
int NavCategory::GetFlatIndex(ViewMode mode)
|
||||
{
|
||||
int index = -1;
|
||||
CategoryGroupType type = CategoryGroupType::None;
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode, &type, &index](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
index++;
|
||||
if (initializer.groupType != type)
|
||||
{
|
||||
type = initializer.groupType;
|
||||
index++;
|
||||
}
|
||||
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? index
|
||||
: -1;
|
||||
}
|
||||
|
||||
// GetIndex is 0-based, GetPostion is 1-based
|
||||
int NavCategory::GetIndexInGroup(ViewMode mode, CategoryGroupType type)
|
||||
{
|
||||
int index = -1;
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode, type, &index](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
if (initializer.groupType == type)
|
||||
{
|
||||
index++;
|
||||
return initializer.viewMode == mode;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? index
|
||||
: -1;
|
||||
}
|
||||
|
||||
// GetIndex is 0-based, GetPostion is 1-based
|
||||
int NavCategory::GetPosition(ViewMode mode)
|
||||
{
|
||||
int position = 0;
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[mode, &position](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
position++;
|
||||
return initializer.viewMode == mode;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? position
|
||||
: -1;
|
||||
}
|
||||
|
||||
ViewMode NavCategory::GetViewModeForVirtualKey(MyVirtualKey virtualKey)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest),
|
||||
[virtualKey](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.virtualKey == virtualKey;
|
||||
});
|
||||
|
||||
return (iter != s_categoryManifest.end())
|
||||
? iter->viewMode
|
||||
: ViewMode::None;
|
||||
}
|
||||
|
||||
vector<MyVirtualKey> NavCategory::GetCategoryAcceleratorKeys()
|
||||
{
|
||||
vector<MyVirtualKey> accelerators{};
|
||||
for (auto category : s_categoryManifest)
|
||||
{
|
||||
if (category.virtualKey != MyVirtualKey::None)
|
||||
{
|
||||
accelerators.push_back(category.virtualKey);
|
||||
}
|
||||
}
|
||||
|
||||
return accelerators;
|
||||
}
|
||||
|
||||
NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer) :
|
||||
m_Categories(ref new Vector<NavCategory^>())
|
||||
{
|
||||
m_GroupType = groupInitializer.type;
|
||||
|
||||
auto resProvider = AppResourceProvider::GetInstance();
|
||||
String^ headerResourceKey = StringReference(groupInitializer.headerResourceKey);
|
||||
String^ modeResourceKey = StringReference(groupInitializer.modeResourceKey);
|
||||
String^ automationResourceKey = StringReference(groupInitializer.automationResourceKey);
|
||||
m_Name = resProvider.GetResourceString(headerResourceKey);
|
||||
String^ groupMode = resProvider.GetResourceString(modeResourceKey);
|
||||
String^ automationName = resProvider.GetResourceString(automationResourceKey);
|
||||
|
||||
String^ navCategoryHeaderAutomationNameFormat = resProvider.GetResourceString(L"NavCategoryHeader_AutomationNameFormat");
|
||||
m_AutomationName = ref new String(LocalizationStringUtil::GetLocalizedString(
|
||||
navCategoryHeaderAutomationNameFormat->Data(),
|
||||
automationName->Data()).c_str());
|
||||
|
||||
String^ navCategoryItemAutomationNameFormat = resProvider.GetResourceString(L"NavCategoryItem_AutomationNameFormat");
|
||||
|
||||
for (const NavCategoryInitializer& categoryInitializer : s_categoryManifest)
|
||||
{
|
||||
if (categoryInitializer.groupType == groupInitializer.type)
|
||||
{
|
||||
String^ nameResourceKey = StringReference(categoryInitializer.nameResourceKey);
|
||||
String^ categoryName = resProvider.GetResourceString(nameResourceKey + "Text");
|
||||
String^ categoryAutomationName = ref new String(LocalizationStringUtil::GetLocalizedString(
|
||||
navCategoryItemAutomationNameFormat->Data(),
|
||||
categoryName->Data(),
|
||||
m_Name->Data()).c_str());
|
||||
|
||||
m_Categories->Append(ref new NavCategory(
|
||||
categoryName,
|
||||
categoryAutomationName,
|
||||
StringReference(categoryInitializer.glyph),
|
||||
resProvider.GetResourceString(nameResourceKey + "AccessKey"),
|
||||
groupMode,
|
||||
categoryInitializer.viewMode,
|
||||
categoryInitializer.supportsNegative));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IObservableVector<NavCategoryGroup^>^ NavCategoryGroup::CreateMenuOptions()
|
||||
{
|
||||
auto menuOptions = ref new Vector<NavCategoryGroup^>();
|
||||
menuOptions->Append(CreateCalculatorCategory());
|
||||
menuOptions->Append(CreateConverterCategory());
|
||||
return menuOptions;
|
||||
}
|
||||
|
||||
NavCategoryGroup^ NavCategoryGroup::CreateCalculatorCategory()
|
||||
{
|
||||
return ref new NavCategoryGroup(s_categoryGroupManifest.at(0));
|
||||
}
|
||||
|
||||
NavCategoryGroup^ NavCategoryGroup::CreateConverterCategory()
|
||||
{
|
||||
return ref new NavCategoryGroup(s_categoryGroupManifest.at(1));
|
||||
}
|
||||
|
||||
vector<NavCategoryInitializer> NavCategoryGroup::GetInitializerCategoryGroup(CategoryGroupType groupType)
|
||||
{
|
||||
vector<NavCategoryInitializer> initializers{};
|
||||
copy_if(begin(s_categoryManifest), end(s_categoryManifest), back_inserter(initializers),
|
||||
[groupType](const NavCategoryInitializer& initializer)
|
||||
{
|
||||
return initializer.groupType == groupType;
|
||||
});
|
||||
|
||||
return initializers;
|
||||
}
|
||||
|
||||
String^ NavCategoryGroup::GetHeaderResourceKey(CategoryGroupType type)
|
||||
{
|
||||
auto iter = find_if(begin(s_categoryGroupManifest), end(s_categoryGroupManifest),
|
||||
[type](const NavCategoryGroupInitializer& initializer)
|
||||
{
|
||||
return initializer.type == type;
|
||||
});
|
||||
|
||||
return (iter != s_categoryGroupManifest.end())
|
||||
? StringReference(iter->headerResourceKey)
|
||||
: nullptr;
|
||||
}
|
||||
231
src/CalcViewModel/Common/NavCategory.h
Normal file
231
src/CalcViewModel/Common/NavCategory.h
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
/* The NavCategory group of classes and enumerations is intended to serve
|
||||
* as a single location for storing metadata about a navigation mode.
|
||||
*
|
||||
* These .h and .cpp files:
|
||||
* - Define the ViewMode enumeration which is used for setting the mode of the app.
|
||||
* - Define a list of metadata associated with each ViewMode.
|
||||
* - Define the order of groups and items in the navigation menu.
|
||||
* - Provide a static helper function for creating the navigation menu options.
|
||||
* - Provide static helper functions for querying information about a given ViewMode.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
// Don't change the order of these enums
|
||||
// and definitely don't use int arithmetic
|
||||
// to change modes.
|
||||
public enum class ViewMode
|
||||
{
|
||||
None = -1,
|
||||
Standard = 0,
|
||||
Scientific = 1,
|
||||
Programmer = 2,
|
||||
Date = 3,
|
||||
Volume = 4,
|
||||
Length = 5,
|
||||
Weight = 6,
|
||||
Temperature = 7,
|
||||
Energy = 8,
|
||||
Area = 9,
|
||||
Speed = 10,
|
||||
Time = 11,
|
||||
Power = 12,
|
||||
Data = 13,
|
||||
Pressure = 14,
|
||||
Angle = 15,
|
||||
Currency = 16
|
||||
};
|
||||
|
||||
public enum class CategoryGroupType
|
||||
{
|
||||
None = -1,
|
||||
Calculator = 0,
|
||||
Converter = 1
|
||||
};
|
||||
|
||||
private struct NavCategoryInitializer
|
||||
{
|
||||
constexpr NavCategoryInitializer(
|
||||
ViewMode mode,
|
||||
int id,
|
||||
wchar_t const * name,
|
||||
wchar_t const * nameKey,
|
||||
wchar_t const * glyph,
|
||||
CategoryGroupType group,
|
||||
CalculatorApp::Common::MyVirtualKey vKey,
|
||||
bool categorySupportsNegative)
|
||||
:
|
||||
viewMode(mode),
|
||||
serializationId(id),
|
||||
friendlyName(name),
|
||||
nameResourceKey(nameKey),
|
||||
glyph(glyph),
|
||||
groupType(group),
|
||||
virtualKey(vKey),
|
||||
supportsNegative(categorySupportsNegative)
|
||||
{}
|
||||
|
||||
const ViewMode viewMode;
|
||||
const int serializationId;
|
||||
const wchar_t * const friendlyName;
|
||||
const wchar_t * const nameResourceKey;
|
||||
const wchar_t * const glyph;
|
||||
const CategoryGroupType groupType;
|
||||
const CalculatorApp::Common::MyVirtualKey virtualKey;
|
||||
const bool supportsNegative;
|
||||
};
|
||||
|
||||
private struct NavCategoryGroupInitializer
|
||||
{
|
||||
constexpr NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const * h, wchar_t const * n, wchar_t const * a) :
|
||||
type(t), headerResourceKey(h), modeResourceKey(n), automationResourceKey(a)
|
||||
{}
|
||||
|
||||
const CategoryGroupType type;
|
||||
const wchar_t *headerResourceKey;
|
||||
const wchar_t *modeResourceKey;
|
||||
const wchar_t *automationResourceKey;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
|
||||
property Platform::String^ Name
|
||||
{
|
||||
Platform::String^ get() { return m_name; }
|
||||
}
|
||||
|
||||
property Platform::String^ AutomationName
|
||||
{
|
||||
Platform::String^ get() { return m_automationName; }
|
||||
}
|
||||
|
||||
property Platform::String^ Glyph
|
||||
{
|
||||
Platform::String^ get() { return m_glyph; }
|
||||
}
|
||||
|
||||
property int Position
|
||||
{
|
||||
int get() { return m_position; }
|
||||
}
|
||||
|
||||
property ViewMode Mode
|
||||
{
|
||||
ViewMode get()
|
||||
{
|
||||
return m_viewMode;
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String^ AutomationId
|
||||
{
|
||||
Platform::String^ get()
|
||||
{
|
||||
return m_viewMode.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String^ AccessKey
|
||||
{
|
||||
Platform::String^ get()
|
||||
{
|
||||
return m_accessKey;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
property bool SupportsNegative
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return m_supportsNegative;
|
||||
}
|
||||
}
|
||||
|
||||
// For saving/restoring last mode used.
|
||||
static int Serialize(ViewMode mode);
|
||||
static ViewMode Deserialize(Platform::Object^ obj);
|
||||
static ViewMode GetViewModeForFriendlyName(Platform::String^ name);
|
||||
|
||||
static bool IsValidViewMode(ViewMode mode);
|
||||
static bool IsCalculatorViewMode(ViewMode mode);
|
||||
static bool IsDateCalculatorViewMode(ViewMode mode);
|
||||
static bool IsConverterViewMode(ViewMode mode);
|
||||
|
||||
static Platform::String^ GetFriendlyName(ViewMode mode);
|
||||
static Platform::String^ GetNameResourceKey(ViewMode mode);
|
||||
static CategoryGroupType GetGroupType(ViewMode mode);
|
||||
|
||||
// GetIndex is 0-based, GetPostion is 1-based
|
||||
static int GetIndex(ViewMode mode);
|
||||
static int GetFlatIndex(ViewMode mode);
|
||||
static int GetIndexInGroup(ViewMode mode, CategoryGroupType type);
|
||||
static int GetPosition(ViewMode mode);
|
||||
|
||||
static ViewMode GetViewModeForVirtualKey(CalculatorApp::Common::MyVirtualKey virtualKey);
|
||||
|
||||
internal:
|
||||
NavCategory(Platform::String^ name, Platform::String^ automationName, Platform::String^ glyph, Platform::String^ accessKey, Platform::String^ mode, ViewMode viewMode, bool supportsNegative) :
|
||||
m_name(name),
|
||||
m_automationName(automationName),
|
||||
m_glyph(glyph),
|
||||
m_accessKey(accessKey),
|
||||
m_mode(mode),
|
||||
m_viewMode(viewMode),
|
||||
m_supportsNegative(supportsNegative)
|
||||
{
|
||||
m_position = NavCategory::GetPosition(m_viewMode);
|
||||
}
|
||||
|
||||
static std::vector<CalculatorApp::Common::MyVirtualKey> GetCategoryAcceleratorKeys();
|
||||
|
||||
private:
|
||||
static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType);
|
||||
|
||||
ViewMode m_viewMode;
|
||||
Platform::String^ m_name;
|
||||
Platform::String^ m_automationName;
|
||||
Platform::String^ m_glyph;
|
||||
Platform::String^ m_accessKey;
|
||||
Platform::String^ m_mode;
|
||||
int m_position;
|
||||
bool m_supportsNegative;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_R(Platform::String^, Name);
|
||||
OBSERVABLE_PROPERTY_R(Platform::String^, AutomationName);
|
||||
OBSERVABLE_PROPERTY_R(CategoryGroupType, GroupType);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<NavCategory^>^, Categories);
|
||||
|
||||
static Windows::Foundation::Collections::IObservableVector<NavCategoryGroup^>^ CreateMenuOptions();
|
||||
|
||||
static Platform::String^ GetHeaderResourceKey(CategoryGroupType type);
|
||||
|
||||
internal:
|
||||
static NavCategoryGroup^ CreateCalculatorCategory();
|
||||
static NavCategoryGroup^ CreateConverterCategory();
|
||||
|
||||
private:
|
||||
NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer);
|
||||
|
||||
static std::vector<NavCategoryInitializer> GetInitializerCategoryGroup(CategoryGroupType groupType);
|
||||
};
|
||||
}
|
||||
}
|
||||
57
src/CalcViewModel/Common/NetworkManager.cpp
Normal file
57
src/CalcViewModel/Common/NetworkManager.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NetworkManager.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace Platform;
|
||||
using namespace Windows::Networking::Connectivity;
|
||||
|
||||
NetworkManager::NetworkManager()
|
||||
{
|
||||
m_NetworkStatusChangedToken =
|
||||
NetworkInformation::NetworkStatusChanged += ref new NetworkStatusChangedEventHandler(
|
||||
this, &NetworkManager::OnNetworkStatusChange, CallbackContext::Same);
|
||||
}
|
||||
|
||||
NetworkManager::~NetworkManager()
|
||||
{
|
||||
NetworkInformation::NetworkStatusChanged -= m_NetworkStatusChangedToken;
|
||||
}
|
||||
|
||||
NetworkAccessBehavior NetworkManager::GetNetworkAccessBehavior()
|
||||
{
|
||||
NetworkAccessBehavior behavior = NetworkAccessBehavior::Offline;
|
||||
ConnectionProfile^ connectionProfile = NetworkInformation::GetInternetConnectionProfile();
|
||||
if (connectionProfile != nullptr)
|
||||
{
|
||||
NetworkConnectivityLevel connectivityLevel = connectionProfile->GetNetworkConnectivityLevel();
|
||||
if (connectivityLevel == NetworkConnectivityLevel::InternetAccess
|
||||
|| connectivityLevel == NetworkConnectivityLevel::ConstrainedInternetAccess)
|
||||
{
|
||||
ConnectionCost^ connectionCost = connectionProfile->GetConnectionCost();
|
||||
behavior = ConvertCostInfoToBehavior(connectionCost);
|
||||
}
|
||||
}
|
||||
|
||||
return behavior;
|
||||
}
|
||||
|
||||
void NetworkManager::OnNetworkStatusChange(_In_ Object^ /*sender*/)
|
||||
{
|
||||
NetworkBehaviorChanged(GetNetworkAccessBehavior());
|
||||
}
|
||||
|
||||
// See app behavior guidelines at https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj835821(v=win.10).aspx
|
||||
NetworkAccessBehavior NetworkManager::ConvertCostInfoToBehavior(_In_ ConnectionCost^ connectionCost)
|
||||
{
|
||||
if (connectionCost->Roaming || connectionCost->OverDataLimit
|
||||
|| connectionCost->NetworkCostType == NetworkCostType::Variable
|
||||
|| connectionCost->NetworkCostType == NetworkCostType::Fixed)
|
||||
{
|
||||
return NetworkAccessBehavior::OptIn;
|
||||
}
|
||||
|
||||
return NetworkAccessBehavior::Normal;
|
||||
}
|
||||
35
src/CalcViewModel/Common/NetworkManager.h
Normal file
35
src/CalcViewModel/Common/NetworkManager.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
public enum class NetworkAccessBehavior
|
||||
{
|
||||
Normal = 0,
|
||||
OptIn = 1,
|
||||
Offline = 2
|
||||
};
|
||||
|
||||
public delegate void NetworkBehaviorChangedHandler(NetworkAccessBehavior behavior);
|
||||
|
||||
public ref class NetworkManager sealed
|
||||
{
|
||||
public:
|
||||
NetworkManager();
|
||||
|
||||
static NetworkAccessBehavior GetNetworkAccessBehavior();
|
||||
|
||||
event NetworkBehaviorChangedHandler^ NetworkBehaviorChanged;
|
||||
|
||||
private:
|
||||
~NetworkManager();
|
||||
|
||||
void OnNetworkStatusChange(_In_ Platform::Object^ sender);
|
||||
static NetworkAccessBehavior ConvertCostInfoToBehavior(_In_ Windows::Networking::Connectivity::ConnectionCost^ connectionCost);
|
||||
|
||||
private:
|
||||
Windows::Foundation::EventRegistrationToken m_NetworkStatusChangedToken;
|
||||
};
|
||||
}
|
||||
56
src/CalcViewModel/Common/TraceActivity.h
Normal file
56
src/CalcViewModel/Common/TraceActivity.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
constexpr int64_t WINEVENT_KEYWORD_RESPONSE_TIME = 0x1000000000000;
|
||||
|
||||
// RAII wrapper that automatically sends the Stop event when the class gets destructed.
|
||||
class TraceActivity
|
||||
{
|
||||
public:
|
||||
TraceActivity() :
|
||||
m_channel(nullptr),
|
||||
m_activity(nullptr),
|
||||
m_fields(nullptr)
|
||||
{ }
|
||||
|
||||
TraceActivity(
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingChannel channel,
|
||||
std::wstring_view activityName,
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingFields fields) :
|
||||
m_channel(channel),
|
||||
m_activityName(activityName),
|
||||
m_fields(fields),
|
||||
m_activity(nullptr)
|
||||
{
|
||||
// Write the activity's START event. Note that you must not specify keyword
|
||||
// or level for START and STOP events because they always use the activity's
|
||||
// keyword and level.
|
||||
m_activity = m_channel.StartActivity(
|
||||
m_activityName,
|
||||
m_fields,
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingLevel::Verbose,
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingOptions(WINEVENT_KEYWORD_RESPONSE_TIME)
|
||||
);
|
||||
}
|
||||
|
||||
~TraceActivity()
|
||||
{
|
||||
if (m_activity != nullptr)
|
||||
{
|
||||
// Write the activity's STOP event.
|
||||
m_activity.StopActivity(m_activityName, m_fields);
|
||||
m_activity = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring m_activityName;
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingChannel m_channel;
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingFields m_fields;
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingActivity m_activity;
|
||||
};
|
||||
}
|
||||
985
src/CalcViewModel/Common/TraceLogger.cpp
Normal file
985
src/CalcViewModel/Common/TraceLogger.cpp
Normal file
@@ -0,0 +1,985 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "TraceLogger.h"
|
||||
#include "NetworkManager.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Concurrency;
|
||||
using namespace std;
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Diagnostics;
|
||||
using namespace winrt::Windows::Globalization;
|
||||
using namespace winrt::Windows::Globalization::DateTimeFormatting;
|
||||
using namespace winrt::Windows::System::UserProfile;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
static multimap<int, vector<wstring>> s_memoryMap;
|
||||
|
||||
static constexpr array<const wchar_t * const, 9> s_programmerType{
|
||||
L"N/A", L"QwordType", L"DwordType",
|
||||
L"WordType", L"ByteType", L"HexBase",
|
||||
L"DecBase", L"OctBase", L"BinBase" };
|
||||
static reader_writer_lock s_traceLoggerLock;
|
||||
|
||||
// Telemetry events. Uploaded to asimov.
|
||||
constexpr auto EVENT_NAME_DEBUG = L"Debug";
|
||||
constexpr auto EVENT_NAME_ERROR = L"ErrorMessage";
|
||||
constexpr auto EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM = L"AppPrelaunchedBySystem";
|
||||
constexpr auto EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER = L"PrelaunchedAppActivatedByUser";
|
||||
constexpr auto EVENT_NAME_APP_LAUNCH_BEGIN = L"AppLaunchBegin";
|
||||
constexpr auto EVENT_NAME_APP_LAUNCH_END = L"AppLaunchEnd";
|
||||
constexpr auto EVENT_NAME_APP_RESUME_END = L"AppResumeEnd";
|
||||
constexpr auto EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION = L"PreviousStateOnWindowCreation";
|
||||
constexpr auto EVENT_NAME_SIZE_ON_SUSPENSION = L"CalculatorSizeOnSuspension";
|
||||
constexpr auto EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION = L"CalculatorViewedInSession";
|
||||
constexpr auto EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION = L"DateCalculatorViewedInSession";
|
||||
constexpr auto EVENT_NAME_CONVERTER_VIEWED_IN_SESSION = L"ConverterViewedInSession";
|
||||
constexpr auto EVENT_NAME_MODE_CHANGE_BEGIN = L"ModeChangeBegin";
|
||||
constexpr auto EVENT_NAME_MODE_CHANGE_END = L"ModeChangeEnd";
|
||||
constexpr auto EVENT_NAME_HISTORY_BODY_OPENED = L"HistoryBodyOpened";
|
||||
constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN = L"HistoryItemLoadBegin";
|
||||
constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_END = L"HistoryItemLoadEnd";
|
||||
constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN = L"HistoryFlyoutOpenBegin";
|
||||
constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_END = L"HistoryFlyoutOpenEnd";
|
||||
constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_BEGIN = L"NewWindowCreationBegin";
|
||||
constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_END = L"NewWindowCreationEnd";
|
||||
constexpr auto EVENT_NAME_HISTORY_CLEAR = L"HistoryClearBegin";
|
||||
constexpr auto EVENT_NAME_MULTIPLE_MEMORY_USED = L"MultipleMemoryUsed";
|
||||
constexpr auto EVENT_NAME_SINGLE_MEMORY_USED = L"SingleMemoryUsed";
|
||||
constexpr auto EVENT_NAME_SHARED_MEMORY_USED = L"SharedMemoryUsed";
|
||||
constexpr auto EVENT_NAME_MEMORY_BODY_OPENED = L"MemoryBodyOpened";
|
||||
constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN = L"MemoryFlyoutOpenBegin";
|
||||
constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_END = L"MemoryFlyoutOpenEnd";
|
||||
constexpr auto EVENT_NAME_MEMORY_CLEAR_ALL = L"MemoryClearAll";
|
||||
constexpr auto EVENT_NAME_INVALID_INPUT_PASTED = L"InvalidInputPasted";
|
||||
constexpr auto EVENT_NAME_VALID_INPUT_PASTED = L"ValidInputPasted";
|
||||
constexpr auto EVENT_NAME_BITFLIP_PANE_CLICKED = L"BitFlipPaneClicked";
|
||||
constexpr auto EVENT_NAME_BITFLIP_BUTTONS_USED = L"BitFlipToggleButtonUsed";
|
||||
constexpr auto EVENT_NAME_ANGLE_BUTTONS_USED = L"AngleButtonUsedInSession";
|
||||
constexpr auto EVENT_NAME_HYP_BUTTON_USED = L"HypButtonUsedInSession";
|
||||
constexpr auto EVENT_NAME_FUNCTION_USAGE = L"FunctionUsageInSession";
|
||||
constexpr auto EVENT_NAME_BITLENGTH_BUTTON_USED = L"BitLengthButtonUsed";
|
||||
constexpr auto EVENT_NAME_RADIX_BUTTON_USED = L"RadixButtonUsed";
|
||||
constexpr auto EVENT_NAME_MAX_WINDOW_COUNT = L"MaxWindowCountInSession";
|
||||
constexpr auto EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL = L"WindowActivatedThroughProtocol";
|
||||
constexpr auto EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH = L"WindowLaunchedThroughTile";
|
||||
constexpr auto EVENT_NAME_DATE_DIFFERENCE_USED = L"DateDifferenceModeUsed";
|
||||
constexpr auto EVENT_NAME_DATE_ADD_SUBTRACT_USED = L"DateAddSubtractModeUsed";
|
||||
constexpr auto EVENT_NAME_DATE_DIFFERENCE_FOUND = L"DateDifferenceFound";
|
||||
constexpr auto EVENT_NAME_HIDE_IF_SHOWN = L"HideIfShown";
|
||||
constexpr auto EVENT_NAME_ABOUT_FLYOUT_OPENED = L"AboutFlyoutOpened";
|
||||
constexpr auto EVENT_NAME_NAV_BAR_OPENED = L"NavBarOpened";
|
||||
constexpr auto EVENT_NAME_CORE_WINDOW_WAS_NULL = L"CoreWindowWasNull";
|
||||
|
||||
constexpr auto EVENT_NAME_EXCEPTION = L"Exception";
|
||||
|
||||
#ifdef SEND_TELEMETRY
|
||||
// c.f. WINEVENT_KEYWORD_RESERVED_63-56 0xFF00000000000000 // Bits 63-56 - channel keywords
|
||||
// c.f. WINEVENT_KEYWORD_* 0x00FF000000000000 // Bits 55-48 - system-reserved keywords
|
||||
constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0x0000800000000000; // Bit 47
|
||||
constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0x0000400000000000; // Bit 46
|
||||
constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0x0000200000000000; // Bit 45
|
||||
constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment)
|
||||
#else
|
||||
// define all Keyword options as 0 when we do not want to upload app telemetry
|
||||
constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0;
|
||||
constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0;
|
||||
constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0;
|
||||
constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0;
|
||||
#endif
|
||||
|
||||
#pragma region TraceLogger setup and cleanup
|
||||
|
||||
TraceLogger::TraceLogger() :
|
||||
g_calculatorProvider(
|
||||
L"MicrosoftCalculator",
|
||||
LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), // Microsoft Telemetry group
|
||||
GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 }), //Unique providerID {0905CA09-610E-401E-B650-2F212980B9E0}
|
||||
m_appLaunchActivity{ nullptr }
|
||||
{
|
||||
// initialize the function array
|
||||
InitFunctionLogArray();
|
||||
}
|
||||
|
||||
TraceLogger::~TraceLogger()
|
||||
{
|
||||
}
|
||||
|
||||
TraceLogger& TraceLogger::GetInstance()
|
||||
{
|
||||
static TraceLogger s_selfInstance;
|
||||
return s_selfInstance;
|
||||
}
|
||||
|
||||
bool TraceLogger::GetTraceLoggingProviderEnabled() const
|
||||
{
|
||||
return g_calculatorProvider.Enabled();
|
||||
}
|
||||
|
||||
#pragma region Tracing methods
|
||||
void TraceLogger::LogTelemetryEvent(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY));
|
||||
}
|
||||
|
||||
void TraceLogger::LogMeasureEvent(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_MEASURES));
|
||||
}
|
||||
|
||||
void TraceLogger::LogCriticalDataEvent(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_CRITICAL_DATA));
|
||||
}
|
||||
|
||||
void TraceLogger::LogPerformanceEvent(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(WINEVENT_KEYWORD_RESPONSE_TIME));
|
||||
}
|
||||
|
||||
void TraceLogger::LogInfoEvent(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Information);
|
||||
}
|
||||
|
||||
unique_ptr<TraceActivity> TraceLogger::CreateTraceActivity(wstring_view eventName, LoggingFields fields) const
|
||||
{
|
||||
return make_unique<TraceActivity>(g_calculatorProvider, eventName, fields);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
void TraceLogger::InsertIntoMemoryMap(int windowId, bool isStandard, bool isScientific, bool isProgrammer)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
auto iterMap = s_memoryMap.find(windowId);
|
||||
if (iterMap == s_memoryMap.end())
|
||||
{
|
||||
s_memoryMap.insert(std::make_pair(windowId, vector<wstring>()));
|
||||
iterMap = s_memoryMap.find(windowId);
|
||||
}
|
||||
|
||||
if (isScientific)
|
||||
{
|
||||
iterMap->second.insert(iterMap->second.begin(), L"Scientific");
|
||||
}
|
||||
else if (isProgrammer)
|
||||
{
|
||||
iterMap->second.insert(iterMap->second.begin(), L"Programmer");
|
||||
}
|
||||
else
|
||||
{
|
||||
iterMap->second.insert(iterMap->second.begin(), L"Standard");
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::UpdateMemoryMap(int windowId, int memoryPosition, bool isStandard, bool isScientific, bool isProgrammer)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
auto iterMap = s_memoryMap.find(windowId);
|
||||
assert(iterMap != s_memoryMap.end());
|
||||
assert(iterMap->second.size() >= (unsigned int)memoryPosition);
|
||||
|
||||
if (isScientific)
|
||||
{
|
||||
iterMap->second[memoryPosition] = L"Scientific";
|
||||
}
|
||||
else if (isProgrammer)
|
||||
{
|
||||
iterMap->second[memoryPosition] = L"Programmer";
|
||||
}
|
||||
else
|
||||
{
|
||||
iterMap->second[memoryPosition] = L"Standard";
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::DeleteFromMemoryMap(int windowId, int memoryPosition)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
auto iterMap = s_memoryMap.find(windowId);
|
||||
assert(iterMap != s_memoryMap.end());
|
||||
|
||||
iterMap->second.erase(iterMap->second.begin() + memoryPosition);
|
||||
}
|
||||
|
||||
// return true if windowId is logged once else return false
|
||||
bool TraceLogger::UpdateWindowIdLog(int windowId)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
if (windowIdLog.find(windowId) == windowIdLog.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (windowIdLog[windowId] == false)
|
||||
{
|
||||
windowIdLog[windowId] = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// call comes here at the time of ApplicationViewModel initialisation
|
||||
void TraceLogger::LogCalculatorModeViewed(ViewMode mode, int windowId)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
// store windowId in windowIdLog which says we have logged mode for the present windowId.
|
||||
if (windowIdLog.find(windowId) == windowIdLog.end())
|
||||
{
|
||||
windowIdLog.insert(pair<int, bool>(windowId, false));
|
||||
}
|
||||
// if the event is not logged already for the present mode
|
||||
if (currentMode != mode)
|
||||
{
|
||||
currentMode = mode;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"CalculatorMode", NavCategory::GetFriendlyName(mode)->Data());
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION, fields);
|
||||
}
|
||||
}
|
||||
|
||||
// call comes here at the time of ApplicationViewModel initialization
|
||||
void TraceLogger::LogDateCalculatorModeViewed(ViewMode mode, int windowId)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
// store windowId in windowIdLog which says we have logged mode for the present windowId.
|
||||
if (windowIdLog.find(windowId) == windowIdLog.end())
|
||||
{
|
||||
windowIdLog.insert(pair<int, bool>(windowId, false));
|
||||
}
|
||||
// if the event is not logged already for the present mode
|
||||
if (currentMode != mode)
|
||||
{
|
||||
currentMode = mode;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"DateCalculatorMode", NavCategory::GetFriendlyName(mode)->Data());
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION, fields);
|
||||
}
|
||||
}
|
||||
|
||||
// call comes here at the time of ApplicationViewModel initialization
|
||||
void TraceLogger::LogConverterModeViewed(ViewMode mode, int windowId)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
if (windowIdLog.find(windowId) == windowIdLog.end())
|
||||
{
|
||||
windowIdLog.insert(pair<int, bool>(windowId, false));
|
||||
}
|
||||
// if the event is not logged already for the present mode
|
||||
if (currentMode != mode)
|
||||
{
|
||||
currentMode = mode;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"ConverterMode", NavCategory::GetFriendlyName(mode)->Data());
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_CONVERTER_VIEWED_IN_SESSION, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogSharedMemoryUsed(wstring_view fromMode, wstring_view toMode, unsigned int memorySize) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FromMode", fromMode);
|
||||
fields.AddString(L"ToMode", toMode);
|
||||
fields.AddUInt32(L"MemoryListSize", memorySize);
|
||||
LogTelemetryEvent(EVENT_NAME_SHARED_MEMORY_USED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogBitFlipPaneClicked() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_BITFLIP_PANE_CLICKED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogBitFlipUsed() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_BITFLIP_BUTTONS_USED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogAppLaunchStart()
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
if (!isAppLaunchBeginLogged)
|
||||
{
|
||||
m_appLaunchActivity = g_calculatorProvider.StartActivity(
|
||||
EVENT_NAME_APP_LAUNCH_BEGIN,
|
||||
nullptr,
|
||||
LoggingLevel::Verbose,
|
||||
LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY));
|
||||
|
||||
isAppLaunchBeginLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogAppLaunchComplete(/*Windows::ApplicationModel::Activation::ActivationKind activationKind, Windows::ApplicationModel::Activation::ApplicationExecutionState executionState*/)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
if ((m_appLaunchActivity != nullptr) && (!isAppLaunchEndLogged))
|
||||
{
|
||||
m_appLaunchActivity.StopActivity(EVENT_NAME_APP_LAUNCH_END);
|
||||
|
||||
isAppLaunchEndLogged = true;
|
||||
}
|
||||
|
||||
m_appLaunchActivity = nullptr;
|
||||
}
|
||||
|
||||
void TraceLogger::LogAppResumeComplete()
|
||||
{
|
||||
if (m_appLaunchActivity != nullptr)
|
||||
{
|
||||
m_appLaunchActivity.StopActivity(EVENT_NAME_APP_RESUME_END);
|
||||
}
|
||||
|
||||
m_appLaunchActivity = nullptr;
|
||||
}
|
||||
|
||||
void TraceLogger::LogDebug(wstring_view debugData)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"DebugData", debugData);
|
||||
LogTelemetryEvent(EVENT_NAME_DEBUG, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogOnAppLaunch(wstring_view previousExecutionState) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"PreviousExecutionState", previousExecutionState);
|
||||
LogTelemetryEvent(EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogAboutFlyoutOpened() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_ABOUT_FLYOUT_OPENED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogNavBarOpened() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_NAV_BAR_OPENED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogClearHistory() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_CLEAR, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogHistoryFlyoutOpenBegin(unsigned int historyItemCount) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
// we want to record the event only when history item count is atleast 20
|
||||
if (historyItemCount >= 20)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"HistoryItemCount", historyItemCount);
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogHistoryFlyoutOpenEnd(int historyItemCount) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
if (historyItemCount >= 20)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"HistoryItemCount", historyItemCount);
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_END, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogHistoryBodyOpened() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_BODY_OPENED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogMemoryFlyoutOpenBegin(unsigned int memoryItemCount) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
// we want to record the event only when memory item count is atleast 4
|
||||
if (memoryItemCount >= 4)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"MemoryItemCount", memoryItemCount);
|
||||
LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogMemoryFlyoutOpenEnd(unsigned int memoryItemCount) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
if (memoryItemCount >= 4)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"MemoryItemCount", memoryItemCount);
|
||||
LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_END, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogMemoryBodyOpened() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_MEMORY_BODY_OPENED, fields);
|
||||
}
|
||||
|
||||
//If calculator is launched in any mode other than standard then this call will come which is not intended. But there is no way to avoid it.
|
||||
//So don't use this function to analyze the count of mode change in session instead use CalculatorViewedInSession and ConverterViewedInSession to do that.
|
||||
//Use of this function is to analyze perf of mode change.
|
||||
void TraceLogger::LogModeChangeBegin(ViewMode fromMode, ViewMode toMode, int windowId)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
if (NavCategory::IsValidViewMode(fromMode) && NavCategory::IsValidViewMode(toMode))
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FromMode", NavCategory::GetFriendlyName(fromMode)->Data());
|
||||
fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data());
|
||||
fields.AddInt32(L"WindowId", windowId);
|
||||
LogMeasureEvent(EVENT_NAME_MODE_CHANGE_BEGIN, fields);
|
||||
}
|
||||
}
|
||||
|
||||
//comment: same as LogModeChangeBegin
|
||||
void TraceLogger::LogModeChangeEnd(ViewMode toMode, int windowId) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
if (NavCategory::IsValidViewMode(toMode))
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data());
|
||||
fields.AddInt32(L"WindowId", windowId);
|
||||
LogMeasureEvent(EVENT_NAME_MODE_CHANGE_END, fields);
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogHistoryItemLoadBegin() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogHistoryItemLoadEnd(unsigned int historyTokenCount) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"HistoryTokenCount", historyTokenCount);
|
||||
LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_END, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogNewWindowCreationBegin(int windowId)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_BEGIN, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogNewWindowCreationEnd(int windowId)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_END, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogError(wstring_view errorString)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"ErrorString", errorString);
|
||||
LogTelemetryEvent(EVENT_NAME_ERROR, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogPrelaunchedAppActivatedByUser()
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogAppPrelaunchedBySystem()
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogMemoryClearAll(int windowId)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
auto iterMap = s_memoryMap.find(windowId);
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_MEMORY_CLEAR_ALL, fields);
|
||||
|
||||
if (iterMap != s_memoryMap.end())
|
||||
{
|
||||
iterMap->second.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogMemoryUsed(int windowId, unsigned int slotPosition, bool isStandard, bool isScientific, bool isProgrammer, unsigned int memorySize) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
// Reader lock for the static resources
|
||||
reader_writer_lock::scoped_lock_read lock(s_traceLoggerLock);
|
||||
auto iterMap = s_memoryMap.find(windowId);
|
||||
|
||||
if (slotPosition == 0)
|
||||
{
|
||||
LogSingleMemoryUsed(memorySize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMultipleMemoryUsed(slotPosition, memorySize);
|
||||
}
|
||||
|
||||
if (iterMap != s_memoryMap.end())
|
||||
{
|
||||
// if current mode is not equal to mode of memoryMap[slotPosition] then LogSharedMemoryUsed
|
||||
if (isStandard && (iterMap->second[slotPosition] != L"Standard"))
|
||||
{
|
||||
LogSharedMemoryUsed(iterMap->second[slotPosition], L"Standard", memorySize);
|
||||
}
|
||||
else if (isScientific && (iterMap->second[slotPosition] != L"Scientific"))
|
||||
{
|
||||
LogSharedMemoryUsed(iterMap->second[slotPosition], L"Scientific", memorySize);
|
||||
}
|
||||
else if (isProgrammer && (iterMap->second[slotPosition] != L"Programmer"))
|
||||
{
|
||||
LogSharedMemoryUsed(iterMap->second[slotPosition], L"Programmer", memorySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogMultipleMemoryUsed(unsigned int slotPosition, unsigned int memorySize) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"MemoryIndex", slotPosition);
|
||||
fields.AddUInt32(L"MemoryListSize", memorySize);
|
||||
LogTelemetryEvent(EVENT_NAME_MULTIPLE_MEMORY_USED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogSingleMemoryUsed(unsigned int memorySize) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"MemoryListSize", memorySize);
|
||||
LogTelemetryEvent(EVENT_NAME_SINGLE_MEMORY_USED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogInvalidInputPasted(wstring_view reason, wstring_view pastedExpression, ViewMode mode, int programmerNumberBase, int bitLengthType)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data());
|
||||
fields.AddString(L"Reason", reason);
|
||||
fields.AddString(L"PastedExpression", pastedExpression);
|
||||
fields.AddString(L"ProgrammerNumberBase", GetProgrammerType(programmerNumberBase).c_str());
|
||||
fields.AddString(L"BitLengthType", GetProgrammerType(bitLengthType).c_str());
|
||||
LogTelemetryEvent(EVENT_NAME_INVALID_INPUT_PASTED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogValidInputPasted(ViewMode mode) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data());
|
||||
LogTelemetryEvent(EVENT_NAME_VALID_INPUT_PASTED, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogStandardException(wstring_view functionName, const exception& e) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FunctionName", functionName);
|
||||
wstringstream exceptionMessage;
|
||||
exceptionMessage << e.what();
|
||||
fields.AddString(L"ExceptionMessage", exceptionMessage.str());
|
||||
LogMeasureEvent(EVENT_NAME_EXCEPTION, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogWinRTException(wstring_view functionName, hresult_error const& e) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FunctionName", functionName);
|
||||
fields.AddInt32(L"HRESULT", e.code());
|
||||
fields.AddString(L"ExceptionMessage", e.message());
|
||||
LogMeasureEvent(EVENT_NAME_EXCEPTION, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogPlatformException(wstring_view functionName, Platform::Exception^ e) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FunctionName", functionName);
|
||||
fields.AddInt32(L"HRESULT", e->HResult);
|
||||
fields.AddString(L"ExceptionMessage", e->Message->Data());
|
||||
LogMeasureEvent(EVENT_NAME_EXCEPTION, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::UpdateFunctionUsage(int funcIndex)
|
||||
{
|
||||
// Writer lock for the static resources
|
||||
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
|
||||
|
||||
if (GetIndex(funcIndex))
|
||||
{
|
||||
// funcIndex is passed by reference and will be having the returned index
|
||||
funcLog[funcIndex].count++;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::InitFunctionLogArray()
|
||||
{
|
||||
int i = -1;
|
||||
for (int funcIndex = 0; funcIndex != maxFunctionSize; funcIndex++)
|
||||
{
|
||||
FunctionLogEnum func = safe_cast<FunctionLogEnum>(funcIndex);
|
||||
wstring functionName = func.ToString()->Data();
|
||||
if (functionName.compare(L"CalculatorApp.FunctionLogEnum") != 0)
|
||||
{
|
||||
findIndex[funcIndex] = ++i;
|
||||
funcLog.push_back(FuncLog(functionName));
|
||||
}
|
||||
}
|
||||
// update the functionCount with total function count which we are tracking through tracelog.
|
||||
functionCount = i;
|
||||
}
|
||||
|
||||
wstring TraceLogger::GetProgrammerType(int index)
|
||||
{
|
||||
if (index >= 0)
|
||||
{
|
||||
return s_programmerType[index];
|
||||
}
|
||||
//return "N/A"
|
||||
return s_programmerType[0];
|
||||
}
|
||||
|
||||
bool TraceLogger::GetIndex(int &index)
|
||||
{
|
||||
if (findIndex[index] > 0)
|
||||
{
|
||||
index = findIndex[index];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceLogger::UpdateWindowCount(size_t windowCount)
|
||||
{
|
||||
maxWindowCount = (maxWindowCount > windowCount) ? maxWindowCount : windowCount;
|
||||
windowLaunchCount++;
|
||||
}
|
||||
|
||||
void TraceLogger::LogMaxWindowCount()
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowCount", (unsigned int)maxWindowCount);
|
||||
LogTelemetryEvent(EVENT_NAME_MAX_WINDOW_COUNT, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogWindowActivated() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogWindowLaunched() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogBitLengthButtonUsed(int windowId)
|
||||
{
|
||||
if (bitLengthButtonUsage.find(windowId) == bitLengthButtonUsage.end())
|
||||
{
|
||||
bitLengthButtonUsage.insert(pair<int, int>(windowId, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
bitLengthButtonUsage[windowId]++;
|
||||
}
|
||||
if ((bitLengthButtonUsage[windowId] == 5) && !bitLengthButtonLoggedInSession)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_BITLENGTH_BUTTON_USED, fields);
|
||||
|
||||
bitLengthButtonLoggedInSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogRadixButtonUsed(int windowId)
|
||||
{
|
||||
if (radixButtonUsage.find(windowId) == radixButtonUsage.end())
|
||||
{
|
||||
radixButtonUsage.insert(pair<int, int>(windowId, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
radixButtonUsage[windowId]++;
|
||||
}
|
||||
if ((radixButtonUsage[windowId] == 2) && !radixButtonLoggedInSession)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_RADIX_BUTTON_USED, fields);
|
||||
|
||||
radixButtonLoggedInSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogAngleButtonUsed(int windowId)
|
||||
{
|
||||
if (angleButtonUsage.find(windowId) == angleButtonUsage.end())
|
||||
{
|
||||
angleButtonUsage.insert(pair<int, int>(windowId, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
angleButtonUsage[windowId]++;
|
||||
}
|
||||
if ((angleButtonUsage[windowId] == 2) && !angleButtonLoggedInSession)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_ANGLE_BUTTONS_USED, fields);
|
||||
|
||||
angleButtonLoggedInSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogFunctionUsage(int windowId)
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
for (int i = 0; i < functionCount; i++)
|
||||
{
|
||||
// log only those functions which are used
|
||||
if (funcLog[i].count > 0)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"FunctionName", funcLog[i].funcName.data());
|
||||
fields.AddUInt32(L"UsageCount", funcLog[i].count);
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_FUNCTION_USAGE, fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogHypButtonUsed(int windowId)
|
||||
{
|
||||
if (!isHypButtonLogged)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddUInt32(L"WindowId", windowId);
|
||||
LogTelemetryEvent(EVENT_NAME_HYP_BUTTON_USED, fields);
|
||||
|
||||
isHypButtonLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogDateDifferenceModeUsed(int windowId)
|
||||
{
|
||||
if (!m_dateDiffUsageLoggedInSession)
|
||||
{
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_USED, fields);
|
||||
|
||||
m_dateDiffUsageLoggedInSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogDateAddSubtractModeUsed(int windowId, bool isAddMode)
|
||||
{
|
||||
auto usageMap = isAddMode ? &m_dateAddModeUsage : &m_dateSubtractModeUsage;
|
||||
auto isLoggedInSession = isAddMode ? &m_dateAddUsageLoggedInSession : &m_dateSubtractUsageLoggedInSession;
|
||||
|
||||
if (usageMap->find(windowId) == usageMap->end())
|
||||
{
|
||||
usageMap->insert(pair<int, int>(windowId, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
(*usageMap)[windowId]++;
|
||||
}
|
||||
|
||||
// Ignore first 3 calls during the initialization of the combo box selected items for Add mode
|
||||
int firstChangeEventCount = isAddMode ? 4 : 1;
|
||||
|
||||
if (((*usageMap)[windowId] == firstChangeEventCount)
|
||||
&& (!(*isLoggedInSession)))
|
||||
{
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"AddSubtractMode", isAddMode ? L"Add" : L"Subtract");
|
||||
LogTelemetryEvent(EVENT_NAME_DATE_ADD_SUBTRACT_USED, fields);
|
||||
|
||||
*isLoggedInSession = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TraceLogger::LogDateClippedTimeDifferenceFound(Calendar const& today, DateTime const& clippedTime) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
auto calendarSystem = today.GetCalendarSystem();
|
||||
auto clock = today.GetClock();
|
||||
DateTimeFormatter dtFormatter{
|
||||
L"longdate shorttime",
|
||||
{ L"en-US" },
|
||||
GlobalizationPreferences::HomeGeographicRegion(),
|
||||
calendarSystem,
|
||||
clock };
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"ResolvedCalendarLanguage", today.ResolvedLanguage());
|
||||
fields.AddString(L"Timezone", today.GetTimeZone());
|
||||
fields.AddString(L"CalendarSystem", calendarSystem);
|
||||
fields.AddString(L"Clock", clock);
|
||||
fields.AddBoolean(L"IsDaylightSavingTime", today.IsDaylightSavingTime());
|
||||
fields.AddString(L"TodayDate", dtFormatter.Format(today.GetDateTime()));
|
||||
fields.AddString(L"ClippedDate", dtFormatter.Format(clippedTime));
|
||||
LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_FOUND, fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogUserRequestedRefreshFailed() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(L"UserRequestedRefreshFailed", fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogConversionResult(wstring_view fromValue, wstring_view fromUnit, wstring_view toValue, wstring_view toUnit) const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
wstring behaviorString{};
|
||||
NetworkAccessBehavior behavior = NetworkManager::GetNetworkAccessBehavior();
|
||||
switch (behavior)
|
||||
{
|
||||
case NetworkAccessBehavior::Offline:
|
||||
behaviorString = L"Offline";
|
||||
break;
|
||||
|
||||
case NetworkAccessBehavior::OptIn:
|
||||
behaviorString = L"Metered";
|
||||
break;
|
||||
|
||||
case NetworkAccessBehavior::Normal:
|
||||
default:
|
||||
behaviorString = L"Online";
|
||||
break;
|
||||
}
|
||||
|
||||
LoggingFields fields{};
|
||||
fields.AddString(L"NetworkAccess", behaviorString);
|
||||
fields.AddString(L"FromValue", fromValue);
|
||||
fields.AddString(L"FromUnit", fromUnit);
|
||||
fields.AddString(L"ToValue", toValue);
|
||||
fields.AddString(L"ToUnit", toUnit);
|
||||
LogTelemetryEvent(L"CurrencyConverterInputReceived", fields);
|
||||
}
|
||||
|
||||
void TraceLogger::LogViewClosingTelemetry(int windowId)
|
||||
{
|
||||
LogFunctionUsage(windowId);
|
||||
LogMaxWindowCount();
|
||||
}
|
||||
|
||||
void TraceLogger::LogCoreWindowWasNull() const
|
||||
{
|
||||
if (!GetTraceLoggingProviderEnabled()) return;
|
||||
|
||||
LoggingFields fields{};
|
||||
LogTelemetryEvent(EVENT_NAME_CORE_WINDOW_WAS_NULL, fields);
|
||||
}
|
||||
}
|
||||
|
||||
155
src/CalcViewModel/Common/TraceLogger.h
Normal file
155
src/CalcViewModel/Common/TraceLogger.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TraceActivity.h"
|
||||
|
||||
static const int maxFunctionSize = (int)CalculationManager::Command::CommandBINEDITEND;
|
||||
|
||||
// A trace logging provider can only be instantiated and registered once per module.
|
||||
// This class implements a singleton model ensure that only one instance is created.
|
||||
namespace CalculatorApp
|
||||
{
|
||||
struct FuncLog
|
||||
{
|
||||
public:
|
||||
int count;
|
||||
std::wstring funcName;
|
||||
FuncLog() { count = 0; }
|
||||
FuncLog(std::wstring fName)
|
||||
{
|
||||
funcName = fName;
|
||||
count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class TraceLogger
|
||||
{
|
||||
public:
|
||||
TraceLogger(_In_ TraceLogger const&) = delete;
|
||||
TraceLogger const & operator= (_In_ TraceLogger const&) = delete;
|
||||
~TraceLogger();
|
||||
static TraceLogger& GetInstance();
|
||||
bool GetTraceLoggingProviderEnabled() const;
|
||||
|
||||
void LogAppLaunchStart();
|
||||
void LogAppLaunchComplete();
|
||||
void LogAppResumeComplete();
|
||||
void LogOnAppLaunch(std::wstring_view previousExecutionState) const;
|
||||
void LogMemoryClearAll(int);
|
||||
void LogBitFlipPaneClicked() const;
|
||||
void LogBitFlipUsed() const;
|
||||
void LogHistoryBodyOpened() const;
|
||||
void LogHistoryItemLoadBegin() const;
|
||||
void LogHistoryItemLoadEnd(unsigned int) const;
|
||||
void LogHistoryFlyoutOpenBegin(unsigned int) const;
|
||||
void LogHistoryFlyoutOpenEnd(int) const;
|
||||
void LogCalculatorModeViewed(CalculatorApp::Common::ViewMode, int);
|
||||
void LogDateCalculatorModeViewed(CalculatorApp::Common::ViewMode, int);
|
||||
void LogConverterModeViewed(CalculatorApp::Common::ViewMode, int);
|
||||
void LogModeChangeBegin(CalculatorApp::Common::ViewMode, CalculatorApp::Common::ViewMode, int);
|
||||
void LogModeChangeEnd(CalculatorApp::Common::ViewMode, int) const;
|
||||
void LogClearHistory() const;
|
||||
void InsertIntoMemoryMap(int, bool, bool, bool);
|
||||
void UpdateMemoryMap(int, int, bool, bool, bool);
|
||||
void DeleteFromMemoryMap(int, int);
|
||||
void LogMemoryUsed(int, unsigned int, bool, bool, bool, unsigned int) const;
|
||||
void LogMultipleMemoryUsed(unsigned int, unsigned int) const;
|
||||
void LogSingleMemoryUsed(unsigned int) const;
|
||||
void LogSharedMemoryUsed(std::wstring_view, std::wstring_view, unsigned int) const;
|
||||
void LogMemoryBodyOpened() const;
|
||||
void LogMemoryFlyoutOpenBegin(unsigned int) const;
|
||||
void LogDebug(std::wstring_view debugData);
|
||||
void LogMemoryFlyoutOpenEnd(unsigned int) const;
|
||||
void LogInvalidInputPasted(std::wstring_view reason, std::wstring_view pastedExpression, CalculatorApp::Common::ViewMode mode, int ProgrammerNumberBase, int bitLengthType);
|
||||
void LogValidInputPasted(CalculatorApp::Common::ViewMode mode) const;
|
||||
void UpdateFunctionUsage(int func);
|
||||
void LogFunctionUsage(int);
|
||||
void InitFunctionLogArray();
|
||||
void LogBitLengthButtonUsed(int windowId);
|
||||
void LogRadixButtonUsed(int windowId);
|
||||
void LogAngleButtonUsed(int windowId);
|
||||
void LogHypButtonUsed(int windowId);
|
||||
void LogNewWindowCreationBegin(int windowId);
|
||||
void LogNewWindowCreationEnd(int windowId);
|
||||
void LogError(std::wstring_view errorString);
|
||||
void LogPrelaunchedAppActivatedByUser();
|
||||
void LogAppPrelaunchedBySystem();
|
||||
void UpdateWindowCount(size_t windowCount);
|
||||
bool UpdateWindowIdLog(int windowId);
|
||||
void LogMaxWindowCount();
|
||||
void LogWindowActivated() const;
|
||||
void LogWindowLaunched() const;
|
||||
void LogUserRequestedRefreshFailed() const;
|
||||
void LogConversionResult(std::wstring_view fromValue, std::wstring_view fromUnit, std::wstring_view toValue, std::wstring_view toUnit) const;
|
||||
void LogAboutFlyoutOpened() const;
|
||||
void LogNavBarOpened() const;
|
||||
void LogViewClosingTelemetry(int);
|
||||
void LogCoreWindowWasNull() const;
|
||||
|
||||
// Trace methods for Date Calculator usage
|
||||
void LogDateDifferenceModeUsed(int windowId);
|
||||
void LogDateAddSubtractModeUsed(int windowId, bool isAddMode);
|
||||
void LogDateClippedTimeDifferenceFound(winrt::Windows::Globalization::Calendar const& today, winrt::Windows::Foundation::DateTime const& clippedTime) const;
|
||||
|
||||
void LogStandardException(std::wstring_view functionName, _In_ const std::exception& e) const;
|
||||
void LogWinRTException(std::wstring_view functionName, _In_ winrt::hresult_error const& e) const;
|
||||
void LogPlatformException(std::wstring_view functionName, _In_ Platform::Exception^ e) const;
|
||||
|
||||
private:
|
||||
// Create an instance of TraceLogger
|
||||
TraceLogger();
|
||||
|
||||
// Any new Log method should
|
||||
// a) decide the level of logging. This will help us in limiting recording of events only upto a certain level. See this link for guidance https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx
|
||||
// We're using Verbose level for events that are called frequently and needed only for debugging or capturing perf for specific scenarios
|
||||
// b) should decide whether or not to log to telemetry and pass TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY) accordingly
|
||||
// c) Should accept a variable number of additional data arguments if needed
|
||||
void LogTelemetryEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
void LogMeasureEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
void LogCriticalDataEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
void LogPerformanceEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
void LogInfoEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
|
||||
std::unique_ptr<TraceActivity> CreateTraceActivity(std::wstring_view activityName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const;
|
||||
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingChannel g_calculatorProvider;
|
||||
|
||||
bool isSizeChangeLogged = false;
|
||||
bool isHideIfShownLogged = false;
|
||||
bool isSizeChangedFirstTime = true; // to track the first size changed event which is fired on the launch of app
|
||||
bool isAutoConversionBeginLoggedInSession = false;
|
||||
bool isAutoConversionEndLoggedInSession = false;
|
||||
bool angleButtonLoggedInSession = false;
|
||||
bool radixButtonLoggedInSession = false;
|
||||
bool bitLengthButtonLoggedInSession = false;
|
||||
GUID sessionGuid;
|
||||
CalculatorApp::Common::ViewMode currentMode = CalculatorApp::Common::ViewMode::None;
|
||||
std::vector<FuncLog> funcLog;
|
||||
int functionCount = 0;
|
||||
bool isHypButtonLogged = false;
|
||||
bool isAngleButtonInitialized = false;
|
||||
unsigned int findIndex[maxFunctionSize] = { 0 };
|
||||
bool GetIndex(int &index);
|
||||
std::wstring GetProgrammerType(int index);
|
||||
size_t maxWindowCount = 0;
|
||||
bool isAppLaunchBeginLogged = false;
|
||||
bool isAppLaunchEndLogged = false;
|
||||
std::map<int, int> bitLengthButtonUsage;
|
||||
std::map<int, int> angleButtonUsage;
|
||||
std::map<int, int> radixButtonUsage;
|
||||
std::map<int, bool> windowIdLog;
|
||||
|
||||
// Private variables for Date Calculator usage
|
||||
bool m_dateDiffUsageLoggedInSession = false;
|
||||
bool m_dateAddUsageLoggedInSession = false;
|
||||
bool m_dateSubtractUsageLoggedInSession = false;
|
||||
std::map<int, int> m_dateAddModeUsage;
|
||||
std::map<int, int> m_dateSubtractModeUsage;
|
||||
|
||||
size_t windowLaunchCount = 0;
|
||||
|
||||
winrt::Windows::Foundation::Diagnostics::LoggingActivity m_appLaunchActivity;
|
||||
};
|
||||
}
|
||||
234
src/CalcViewModel/Common/Utils.cpp
Normal file
234
src/CalcViewModel/Common/Utils.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//
|
||||
// Utils.cpp
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
#include "Common\AppResourceProvider.h"
|
||||
#include "Common\ExpressionCommandSerializer.h"
|
||||
#include "Common\ExpressionCommandDeserializer.h"
|
||||
#include "ViewState.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Utils;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::ViewManagement;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
|
||||
void Utils::IFTPlatformException(HRESULT hr)
|
||||
{
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Platform::Exception^ exception = ref new Platform::Exception(hr);
|
||||
throw(exception);
|
||||
}
|
||||
}
|
||||
|
||||
String^ Utils::GetStringValue(String^ input)
|
||||
{
|
||||
// Remove first and last " characters
|
||||
if (input->Length() >= 3)
|
||||
{
|
||||
wstring out(input->Begin() + 1, input->End() - 1);
|
||||
return ref new String(out.c_str());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
double Utils::GetDoubleFromWstring(wstring input)
|
||||
{
|
||||
wchar_t unWantedChars[] = { L' ', L',', 8234, 8235, 8236, 8237 };
|
||||
wstring ws = RemoveUnwantedCharsFromWstring(input, unWantedChars, 6);
|
||||
string inputString(ws.begin(), ws.end());
|
||||
return ::atof(inputString.c_str());
|
||||
}
|
||||
|
||||
//returns windowId for the current view
|
||||
int Utils::GetWindowId()
|
||||
{
|
||||
int windowId = -1;
|
||||
|
||||
auto window = CoreWindow::GetForCurrentThread();
|
||||
if (window != nullptr)
|
||||
{
|
||||
windowId = ApplicationView::GetApplicationViewIdForWindow(window);
|
||||
}
|
||||
|
||||
return windowId;
|
||||
}
|
||||
|
||||
void Utils::RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
|
||||
{
|
||||
if (currentDispatcher != nullptr)
|
||||
{
|
||||
auto task = create_task(currentDispatcher->RunAsync(CoreDispatcherPriority::Normal,
|
||||
ref new DispatchedHandler([function]()
|
||||
{
|
||||
function();
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
// returns if the last character of a wstring is the target wchar_t
|
||||
bool Utils::IsLastCharacterTarget(_In_ wstring const &input, _In_ wchar_t target)
|
||||
{
|
||||
return !input.empty() && input.back() == target;
|
||||
}
|
||||
|
||||
//return wstring after removing characters like space, comma, and double quotes
|
||||
wstring Utils::RemoveUnwantedCharsFromWstring(wstring input)
|
||||
{
|
||||
wchar_t unWantedChars[] = { L' ', L',', L'"', 8234, 8235, 8236, 8237 };
|
||||
return RemoveUnwantedCharsFromWstring(input, unWantedChars, 6);
|
||||
}
|
||||
|
||||
//return wstring after removing characters specified by unwantedChars array
|
||||
wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedChars, unsigned int size)
|
||||
{
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
input.erase(std::remove(input.begin(), input.end(), unwantedChars[i]), input.end());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
void Utils::SerializeCommandsAndTokens(_In_ shared_ptr<CalculatorVector <pair<wstring, int>>> const &tokens,
|
||||
_In_ shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> const &commands,
|
||||
DataWriter^ writer)
|
||||
{
|
||||
unsigned int commandsSize;
|
||||
IFTPlatformException(commands->GetSize(&commandsSize));
|
||||
|
||||
// save the size of the commands vector
|
||||
writer->WriteUInt32(commandsSize);
|
||||
|
||||
SerializeCommandVisitor cmdVisitor(writer);
|
||||
for (unsigned int i = 0; i < commandsSize; ++i)
|
||||
{
|
||||
shared_ptr<IExpressionCommand> exprCmd;
|
||||
IFTPlatformException(commands->GetAt(i, &exprCmd));
|
||||
|
||||
CalculationManager::CommandType commandType = exprCmd->GetCommandType();
|
||||
writer->WriteInt32(static_cast<int>(commandType));
|
||||
exprCmd->Accept(cmdVisitor);
|
||||
}
|
||||
|
||||
unsigned int tokensSize;
|
||||
IFTPlatformException(tokens->GetSize(&tokensSize));
|
||||
writer->WriteUInt32(tokensSize);
|
||||
|
||||
for (unsigned int i = 0; i < tokensSize; ++i)
|
||||
{
|
||||
pair<wstring, int> eachToken;
|
||||
IFTPlatformException(tokens->GetAt(i, &eachToken));
|
||||
|
||||
auto stringData = ref new Platform::String(eachToken.first.c_str());
|
||||
auto intData = eachToken.second;
|
||||
writer->WriteUInt32(writer->MeasureString(stringData));
|
||||
writer->WriteString(stringData);
|
||||
writer->WriteInt32(intData);
|
||||
}
|
||||
}
|
||||
|
||||
const shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> Utils::DeserializeCommands(DataReader^ reader)
|
||||
{
|
||||
shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> commandVector = make_shared<CalculatorVector<shared_ptr<IExpressionCommand>>>();
|
||||
auto commandVectorSize = reader->ReadUInt32();
|
||||
|
||||
CommandDeserializer cmdDeserializer(reader);
|
||||
for (unsigned int i = 0; i < commandVectorSize; ++i)
|
||||
{
|
||||
auto commandTypeInt = reader->ReadInt32();
|
||||
CalculationManager::CommandType commandType = static_cast<CalculationManager::CommandType>(commandTypeInt);
|
||||
shared_ptr<IExpressionCommand> exprCmd = cmdDeserializer.Deserialize(commandType);
|
||||
commandVector->Append(exprCmd);
|
||||
}
|
||||
|
||||
return commandVector;
|
||||
}
|
||||
|
||||
const shared_ptr<CalculatorVector <pair<wstring, int>>> Utils::DeserializeTokens(DataReader^ reader)
|
||||
{
|
||||
shared_ptr<CalculatorVector <pair<wstring, int>>> tokenVector = make_shared<CalculatorVector<pair<wstring, int>>>();
|
||||
auto tokensSize = reader->ReadUInt32();
|
||||
|
||||
for (unsigned int i = 0; i < tokensSize; ++i)
|
||||
{
|
||||
pair<wstring, int> eachToken;
|
||||
auto stringDataLen = reader->ReadUInt32();
|
||||
auto stringData = reader->ReadString(stringDataLen);
|
||||
auto intData = reader->ReadInt32();
|
||||
eachToken.first = stringData->Data();
|
||||
eachToken.second = intData;
|
||||
tokenVector->Append(eachToken);
|
||||
}
|
||||
|
||||
return tokenVector;
|
||||
}
|
||||
|
||||
DateTime Utils::GetUniversalSystemTime()
|
||||
{
|
||||
SYSTEMTIME sysTime = {};
|
||||
GetSystemTime(&sysTime);
|
||||
|
||||
FILETIME sysTimeAsFileTime = {};
|
||||
SystemTimeToFileTime(&sysTime, &sysTimeAsFileTime);
|
||||
|
||||
ULARGE_INTEGER ularge;
|
||||
ularge.HighPart = sysTimeAsFileTime.dwHighDateTime;
|
||||
ularge.LowPart = sysTimeAsFileTime.dwLowDateTime;
|
||||
|
||||
DateTime result;
|
||||
result.UniversalTime = ularge.QuadPart;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Utils::IsDateTimeOlderThan(DateTime dateTime, const long long duration)
|
||||
{
|
||||
DateTime now = Utils::GetUniversalSystemTime();
|
||||
return dateTime.UniversalTime + duration < now.UniversalTime;
|
||||
}
|
||||
|
||||
task<void> Utils::WriteFileToFolder(IStorageFolder^ folder, String^ fileName, String^ contents, CreationCollisionOption collisionOption)
|
||||
{
|
||||
if (folder == nullptr)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
StorageFile^ file = co_await folder->CreateFileAsync(fileName, collisionOption);
|
||||
if (file == nullptr)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
co_await FileIO::WriteTextAsync(file, contents);
|
||||
}
|
||||
|
||||
task<String^> Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName)
|
||||
{
|
||||
if (folder == nullptr)
|
||||
{
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
StorageFile^ file = co_await folder->GetFileAsync(fileName);
|
||||
if (file == nullptr)
|
||||
{
|
||||
co_return nullptr;
|
||||
}
|
||||
|
||||
String^ contents = co_await FileIO::ReadTextAsync(file);
|
||||
co_return contents;
|
||||
}
|
||||
414
src/CalcViewModel/Common/Utils.h
Normal file
414
src/CalcViewModel/Common/Utils.h
Normal file
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Utility macros to make Models easier to write
|
||||
// generates a member variable called m_<n>
|
||||
#define PROPERTY_R(t, n)\
|
||||
property t n {\
|
||||
t get() { return m_##n; }\
|
||||
private: void set(t value) { m_##n = value; }\
|
||||
} private: t m_##n; public:
|
||||
|
||||
#define PROPERTY_RW(t, n)\
|
||||
property t n {\
|
||||
t get() { return m_##n; }\
|
||||
void set(t value) { m_##n = value; }\
|
||||
} private: t m_##n; public:
|
||||
|
||||
#define OBSERVABLE_PROPERTY_R(t, n)\
|
||||
property t n {\
|
||||
t get() { return m_##n; }\
|
||||
private: void set(t value) {\
|
||||
if (m_##n != value) {\
|
||||
m_##n = value;\
|
||||
RaisePropertyChanged(L#n);\
|
||||
}}\
|
||||
} private: t m_##n; public:
|
||||
|
||||
#define OBSERVABLE_PROPERTY_RW(t, n)\
|
||||
property t n {\
|
||||
t get() { return m_##n; }\
|
||||
void set(t value) {\
|
||||
if (m_##n != value) {\
|
||||
m_##n = value;\
|
||||
RaisePropertyChanged(L#n);\
|
||||
}\
|
||||
}\
|
||||
} private: t m_##n; public:
|
||||
|
||||
#define NAMED_OBSERVABLE_PROPERTY_RW(t, n)\
|
||||
OBSERVABLE_PROPERTY_RW(t, n)\
|
||||
private: property Platform::StringReference n##_PropertyName {\
|
||||
Platform::StringReference get() { return Platform::StringReference(L#n); }\
|
||||
} public:
|
||||
|
||||
#define OBSERVABLE_PROPERTY_FIELD(n) m_##n
|
||||
|
||||
// This variant of the observable object is for objects that don't want to react to property changes
|
||||
#ifndef UNIT_TESTS
|
||||
#define OBSERVABLE_OBJECT() virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\
|
||||
internal: void RaisePropertyChanged(Platform::String^ p) {\
|
||||
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); } public:
|
||||
#else
|
||||
#define OBSERVABLE_OBJECT() virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\
|
||||
internal: void RaisePropertyChanged(Platform::String^ p) {\
|
||||
} public:
|
||||
#endif
|
||||
|
||||
// The callback specified in the macro is a method in the class that will be called every time the object changes
|
||||
// the callback is supposed to be have a single parameter of type Platform::String^
|
||||
#ifndef UNIT_TESTS
|
||||
#define OBSERVABLE_OBJECT_CALLBACK(c) virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\
|
||||
internal: void RaisePropertyChanged(Platform::String^ p) {\
|
||||
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p));\
|
||||
c(p);\
|
||||
} public:
|
||||
#else
|
||||
#define OBSERVABLE_OBJECT_CALLBACK(c) virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\
|
||||
internal: void RaisePropertyChanged(Platform::String^ p) {\
|
||||
c(p);\
|
||||
} public:
|
||||
#endif
|
||||
|
||||
// The variable member generated by this macro should not be used in the class code, use the
|
||||
// property getter instead.
|
||||
#define COMMAND_FOR_METHOD(p, m) property Windows::UI::Xaml::Input::ICommand^ p {\
|
||||
Windows::UI::Xaml::Input::ICommand^ get() {\
|
||||
if (!donotuse_##p) {\
|
||||
donotuse_##p = CalculatorApp::Common::MakeDelegate(this, &m);\
|
||||
} return donotuse_##p; }} private: Windows::UI::Xaml::Input::ICommand^ donotuse_##p; public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_DECLARATION(t, n)\
|
||||
property t n {\
|
||||
t get() { return safe_cast<t>(GetValue(s_##n##Property)); }\
|
||||
void set(t value) { SetValue(s_##n##Property, value); } }\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ s_##n##Property; public:
|
||||
|
||||
// Utilities for DependencyProperties
|
||||
namespace Utils
|
||||
{
|
||||
namespace Details
|
||||
{
|
||||
template <typename T>
|
||||
struct IsRefClass
|
||||
{
|
||||
static const bool value = __is_ref_class(T);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RemoveHat
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RemoveHat<T^>
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<IsRefClass<T>::value, T^>::type MakeDefault()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!IsRefClass<T>::value, T>::type MakeDefault()
|
||||
{
|
||||
return T();
|
||||
}
|
||||
|
||||
// There's a bug in Xaml in which custom enums are not recognized by the property system/binding
|
||||
// therefore this template will determine that for enums the type to use to register the
|
||||
// DependencyProperty is to be Object, for everything else it will use the type
|
||||
// NOTE: If we are to find more types in which this is broken this template
|
||||
// will be specialized for those types to return Object
|
||||
template <typename T>
|
||||
struct TypeToUseForDependencyProperty
|
||||
{
|
||||
typedef typename std::conditional<std::is_enum<T>::value, Platform::Object, T>::type type;
|
||||
};
|
||||
}
|
||||
|
||||
const wchar_t LRE = 0x202a; // Left-to-Right Embedding
|
||||
const wchar_t PDF = 0x202c; // Pop Directional Formatting
|
||||
const wchar_t LRO = 0x202d; // Left-to-Right Override
|
||||
|
||||
// Regular DependencyProperty
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty(
|
||||
_In_ const wchar_t* const name,
|
||||
_In_ Windows::UI::Xaml::PropertyMetadata^ metadata)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TOwner>::type OwnerType;
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
typedef typename Details::TypeToUseForDependencyProperty<ThisPropertyType>::type ThisDependencyPropertyType;
|
||||
|
||||
static_assert(Details::IsRefClass<OwnerType>::value, "The owner of a DependencyProperty must be a ref class");
|
||||
|
||||
return Windows::UI::Xaml::DependencyProperty::Register(
|
||||
Platform::StringReference(name),
|
||||
ThisDependencyPropertyType::typeid, // Work around bugs in Xaml by using the filtered type
|
||||
OwnerType::typeid,
|
||||
metadata);
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty(_In_ const wchar_t* const name)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
|
||||
return RegisterDependencyProperty<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(Details::MakeDefault<ThisPropertyType>()));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyProperty(_In_ const wchar_t* const name, TType defaultValue)
|
||||
{
|
||||
return RegisterDependencyProperty<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(defaultValue));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType, typename TCallback>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyWithCallback(
|
||||
_In_ wchar_t const * const name,
|
||||
TCallback callback)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
return RegisterDependencyProperty<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(
|
||||
Details::MakeDefault<ThisPropertyType>(),
|
||||
ref new Windows::UI::Xaml::PropertyChangedCallback(callback)));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType, typename TCallback>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyWithCallback(
|
||||
_In_ wchar_t const * const name,
|
||||
TType defaultValue,
|
||||
TCallback callback)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
return RegisterDependencyProperty<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(
|
||||
defaultValue,
|
||||
ref new Windows::UI::Xaml::PropertyChangedCallback(callback)));
|
||||
}
|
||||
|
||||
// Attached DependencyProperty
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached(
|
||||
_In_ const wchar_t* const name,
|
||||
_In_ Windows::UI::Xaml::PropertyMetadata^ metadata)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TOwner>::type OwnerType;
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
typedef typename Details::TypeToUseForDependencyProperty<ThisPropertyType>::type ThisDependencyPropertyType;
|
||||
|
||||
static_assert(Details::IsRefClass<OwnerType>::value, "The owner of a DependencyProperty must be a ref class");
|
||||
|
||||
return Windows::UI::Xaml::DependencyProperty::RegisterAttached(
|
||||
Platform::StringReference(name),
|
||||
ThisDependencyPropertyType::typeid, // Work around bugs in Xaml by using the filtered type
|
||||
OwnerType::typeid,
|
||||
metadata);
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached(_In_ const wchar_t* const name)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
return RegisterDependencyPropertyAttached<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(Details::MakeDefault<ThisPropertyType>()));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttached(_In_ const wchar_t* const name, TType defaultValue)
|
||||
{
|
||||
return RegisterDependencyPropertyAttached<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(defaultValue));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType, typename TCallback>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttachedWithCallback(
|
||||
_In_ wchar_t const * const name,
|
||||
TCallback callback)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
return RegisterDependencyPropertyAttached<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(
|
||||
Details::MakeDefault<ThisPropertyType>(),
|
||||
ref new Windows::UI::Xaml::PropertyChangedCallback(callback)));
|
||||
}
|
||||
|
||||
template <typename TOwner, typename TType, typename TCallback>
|
||||
Windows::UI::Xaml::DependencyProperty^ RegisterDependencyPropertyAttachedWithCallback(
|
||||
_In_ wchar_t const * const name,
|
||||
TType defaultValue,
|
||||
TCallback callback)
|
||||
{
|
||||
typedef typename Details::RemoveHat<TType>::type ThisPropertyType;
|
||||
return RegisterDependencyPropertyAttached<TOwner, TType>(
|
||||
name,
|
||||
ref new Windows::UI::Xaml::PropertyMetadata(
|
||||
defaultValue,
|
||||
ref new Windows::UI::Xaml::PropertyChangedCallback(callback)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Swap(T *ref1, T *ref2)
|
||||
{
|
||||
T temp = *ref1;
|
||||
*ref1 = *ref2;
|
||||
*ref2 = temp;
|
||||
}
|
||||
|
||||
void IFTPlatformException(HRESULT hr);
|
||||
Platform::String^ GetStringValue(Platform::String^ input);
|
||||
bool IsLastCharacterTarget(std::wstring const &input, wchar_t target);
|
||||
std::wstring RemoveUnwantedCharsFromWstring(std::wstring inputString, wchar_t* unwantedChars, unsigned int size);
|
||||
std::wstring RemoveUnwantedCharsFromWstring(std::wstring input);
|
||||
double GetDoubleFromWstring(std::wstring input);
|
||||
int GetWindowId();
|
||||
void RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ Windows::UI::Core::CoreDispatcher^ currentDispatcher);
|
||||
void SerializeCommandsAndTokens(_In_ std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const &tokens,
|
||||
_In_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands,
|
||||
Windows::Storage::Streams::DataWriter^ writer);
|
||||
|
||||
const std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> DeserializeCommands(Windows::Storage::Streams::DataReader^ reader);
|
||||
const std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> DeserializeTokens(Windows::Storage::Streams::DataReader^ reader);
|
||||
|
||||
Windows::Foundation::DateTime GetUniversalSystemTime();
|
||||
bool IsDateTimeOlderThan(Windows::Foundation::DateTime dateTime, const long long duration);
|
||||
|
||||
concurrency::task<void> WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption);
|
||||
concurrency::task<Platform::String^> ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName);
|
||||
}
|
||||
|
||||
// This goes into the header to define the property, in the public: section of the class
|
||||
#define DEPENDENCY_PROPERTY_OWNER(owner)\
|
||||
private: typedef owner DependencyPropertiesOwner; public:
|
||||
|
||||
// Normal DependencyProperty
|
||||
#define DEPENDENCY_PROPERTY(type, name)\
|
||||
property type name {\
|
||||
type get() { return safe_cast<type>(GetValue(s_##name##Property)); }\
|
||||
void set(type value) { SetValue(s_##name##Property, value); }\
|
||||
} private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyProperty<DependencyPropertiesOwner, type>(L#name); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_WITH_DEFAULT(type, name, defaultValue)\
|
||||
property type name {\
|
||||
type get() { return safe_cast<type>(GetValue(s_##name##Property)); }\
|
||||
void set(type value) { SetValue(s_##name##Property, value); }\
|
||||
} private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyProperty<DependencyPropertiesOwner, type>(L#name, defaultValue); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_WITH_CALLBACK(type, name)\
|
||||
property type name {\
|
||||
type get() { return safe_cast<type>(GetValue(s_##name##Property)); }\
|
||||
void set(type value) { SetValue(s_##name##Property, value); }\
|
||||
} private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyWithCallback<DependencyPropertiesOwner, type>(L#name, &On##name##PropertyChangedImpl); }\
|
||||
static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\
|
||||
auto self = safe_cast<DependencyPropertiesOwner^>(sender);\
|
||||
self->On##name##PropertyChanged(safe_cast<type>(args->OldValue), safe_cast<type>(args->NewValue)); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue)\
|
||||
property type name {\
|
||||
type get() { return safe_cast<type>(GetValue(s_##name##Property)); }\
|
||||
void set(type value) { SetValue(s_##name##Property, value); }\
|
||||
} private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyWithCallback<DependencyPropertiesOwner, type>(L#name, defaultValue, &On##name##PropertyChangedImpl); }\
|
||||
static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\
|
||||
auto self = safe_cast<DependencyPropertiesOwner^>(sender);\
|
||||
self->On##name##PropertyChanged(safe_cast<type>(args->OldValue), safe_cast<type>(args->NewValue)); } public:
|
||||
|
||||
// Attached DependencyProperty
|
||||
#define DEPENDENCY_PROPERTY_ATTACHED(type, name)\
|
||||
static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast<type>(target->GetValue(s_##name##Property)); }\
|
||||
static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyAttached<DependencyPropertiesOwner, type>(L#name); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT(type, name, defaultValue)\
|
||||
static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast<type>(target->GetValue(s_##name##Property)); }\
|
||||
static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyAttached<DependencyPropertiesOwner, type>(L#name, defaultValue); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(type, name)\
|
||||
static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast<type>(target->GetValue(s_##name##Property)); }\
|
||||
static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyAttachedWithCallback<DependencyPropertiesOwner, type>(L#name, &On##name##PropertyChangedImpl); }\
|
||||
static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\
|
||||
On##name##PropertyChanged(sender, safe_cast<type>(args->OldValue), safe_cast<type>(args->NewValue)); } public:
|
||||
|
||||
#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue)\
|
||||
static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast<type>(target->GetValue(s_##name##Property)); }\
|
||||
static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\
|
||||
public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\
|
||||
Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\
|
||||
}\
|
||||
private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\
|
||||
return Utils::RegisterDependencyPropertyAttachedWithCallback<DependencyPropertiesOwner, type>(L#name, defaultValue, &On##name##PropertyChangedImpl); }\
|
||||
static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\
|
||||
On##name##PropertyChanged(sender, safe_cast<type>(args->OldValue), safe_cast<type>(args->NewValue)); } public:
|
||||
|
||||
// This goes into the cpp to initalize the static variable
|
||||
#define DEPENDENCY_PROPERTY_INITIALIZATION(owner, name)\
|
||||
Windows::UI::Xaml::DependencyProperty^ owner::s_##name##Property =\
|
||||
owner::Initialize##name##Property();
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
template <typename T>
|
||||
T from_cx(Platform::Object^ from)
|
||||
{
|
||||
T to{ nullptr };
|
||||
|
||||
winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)->QueryInterface(winrt::guid_of<T>(),
|
||||
reinterpret_cast<void**>(winrt::put_abi(to))));
|
||||
|
||||
return to;
|
||||
}
|
||||
}
|
||||
83
src/CalcViewModel/Common/ValidatingConverters.h
Normal file
83
src/CalcViewModel/Common/ValidatingConverters.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp { namespace Common
|
||||
{
|
||||
public ref class ValidSelectedItemConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
ValidSelectedItemConverter()
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
||||
virtual Platform::Object^ Convert(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
|
||||
{
|
||||
// Pass through as we don't want to change the value from the source
|
||||
return value;
|
||||
}
|
||||
|
||||
virtual Platform::Object^ ConverBack(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
// Stop the binding if the object is nullptr
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue;
|
||||
}
|
||||
};
|
||||
|
||||
public ref class ValidSelectedIndexConverter sealed: public Windows::UI::Xaml::Data::IValueConverter
|
||||
{
|
||||
public:
|
||||
ValidSelectedIndexConverter()
|
||||
{ }
|
||||
|
||||
private:
|
||||
|
||||
virtual Platform::Object^ Convert(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
|
||||
{
|
||||
// Pass through as we don't want to change the value from the source
|
||||
return value;
|
||||
}
|
||||
|
||||
virtual Platform::Object^ ConverBack(
|
||||
Platform::Object^ value,
|
||||
Windows::UI::Xaml::Interop::TypeName /*targetType*/,
|
||||
Platform::Object^ /*parameter*/,
|
||||
Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
|
||||
{
|
||||
// The value to be valid has to be a boxed int32 value
|
||||
// extract that value and ensure it is valid, ie >= 0
|
||||
if (value)
|
||||
{
|
||||
auto box = dynamic_cast<Windows::Foundation::IPropertyValue^>(value);
|
||||
if (box && box->Type == Windows::Foundation::PropertyType::Int32)
|
||||
{
|
||||
int index = box->GetInt32();
|
||||
if (index >= 0)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// The value is not valid therefore stop the binding right here
|
||||
return Windows::UI::Xaml::DependencyProperty::UnsetValue;
|
||||
}
|
||||
};
|
||||
}}
|
||||
814
src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp
Normal file
814
src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp
Normal file
@@ -0,0 +1,814 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CurrencyDataLoader.h"
|
||||
#include "UnitConverterDataConstants.h"
|
||||
#include "Common\AppResourceProvider.h"
|
||||
#include "Common\LocalizationStringUtil.h"
|
||||
#include "Common\LocalizationService.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::LocalizationServiceProperties;
|
||||
using namespace CalculatorApp::DataLoaders;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace CalculatorApp::ViewModel::CurrencyDataLoaderConstants;
|
||||
using namespace concurrency;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace UnitConversionManager;
|
||||
using namespace Windows::ApplicationModel::Resources::Core;
|
||||
using namespace Windows::Data::Json;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::Globalization::DateTimeFormatting;
|
||||
using namespace Windows::Globalization::NumberFormatting;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::System::UserProfile;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::Web::Http;
|
||||
|
||||
static constexpr auto CURRENCY_UNIT_FROM_KEY = L"CURRENCY_UNIT_FROM_KEY";
|
||||
static constexpr auto CURRENCY_UNIT_TO_KEY = L"CURRENCY_UNIT_TO_KEY";
|
||||
|
||||
// Calculate number of 100-nanosecond intervals-per-day
|
||||
// (1 interval/100 nanosecond)(100 nanosecond/1e-7 s)(60 s/1 min)(60 min/1 hr)(24 hr/1 day) = (interval/day)
|
||||
static constexpr long long DAY_DURATION = 1LL * 60 * 60 * 24 * 10000000;
|
||||
static constexpr long long WEEK_DURATION = DAY_DURATION * 7;
|
||||
|
||||
static constexpr int FORMATTER_DIGIT_COUNT = 4;
|
||||
|
||||
static constexpr auto CACHE_TIMESTAMP_KEY = L"CURRENCY_CONVERTER_TIMESTAMP";
|
||||
static constexpr auto CACHE_LANGCODE_KEY = L"CURRENCY_CONVERTER_LANGCODE";
|
||||
static constexpr auto CACHE_DELIMITER = L"%";
|
||||
|
||||
static constexpr auto STATIC_DATA_FILENAME = L"CURRENCY_CONVERTER_STATIC_DATA.txt";
|
||||
static constexpr array<wstring_view, 5> STATIC_DATA_PROPERTIES = {
|
||||
wstring_view{ L"CountryCode", 11 },
|
||||
wstring_view{ L"CountryName", 11 },
|
||||
wstring_view{ L"CurrencyCode", 12 },
|
||||
wstring_view{ L"CurrencyName", 12 },
|
||||
wstring_view{ L"CurrencySymbol", 14 }
|
||||
};
|
||||
|
||||
static constexpr auto ALL_RATIOS_DATA_FILENAME = L"CURRENCY_CONVERTER_ALL_RATIOS_DATA.txt";
|
||||
static constexpr auto RATIO_KEY = L"Rt";
|
||||
static constexpr auto CURRENCY_CODE_KEY = L"An";
|
||||
static constexpr array<wstring_view, 2> ALL_RATIOS_DATA_PROPERTIES = {
|
||||
wstring_view{ RATIO_KEY, 2 },
|
||||
wstring_view{ CURRENCY_CODE_KEY, 2 }
|
||||
};
|
||||
|
||||
static constexpr auto DEFAULT_FROM_TO_CURRENCY_FILE_URI = L"ms-appx:///DataLoaders/DefaultFromToCurrency.json";
|
||||
static constexpr auto FROM_KEY = L"from";
|
||||
static constexpr auto TO_KEY = L"to";
|
||||
|
||||
// Fallback default values.
|
||||
static constexpr auto DEFAULT_FROM_CURRENCY = DefaultCurrencyCode.data();
|
||||
static constexpr auto DEFAULT_TO_CURRENCY = L"EUR";
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
namespace UnitConverterResourceKeys
|
||||
{
|
||||
StringReference CurrencyUnitFromKey(CURRENCY_UNIT_FROM_KEY);
|
||||
StringReference CurrencyUnitToKey(CURRENCY_UNIT_TO_KEY);
|
||||
}
|
||||
|
||||
namespace CurrencyDataLoaderConstants
|
||||
{
|
||||
StringReference CacheTimestampKey(CACHE_TIMESTAMP_KEY);
|
||||
StringReference CacheLangcodeKey(CACHE_LANGCODE_KEY);
|
||||
StringReference CacheDelimiter(CACHE_DELIMITER);
|
||||
StringReference StaticDataFilename(STATIC_DATA_FILENAME);
|
||||
StringReference AllRatiosDataFilename(ALL_RATIOS_DATA_FILENAME);
|
||||
long long DayDuration = DAY_DURATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr<ICurrencyHttpClient> client) :
|
||||
m_client(move(client)),
|
||||
m_loadStatus(CurrencyLoadStatus::NotLoaded),
|
||||
m_responseLanguage(L"en-US"),
|
||||
m_ratioFormat(L""),
|
||||
m_timestampFormat(L""),
|
||||
m_networkManager(ref new NetworkManager()),
|
||||
m_meteredOverrideSet(false)
|
||||
{
|
||||
if (GlobalizationPreferences::Languages->Size > 0)
|
||||
{
|
||||
m_responseLanguage = GlobalizationPreferences::Languages->GetAt(0);
|
||||
}
|
||||
|
||||
if (m_client != nullptr)
|
||||
{
|
||||
m_client->SetSourceCurrencyCode(StringReference(DefaultCurrencyCode.data()));
|
||||
m_client->SetResponseLanguage(m_responseLanguage);
|
||||
}
|
||||
|
||||
if (CoreWindow::GetForCurrentThread() != nullptr)
|
||||
{
|
||||
// Must have a CoreWindow to access the resource context.
|
||||
m_isRtlLanguage = LocalizationService::GetInstance()->IsRtlLayout();
|
||||
}
|
||||
|
||||
m_ratioFormatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter();
|
||||
m_ratioFormatter->IsGrouped = true;
|
||||
m_ratioFormatter->IsDecimalPointAlwaysDisplayed = true;
|
||||
m_ratioFormatter->FractionDigits = FORMATTER_DIGIT_COUNT;
|
||||
|
||||
m_ratioFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyFromToRatioFormat")->Data();
|
||||
m_timestampFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyTimestampFormat")->Data();
|
||||
}
|
||||
|
||||
CurrencyDataLoader::~CurrencyDataLoader()
|
||||
{
|
||||
UnregisterForNetworkBehaviorChanges();
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::UnregisterForNetworkBehaviorChanges()
|
||||
{
|
||||
m_networkManager->NetworkBehaviorChanged -= m_networkBehaviorToken;
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::RegisterForNetworkBehaviorChanges()
|
||||
{
|
||||
UnregisterForNetworkBehaviorChanges();
|
||||
|
||||
m_networkBehaviorToken =
|
||||
m_networkManager->NetworkBehaviorChanged += ref new NetworkBehaviorChangedHandler([this](NetworkAccessBehavior newBehavior)
|
||||
{
|
||||
this->OnNetworkBehaviorChanged(newBehavior);
|
||||
});
|
||||
|
||||
OnNetworkBehaviorChanged(NetworkManager::GetNetworkAccessBehavior());
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::OnNetworkBehaviorChanged(NetworkAccessBehavior newBehavior)
|
||||
{
|
||||
m_networkAccessBehavior = newBehavior;
|
||||
if (m_vmCallback != nullptr)
|
||||
{
|
||||
m_vmCallback->NetworkBehaviorChanged(static_cast<int>(m_networkAccessBehavior));
|
||||
}
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::LoadFinished()
|
||||
{
|
||||
return m_loadStatus != CurrencyLoadStatus::NotLoaded;
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::LoadedFromCache()
|
||||
{
|
||||
return m_loadStatus == CurrencyLoadStatus::LoadedFromCache;
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::LoadedFromWeb()
|
||||
{
|
||||
return m_loadStatus == CurrencyLoadStatus::LoadedFromWeb;
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::ResetLoadStatus()
|
||||
{
|
||||
m_loadStatus = CurrencyLoadStatus::NotLoaded;
|
||||
}
|
||||
|
||||
#pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321
|
||||
void CurrencyDataLoader::LoadData()
|
||||
{
|
||||
RegisterForNetworkBehaviorChanges();
|
||||
|
||||
if (!LoadFinished())
|
||||
{
|
||||
create_task([this]() -> task<bool>
|
||||
{
|
||||
vector<function<task<bool>()>> loadFunctions = {
|
||||
[this]() { return TryLoadDataFromCacheAsync(); },
|
||||
[this]() { return TryLoadDataFromWebAsync(); },
|
||||
};
|
||||
|
||||
bool didLoad = false;
|
||||
for (auto& f : loadFunctions)
|
||||
{
|
||||
didLoad = co_await f();
|
||||
if (didLoad)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
co_return didLoad;
|
||||
}).then([this](bool didLoad)
|
||||
{
|
||||
UpdateDisplayedTimestamp();
|
||||
NotifyDataLoadFinished(didLoad);
|
||||
}, task_continuation_context::use_current());
|
||||
}
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
|
||||
vector<UCM::Category> CurrencyDataLoader::LoadOrderedCategories()
|
||||
{
|
||||
// This function should not be called
|
||||
// The model will use the categories from UnitConverterDataLoader
|
||||
return vector<UCM::Category>();
|
||||
}
|
||||
|
||||
vector<UCM::Unit> CurrencyDataLoader::LoadOrderedUnits(const UCM::Category& /*category*/)
|
||||
{
|
||||
lock_guard<mutex> lock(m_currencyUnitsMutex);
|
||||
return m_currencyUnits;
|
||||
}
|
||||
|
||||
unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> CurrencyDataLoader::LoadOrderedRatios(const UCM::Unit& unit)
|
||||
{
|
||||
lock_guard<mutex> lock(m_currencyUnitsMutex);
|
||||
return m_currencyRatioMap.at(unit);
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::SupportsCategory(const UCM::Category& target)
|
||||
{
|
||||
static int currencyId = NavCategory::Serialize(ViewMode::Currency);
|
||||
return target.id == currencyId;
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::SetViewModelCallback(const shared_ptr<UCM::IViewModelCurrencyCallback>& callback)
|
||||
{
|
||||
m_vmCallback = callback;
|
||||
OnNetworkBehaviorChanged(m_networkAccessBehavior);
|
||||
}
|
||||
|
||||
pair<wstring, wstring> CurrencyDataLoader::GetCurrencySymbols(const UCM::Unit& unit1, const UCM::Unit& unit2)
|
||||
{
|
||||
lock_guard<mutex> lock(m_currencyUnitsMutex);
|
||||
|
||||
wstring symbol1 = L"";
|
||||
wstring symbol2 = L"";
|
||||
|
||||
auto itr1 = m_currencyMetadata.find(unit1);
|
||||
auto itr2 = m_currencyMetadata.find(unit2);
|
||||
if (itr1 != m_currencyMetadata.end() && itr2 != m_currencyMetadata.end())
|
||||
{
|
||||
symbol1 = (itr1->second).symbol;
|
||||
symbol2 = (itr2->second).symbol;
|
||||
}
|
||||
|
||||
return make_pair(symbol1, symbol2);
|
||||
}
|
||||
|
||||
pair<wstring, wstring> CurrencyDataLoader::GetCurrencyRatioEquality(_In_ const UCM::Unit& unit1, _In_ const UCM::Unit& unit2)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto iter = m_currencyRatioMap.find(unit1);
|
||||
if (iter != m_currencyRatioMap.end())
|
||||
{
|
||||
unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> ratioMap = iter->second;
|
||||
auto iter2 = ratioMap.find(unit2);
|
||||
if (iter2 != ratioMap.end())
|
||||
{
|
||||
double ratio = (iter2->second).ratio;
|
||||
|
||||
// Round the raio to FORMATTER_DIGIT_COUNT digits using int math.
|
||||
// Ex: to round 1.23456 to three digits, use
|
||||
// ((int) 1.23456 * (10^3)) / (10^3)
|
||||
double scale = pow(10, FORMATTER_DIGIT_COUNT);
|
||||
double rounded = static_cast<int>(ratio * static_cast<int>(scale)) / scale;
|
||||
|
||||
wstring digitSymbol = wstring{ LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'1') };
|
||||
wstring roundedFormat = m_ratioFormatter->Format(rounded)->Data();
|
||||
|
||||
wstring ratioString = LocalizationStringUtil::GetLocalizedString(
|
||||
m_ratioFormat.c_str(),
|
||||
digitSymbol.c_str(),
|
||||
unit1.abbreviation.c_str(),
|
||||
roundedFormat.c_str(),
|
||||
unit2.abbreviation.c_str()
|
||||
);
|
||||
|
||||
wstring accessibleRatioString = LocalizationStringUtil::GetLocalizedString(
|
||||
m_ratioFormat.c_str(),
|
||||
digitSymbol.c_str(),
|
||||
unit1.accessibleName.c_str(),
|
||||
roundedFormat.c_str(),
|
||||
unit2.accessibleName.c_str()
|
||||
);
|
||||
|
||||
return make_pair(ratioString, accessibleRatioString);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
|
||||
return make_pair(L"", L"");
|
||||
}
|
||||
|
||||
#pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321
|
||||
task<bool> CurrencyDataLoader::TryLoadDataFromCacheAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ResetLoadStatus();
|
||||
|
||||
auto localSettings = ApplicationData::Current->LocalSettings;
|
||||
if (localSettings == nullptr || !localSettings->Values->HasKey(CacheTimestampKey))
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
bool loadComplete = false;
|
||||
m_cacheTimestamp = static_cast<DateTime>(localSettings->Values->Lookup(CacheTimestampKey));
|
||||
if (Utils::IsDateTimeOlderThan(m_cacheTimestamp, DAY_DURATION)
|
||||
&& m_networkAccessBehavior == NetworkAccessBehavior::Normal)
|
||||
{
|
||||
loadComplete = co_await TryLoadDataFromWebAsync();
|
||||
}
|
||||
|
||||
if (!loadComplete)
|
||||
{
|
||||
loadComplete = co_await TryFinishLoadFromCacheAsync();
|
||||
}
|
||||
|
||||
co_return loadComplete;
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex);
|
||||
co_return false;
|
||||
}
|
||||
catch (const exception& e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e);
|
||||
co_return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
}
|
||||
|
||||
task<bool> CurrencyDataLoader::TryFinishLoadFromCacheAsync()
|
||||
{
|
||||
auto localSettings = ApplicationData::Current->LocalSettings;
|
||||
if (localSettings == nullptr)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
if (!localSettings->Values->HasKey(CacheLangcodeKey)
|
||||
|| !static_cast<String^>(localSettings->Values->Lookup(CacheLangcodeKey))->Equals(m_responseLanguage))
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
StorageFolder^ localCacheFolder = ApplicationData::Current->LocalCacheFolder;
|
||||
if (localCacheFolder == nullptr)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
String^ staticDataResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, StaticDataFilename);
|
||||
String^ allRatiosResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, AllRatiosDataFilename);
|
||||
|
||||
vector<UCM::CurrencyStaticData> staticData{};
|
||||
CurrencyRatioMap ratioMap{};
|
||||
|
||||
bool didParse = TryParseWebResponses(
|
||||
staticDataResponse,
|
||||
allRatiosResponse,
|
||||
staticData,
|
||||
ratioMap);
|
||||
if (!didParse)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
m_loadStatus = CurrencyLoadStatus::LoadedFromCache;
|
||||
co_await FinalizeUnits(staticData, ratioMap);
|
||||
|
||||
co_return true;
|
||||
}
|
||||
|
||||
task<bool> CurrencyDataLoader::TryLoadDataFromWebAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ResetLoadStatus();
|
||||
|
||||
if (m_client == nullptr)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
if (m_networkAccessBehavior == NetworkAccessBehavior::Offline ||
|
||||
(m_networkAccessBehavior == NetworkAccessBehavior::OptIn && !m_meteredOverrideSet))
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
String^ staticDataResponse = co_await m_client->GetCurrencyMetadata();
|
||||
String^ allRatiosResponse = co_await m_client->GetCurrencyRatios();
|
||||
if (staticDataResponse == nullptr || allRatiosResponse == nullptr)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
vector<UCM::CurrencyStaticData> staticData{};
|
||||
CurrencyRatioMap ratioMap{};
|
||||
|
||||
bool didParse = TryParseWebResponses(
|
||||
staticDataResponse,
|
||||
allRatiosResponse,
|
||||
staticData,
|
||||
ratioMap);
|
||||
if (!didParse)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
|
||||
// Set the timestamp before saving it below.
|
||||
m_cacheTimestamp = Utils::GetUniversalSystemTime();
|
||||
|
||||
try
|
||||
{
|
||||
const vector<pair<String^, String^>> cachedFiles = {
|
||||
{ StaticDataFilename, staticDataResponse },
|
||||
{ AllRatiosDataFilename, allRatiosResponse }
|
||||
};
|
||||
|
||||
StorageFolder^ localCacheFolder = ApplicationData::Current->LocalCacheFolder;
|
||||
for (const auto& fileInfo : cachedFiles)
|
||||
{
|
||||
co_await Utils::WriteFileToFolder(
|
||||
localCacheFolder,
|
||||
fileInfo.first,
|
||||
fileInfo.second,
|
||||
CreationCollisionOption::ReplaceExisting);
|
||||
}
|
||||
|
||||
SaveLangCodeAndTimestamp();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we fail to save to cache it's okay, we should still continue.
|
||||
}
|
||||
|
||||
m_loadStatus = CurrencyLoadStatus::LoadedFromWeb;
|
||||
co_await FinalizeUnits(staticData, ratioMap);
|
||||
|
||||
co_return true;
|
||||
}
|
||||
catch (Exception^ ex)
|
||||
{
|
||||
TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex);
|
||||
co_return false;
|
||||
}
|
||||
catch (const exception& e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e);
|
||||
co_return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
co_return false;
|
||||
}
|
||||
}
|
||||
|
||||
task<bool> CurrencyDataLoader::TryLoadDataFromWebOverrideAsync()
|
||||
{
|
||||
m_meteredOverrideSet = true;
|
||||
bool didLoad = co_await TryLoadDataFromWebAsync();
|
||||
if (!didLoad)
|
||||
{
|
||||
m_loadStatus = CurrencyLoadStatus::FailedToLoad;
|
||||
TraceLogger::GetInstance().LogUserRequestedRefreshFailed();
|
||||
}
|
||||
|
||||
co_return didLoad;
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
|
||||
bool CurrencyDataLoader::TryParseWebResponses(
|
||||
_In_ String^ staticDataJson,
|
||||
_In_ String^ allRatiosJson,
|
||||
_Inout_ vector<UCM::CurrencyStaticData>& staticData,
|
||||
_Inout_ CurrencyRatioMap& allRatiosData)
|
||||
{
|
||||
return TryParseStaticData(staticDataJson, staticData)
|
||||
&& TryParseAllRatiosData(allRatiosJson, allRatiosData);
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::TryParseStaticData(_In_ String^ rawJson, _Inout_ vector<UCM::CurrencyStaticData>& staticData)
|
||||
{
|
||||
JsonArray^ data = nullptr;
|
||||
if (!JsonArray::TryParse(rawJson, &data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring countryCode{ L"" };
|
||||
wstring countryName{ L"" };
|
||||
wstring currencyCode{ L"" };
|
||||
wstring currencyName{ L"" };
|
||||
wstring currencySymbol{ L"" };
|
||||
|
||||
vector<wstring*> values = {
|
||||
&countryCode,
|
||||
&countryName,
|
||||
¤cyCode,
|
||||
¤cyName,
|
||||
¤cySymbol
|
||||
};
|
||||
|
||||
assert(values.size() == STATIC_DATA_PROPERTIES.size());
|
||||
staticData.resize(size_t{ data->Size });
|
||||
for (unsigned int i = 0; i < data->Size; i++)
|
||||
{
|
||||
JsonObject^ obj = data->GetAt(i)->GetObject();
|
||||
|
||||
for (size_t j = 0; j < values.size(); j++)
|
||||
{
|
||||
(*values[j]) = obj->GetNamedString(StringReference(STATIC_DATA_PROPERTIES[j].data()))->Data();
|
||||
}
|
||||
|
||||
staticData[i] = CurrencyStaticData{
|
||||
countryCode,
|
||||
countryName,
|
||||
currencyCode,
|
||||
currencyName,
|
||||
currencySymbol
|
||||
};
|
||||
}
|
||||
|
||||
// TODO - MSFT 8533667: this sort will be replaced by a WinRT call to sort localized strings
|
||||
sort(begin(staticData), end(staticData), [](CurrencyStaticData unit1, CurrencyStaticData unit2)
|
||||
{
|
||||
return unit1.countryName < unit2.countryName;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CurrencyDataLoader::TryParseAllRatiosData(_In_ String^ rawJson, _Inout_ CurrencyRatioMap& allRatios)
|
||||
{
|
||||
JsonArray^ data = nullptr;
|
||||
if (!JsonArray::TryParse(rawJson, &data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring sourceCurrencyCode{ DefaultCurrencyCode };
|
||||
|
||||
allRatios.clear();
|
||||
for (unsigned int i = 0; i < data->Size; i++)
|
||||
{
|
||||
JsonObject^ obj = data->GetAt(i)->GetObject();
|
||||
|
||||
// Rt is ratio, An is target currency ISO code.
|
||||
double relativeRatio = obj->GetNamedNumber(StringReference(RATIO_KEY));
|
||||
wstring targetCurrencyCode = obj->GetNamedString(StringReference(CURRENCY_CODE_KEY))->Data();
|
||||
|
||||
allRatios.emplace(targetCurrencyCode, CurrencyRatio{
|
||||
relativeRatio,
|
||||
sourceCurrencyCode,
|
||||
targetCurrencyCode
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FinalizeUnits
|
||||
//
|
||||
// There are a few ways we can get the data needed for Currency Converter, including from cache or from web.
|
||||
// This function accepts the data from any source, and acts as a 'last-steps' for the converter to be ready.
|
||||
// This includes identifying which units will be selected and building the map of curreny ratios.
|
||||
#pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321
|
||||
task<void> CurrencyDataLoader::FinalizeUnits(_In_ const vector<UCM::CurrencyStaticData>& staticData, _In_ const CurrencyRatioMap& ratioMap)
|
||||
{
|
||||
unordered_map<int, pair<UCM::Unit, double>> idToUnit{};
|
||||
|
||||
SelectedUnits defaultCurrencies = co_await GetDefaultFromToCurrency();
|
||||
wstring fromCurrency = defaultCurrencies.first;
|
||||
wstring toCurrency = defaultCurrencies.second;
|
||||
|
||||
{
|
||||
lock_guard<mutex> lock(m_currencyUnitsMutex);
|
||||
|
||||
int i = 1;
|
||||
m_currencyUnits.clear();
|
||||
m_currencyMetadata.clear();
|
||||
bool isConversionSourceSet = false;
|
||||
bool isConversionTargetSet = false;
|
||||
for (const UCM::CurrencyStaticData& currencyUnit : staticData)
|
||||
{
|
||||
auto itr = ratioMap.find(currencyUnit.currencyCode);
|
||||
if (itr != ratioMap.end() && (itr->second).ratio > 0)
|
||||
{
|
||||
int id = static_cast<int>(UnitConverterUnits::UnitEnd + i);
|
||||
|
||||
bool isConversionSource = (fromCurrency == currencyUnit.currencyCode);
|
||||
isConversionSourceSet = isConversionSourceSet || isConversionSource;
|
||||
|
||||
bool isConversionTarget = (toCurrency == currencyUnit.currencyCode);
|
||||
isConversionTargetSet = isConversionTargetSet || isConversionTarget;
|
||||
|
||||
UCM::Unit unit = UCM::Unit{
|
||||
id, // id
|
||||
currencyUnit.currencyName, // currencyName
|
||||
currencyUnit.countryName, // countryName
|
||||
currencyUnit.currencyCode, // abbreviation
|
||||
m_isRtlLanguage, // isRtlLanguage
|
||||
isConversionSource, // isConversionSource
|
||||
isConversionTarget // isConversionTarget
|
||||
};
|
||||
|
||||
m_currencyUnits.push_back(unit);
|
||||
m_currencyMetadata.emplace(unit, CurrencyUnitMetadata{ currencyUnit.currencySymbol });
|
||||
idToUnit.insert(pair<int, pair<UCM::Unit, double>>(unit.id, pair<UCM::Unit, double>(unit, (itr->second).ratio)));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isConversionSourceSet || !isConversionTargetSet)
|
||||
{
|
||||
GuaranteeSelectedUnits();
|
||||
defaultCurrencies = { DEFAULT_FROM_CURRENCY, DEFAULT_TO_CURRENCY };
|
||||
}
|
||||
|
||||
m_currencyRatioMap.clear();
|
||||
for (const auto& unit : m_currencyUnits)
|
||||
{
|
||||
unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> conversions;
|
||||
double unitFactor = idToUnit[unit.id].second;
|
||||
for (auto itr = idToUnit.begin(); itr != idToUnit.end(); itr++)
|
||||
{
|
||||
UCM::Unit targetUnit = (itr->second).first;
|
||||
double conversionRatio = (itr->second).second;
|
||||
UCM::ConversionData parsedData = { 1.0, 0.0, false };
|
||||
assert(unitFactor > 0); // divide by zero assert
|
||||
parsedData.ratio = conversionRatio / unitFactor;
|
||||
conversions.insert(make_pair(targetUnit, parsedData));
|
||||
}
|
||||
|
||||
m_currencyRatioMap.insert(make_pair(unit, conversions));
|
||||
}
|
||||
} // unlocked m_currencyUnitsMutex
|
||||
|
||||
SaveSelectedUnitsToLocalSettings(defaultCurrencies);
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
|
||||
void CurrencyDataLoader::GuaranteeSelectedUnits()
|
||||
{
|
||||
bool isConversionSourceSet = false;
|
||||
bool isConversionTargetSet = false;
|
||||
for (UCM::Unit& unit : m_currencyUnits)
|
||||
{
|
||||
unit.isConversionSource = false;
|
||||
unit.isConversionTarget = false;
|
||||
|
||||
if (!isConversionSourceSet && unit.abbreviation == DEFAULT_FROM_CURRENCY)
|
||||
{
|
||||
unit.isConversionSource = true;
|
||||
isConversionSourceSet = true;
|
||||
}
|
||||
if (!isConversionTargetSet && unit.abbreviation == DEFAULT_TO_CURRENCY)
|
||||
{
|
||||
unit.isConversionTarget = true;
|
||||
isConversionTargetSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::NotifyDataLoadFinished(bool didLoad)
|
||||
{
|
||||
if (!didLoad)
|
||||
{
|
||||
m_loadStatus = CurrencyLoadStatus::FailedToLoad;
|
||||
}
|
||||
|
||||
if (m_vmCallback != nullptr)
|
||||
{
|
||||
m_vmCallback->CurrencyDataLoadFinished(didLoad);
|
||||
}
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::SaveLangCodeAndTimestamp()
|
||||
{
|
||||
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
|
||||
if (localSettings == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
localSettings->Values->Insert(CacheTimestampKey, m_cacheTimestamp);
|
||||
localSettings->Values->Insert(CacheLangcodeKey, m_responseLanguage);
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::UpdateDisplayedTimestamp()
|
||||
{
|
||||
if (m_vmCallback != nullptr)
|
||||
{
|
||||
wstring timestamp = GetCurrencyTimestamp();
|
||||
bool isWeekOld = Utils::IsDateTimeOlderThan(m_cacheTimestamp, WEEK_DURATION);
|
||||
|
||||
m_vmCallback->CurrencyTimestampCallback(timestamp, isWeekOld);
|
||||
}
|
||||
}
|
||||
wstring CurrencyDataLoader::GetCurrencyTimestamp()
|
||||
{
|
||||
wstring timestamp = L"";
|
||||
|
||||
DateTime epoch{};
|
||||
if (m_cacheTimestamp.UniversalTime != epoch.UniversalTime)
|
||||
{
|
||||
DateTimeFormatter^ dateFormatter = ref new DateTimeFormatter(L"{month.abbreviated} {day.integer}, {year.full}");
|
||||
wstring date = dateFormatter->Format(m_cacheTimestamp)->Data();
|
||||
|
||||
DateTimeFormatter^ timeFormatter = ref new DateTimeFormatter(L"shorttime");
|
||||
wstring time = timeFormatter->Format(m_cacheTimestamp)->Data();
|
||||
|
||||
timestamp = LocalizationStringUtil::GetLocalizedString(
|
||||
m_timestampFormat.c_str(),
|
||||
date.c_str(),
|
||||
time.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
#pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321
|
||||
task<SelectedUnits> CurrencyDataLoader::GetDefaultFromToCurrency()
|
||||
{
|
||||
wstring fromCurrency{ DEFAULT_FROM_CURRENCY };
|
||||
wstring toCurrency{ DEFAULT_TO_CURRENCY };
|
||||
|
||||
// First, check if we previously stored the last used currencies.
|
||||
bool foundInLocalSettings = TryGetLastUsedCurrenciesFromLocalSettings(&fromCurrency, &toCurrency);
|
||||
if (!foundInLocalSettings)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Second, see if the current locale has preset defaults in DefaultFromToCurrency.json.
|
||||
Uri^ fileUri = ref new Uri(StringReference(DEFAULT_FROM_TO_CURRENCY_FILE_URI));
|
||||
StorageFile^ defaultFromToCurrencyFile = co_await StorageFile::GetFileFromApplicationUriAsync(fileUri);
|
||||
if (defaultFromToCurrencyFile != nullptr)
|
||||
{
|
||||
String^ fileContents = co_await FileIO::ReadTextAsync(defaultFromToCurrencyFile);
|
||||
JsonObject^ fromToObject = JsonObject::Parse(fileContents);
|
||||
JsonObject^ regionalDefaults = fromToObject->GetNamedObject(m_responseLanguage);
|
||||
|
||||
// Get both values before assignment in-case either fails.
|
||||
String^ selectedFrom = regionalDefaults->GetNamedString(StringReference(FROM_KEY));
|
||||
String^ selectedTo = regionalDefaults->GetNamedString(StringReference(TO_KEY));
|
||||
|
||||
fromCurrency = selectedFrom->Data();
|
||||
toCurrency = selectedTo->Data();
|
||||
}
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
co_return make_pair(fromCurrency, toCurrency);
|
||||
};
|
||||
#pragma optimize("", on)
|
||||
|
||||
bool CurrencyDataLoader::TryGetLastUsedCurrenciesFromLocalSettings(_Out_ wstring* const fromCurrency, _Out_ wstring* const toCurrency)
|
||||
{
|
||||
String^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey;
|
||||
String^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey;
|
||||
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
|
||||
if (localSettings != nullptr && localSettings->Values != nullptr)
|
||||
{
|
||||
IPropertySet^ values = localSettings->Values;
|
||||
if (values->HasKey(fromKey) && values->HasKey(toKey))
|
||||
{
|
||||
*fromCurrency = static_cast<String^>(values->Lookup(fromKey))->Data();
|
||||
*toCurrency = static_cast<String^>(values->Lookup(toKey))->Data();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CurrencyDataLoader::SaveSelectedUnitsToLocalSettings(_In_ const SelectedUnits& selectedUnits)
|
||||
{
|
||||
String^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey;
|
||||
String^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey;
|
||||
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
|
||||
if (localSettings != nullptr && localSettings->Values != nullptr)
|
||||
{
|
||||
IPropertySet^ values = localSettings->Values;
|
||||
values->Insert(fromKey, StringReference(selectedUnits.first.c_str()));
|
||||
values->Insert(toKey, StringReference(selectedUnits.second.c_str()));
|
||||
}
|
||||
}
|
||||
133
src/CalcViewModel/DataLoaders/CurrencyDataLoader.h
Normal file
133
src/CalcViewModel/DataLoaders/CurrencyDataLoader.h
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common\NetworkManager.h"
|
||||
#include "ICurrencyHttpClient.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
public enum class CurrencyLoadStatus
|
||||
{
|
||||
NotLoaded = 0,
|
||||
FailedToLoad = 1,
|
||||
LoadedFromCache = 2,
|
||||
LoadedFromWeb = 3
|
||||
};
|
||||
|
||||
namespace UnitConverterResourceKeys
|
||||
{
|
||||
extern Platform::StringReference CurrencyUnitFromKey;
|
||||
extern Platform::StringReference CurrencyUnitToKey;
|
||||
}
|
||||
|
||||
namespace CurrencyDataLoaderConstants
|
||||
{
|
||||
extern Platform::StringReference CacheTimestampKey;
|
||||
extern Platform::StringReference CacheLangcodeKey;
|
||||
extern Platform::StringReference CacheDelimiter;
|
||||
extern Platform::StringReference StaticDataFilename;
|
||||
extern Platform::StringReference AllRatiosDataFilename;
|
||||
extern long long DayDuration;
|
||||
}
|
||||
|
||||
namespace UCM = UnitConversionManager;
|
||||
|
||||
typedef std::unordered_map<std::wstring, UCM::CurrencyRatio> CurrencyRatioMap;
|
||||
typedef std::pair<std::wstring, std::wstring> SelectedUnits;
|
||||
|
||||
struct CurrencyUnitMetadata
|
||||
{
|
||||
CurrencyUnitMetadata(const std::wstring& s) : symbol(s) {}
|
||||
|
||||
const std::wstring symbol;
|
||||
};
|
||||
|
||||
class CurrencyDataLoader : public UCM::IConverterDataLoader,
|
||||
public UCM::ICurrencyConverterDataLoader
|
||||
{
|
||||
public:
|
||||
CurrencyDataLoader(_In_ std::unique_ptr<CalculatorApp::DataLoaders::ICurrencyHttpClient> client);
|
||||
~CurrencyDataLoader();
|
||||
|
||||
bool LoadFinished();
|
||||
bool LoadedFromCache();
|
||||
bool LoadedFromWeb();
|
||||
|
||||
// IConverterDataLoader
|
||||
void LoadData() override;
|
||||
std::vector<UCM::Category> LoadOrderedCategories() override;
|
||||
std::vector<UCM::Unit> LoadOrderedUnits(const UCM::Category& category) override;
|
||||
std::unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> LoadOrderedRatios(const UCM::Unit& unit) override;
|
||||
bool SupportsCategory(const UnitConversionManager::Category& target) override;
|
||||
// IConverterDataLoader
|
||||
|
||||
// ICurrencyConverterDataLoader
|
||||
void SetViewModelCallback(const std::shared_ptr<UCM::IViewModelCurrencyCallback>& callback) override;
|
||||
std::pair<std::wstring, std::wstring> GetCurrencySymbols(const UCM::Unit& unit1, const UCM::Unit& unit2) override;
|
||||
std::pair<std::wstring, std::wstring> GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) override;
|
||||
std::wstring GetCurrencyTimestamp() override;
|
||||
|
||||
concurrency::task<bool> TryLoadDataFromCacheAsync() override;
|
||||
concurrency::task<bool> TryLoadDataFromWebAsync() override;
|
||||
concurrency::task<bool> TryLoadDataFromWebOverrideAsync() override;
|
||||
// ICurrencyConverterDataLoader
|
||||
|
||||
void OnNetworkBehaviorChanged(CalculatorApp::NetworkAccessBehavior newBehavior);
|
||||
|
||||
private:
|
||||
void ResetLoadStatus();
|
||||
void NotifyDataLoadFinished(bool didLoad);
|
||||
|
||||
concurrency::task<bool> TryFinishLoadFromCacheAsync();
|
||||
|
||||
bool TryParseWebResponses(
|
||||
_In_ Platform::String^ staticDataJson,
|
||||
_In_ Platform::String^ allRatiosJson,
|
||||
_Inout_ std::vector<UCM::CurrencyStaticData>& staticData,
|
||||
_Inout_ CurrencyRatioMap& allRatiosData);
|
||||
bool TryParseStaticData(_In_ Platform::String^ rawJson, _Inout_ std::vector<UCM::CurrencyStaticData>& staticData);
|
||||
bool TryParseAllRatiosData(_In_ Platform::String^ rawJson, _Inout_ CurrencyRatioMap& allRatiosData);
|
||||
concurrency::task<void> FinalizeUnits(_In_ const std::vector<UCM::CurrencyStaticData>& staticData, _In_ const CurrencyRatioMap& ratioMap);
|
||||
void GuaranteeSelectedUnits();
|
||||
|
||||
void SaveLangCodeAndTimestamp();
|
||||
void UpdateDisplayedTimestamp();
|
||||
|
||||
void RegisterForNetworkBehaviorChanges();
|
||||
void UnregisterForNetworkBehaviorChanges();
|
||||
|
||||
concurrency::task<SelectedUnits> GetDefaultFromToCurrency();
|
||||
bool TryGetLastUsedCurrenciesFromLocalSettings(_Out_ std::wstring* const fromCurrency, _Out_ std::wstring* const toCurrency);
|
||||
void SaveSelectedUnitsToLocalSettings(_In_ const SelectedUnits& selectedUnits);
|
||||
|
||||
private:
|
||||
Platform::String^ m_responseLanguage;
|
||||
std::unique_ptr<CalculatorApp::DataLoaders::ICurrencyHttpClient> m_client;
|
||||
|
||||
bool m_isRtlLanguage;
|
||||
|
||||
std::mutex m_currencyUnitsMutex;
|
||||
std::vector<UCM::Unit> m_currencyUnits;
|
||||
UCM::UnitToUnitToConversionDataMap m_currencyRatioMap;
|
||||
std::unordered_map<UCM::Unit, CurrencyUnitMetadata, UCM::UnitHash> m_currencyMetadata;
|
||||
|
||||
std::shared_ptr<UCM::IViewModelCurrencyCallback> m_vmCallback;
|
||||
|
||||
Windows::Globalization::NumberFormatting::DecimalFormatter^ m_ratioFormatter;
|
||||
std::wstring m_ratioFormat;
|
||||
Windows::Foundation::DateTime m_cacheTimestamp;
|
||||
std::wstring m_timestampFormat;
|
||||
|
||||
CurrencyLoadStatus m_loadStatus;
|
||||
|
||||
CalculatorApp::NetworkManager^ m_networkManager;
|
||||
CalculatorApp::NetworkAccessBehavior m_networkAccessBehavior;
|
||||
Windows::Foundation::EventRegistrationToken m_networkBehaviorToken;
|
||||
bool m_meteredOverrideSet;
|
||||
};
|
||||
}
|
||||
}
|
||||
46
src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp
Normal file
46
src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "CurrencyHttpClient.h"
|
||||
|
||||
using namespace CalculatorApp::DataLoaders;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Web::Http;
|
||||
|
||||
static constexpr auto sc_MetadataUriLocalizeFor = L"https://go.microsoft.com/fwlink/?linkid=2041093&localizeFor=";
|
||||
static constexpr auto sc_RatiosUriRelativeTo = L"https://go.microsoft.com/fwlink/?linkid=2041339&localCurrency=";
|
||||
|
||||
CurrencyHttpClient::CurrencyHttpClient() :
|
||||
m_client(ref new HttpClient()),
|
||||
m_responseLanguage(L"en-US")
|
||||
{
|
||||
}
|
||||
|
||||
void CurrencyHttpClient::SetSourceCurrencyCode(String^ sourceCurrencyCode)
|
||||
{
|
||||
m_sourceCurrencyCode = sourceCurrencyCode;
|
||||
}
|
||||
|
||||
void CurrencyHttpClient::SetResponseLanguage(String^ responseLanguage)
|
||||
{
|
||||
m_responseLanguage = responseLanguage;
|
||||
}
|
||||
|
||||
IAsyncOperationWithProgress<String^, HttpProgress>^ CurrencyHttpClient::GetCurrencyMetadata()
|
||||
{
|
||||
wstring uri = wstring{ sc_MetadataUriLocalizeFor } + m_responseLanguage->Data();
|
||||
auto metadataUri = ref new Uri(StringReference(uri.c_str()));
|
||||
|
||||
return m_client->GetStringAsync(metadataUri);
|
||||
}
|
||||
|
||||
IAsyncOperationWithProgress<String^, HttpProgress>^ CurrencyHttpClient::GetCurrencyRatios()
|
||||
{
|
||||
wstring uri = wstring{ sc_RatiosUriRelativeTo } + m_sourceCurrencyCode->Data();
|
||||
auto ratiosUri = ref new Uri(StringReference(uri.c_str()));
|
||||
|
||||
return m_client->GetStringAsync(ratiosUri);
|
||||
}
|
||||
29
src/CalcViewModel/DataLoaders/CurrencyHttpClient.h
Normal file
29
src/CalcViewModel/DataLoaders/CurrencyHttpClient.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ICurrencyHttpClient.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace DataLoaders
|
||||
{
|
||||
class CurrencyHttpClient : public ICurrencyHttpClient
|
||||
{
|
||||
public:
|
||||
CurrencyHttpClient();
|
||||
|
||||
void SetSourceCurrencyCode(Platform::String^ sourceCurrencyCode) override;
|
||||
void SetResponseLanguage(Platform::String^ responseLanguage) override;
|
||||
|
||||
Windows::Foundation::IAsyncOperationWithProgress<Platform::String^, Windows::Web::Http::HttpProgress>^ GetCurrencyMetadata() override;
|
||||
Windows::Foundation::IAsyncOperationWithProgress<Platform::String^, Windows::Web::Http::HttpProgress>^ GetCurrencyRatios() override;
|
||||
|
||||
private:
|
||||
Windows::Web::Http::HttpClient^ m_client;
|
||||
Platform::String^ m_responseLanguage;
|
||||
Platform::String^ m_sourceCurrencyCode;
|
||||
};
|
||||
}
|
||||
}
|
||||
134
src/CalcViewModel/DataLoaders/DefaultFromToCurrency.json
Normal file
134
src/CalcViewModel/DataLoaders/DefaultFromToCurrency.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"default": {
|
||||
"from": "USD",
|
||||
"to": "EUR"
|
||||
},
|
||||
"ar-AE": {
|
||||
"from": "USD",
|
||||
"to": "AED"
|
||||
},
|
||||
"ar-EG": {
|
||||
"from": "USD",
|
||||
"to": "EGP"
|
||||
},
|
||||
"ar-SA": {
|
||||
"from": "USD",
|
||||
"to": "SAR"
|
||||
},
|
||||
"da-DK": {
|
||||
"from": "DKK",
|
||||
"to": "USD"
|
||||
},
|
||||
"de-CH": {
|
||||
"from": "EUR",
|
||||
"to": "CHF"
|
||||
},
|
||||
"de-DE": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"en-AU": {
|
||||
"from": "AUD",
|
||||
"to": "USD"
|
||||
},
|
||||
"en-CA": {
|
||||
"from": "CAD",
|
||||
"to": "USD"
|
||||
},
|
||||
"en-ES": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"en-GB": {
|
||||
"from": "GBP",
|
||||
"to": "USD"
|
||||
},
|
||||
"en-IN": {
|
||||
"from": "USD",
|
||||
"to": "INR"
|
||||
},
|
||||
"en-US": {
|
||||
"from": "USD",
|
||||
"to": "EUR"
|
||||
},
|
||||
"es-AR": {
|
||||
"from": "USD",
|
||||
"to": "ARS"
|
||||
},
|
||||
"es-CL": {
|
||||
"from": "USD",
|
||||
"to": "CLP"
|
||||
},
|
||||
"es-CO": {
|
||||
"from": "USD",
|
||||
"to": "COP"
|
||||
},
|
||||
"es-ES": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"es-MX": {
|
||||
"from": "USD",
|
||||
"to": "MXN"
|
||||
},
|
||||
"es-PE": {
|
||||
"from": "USD",
|
||||
"to": "PEN"
|
||||
},
|
||||
"es-VE": {
|
||||
"from": "USD",
|
||||
"to": "VEF"
|
||||
},
|
||||
"es-XL": {
|
||||
"from": "USD",
|
||||
"to": "EUR"
|
||||
},
|
||||
"es-US": {
|
||||
"from": "USD",
|
||||
"to": "EUR"
|
||||
},
|
||||
"fr-CH": {
|
||||
"from": "EUR",
|
||||
"to": "CHF"
|
||||
},
|
||||
"fr-FR": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"it-IT": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"it-SM": {
|
||||
"from": "EUR",
|
||||
"to": "USD"
|
||||
},
|
||||
"ja-JP": {
|
||||
"from": "USD",
|
||||
"to": "JPY"
|
||||
},
|
||||
"nb-NO": {
|
||||
"from": "NOK",
|
||||
"to": "USD"
|
||||
},
|
||||
"pt-BR": {
|
||||
"from": "USD",
|
||||
"to": "BRL"
|
||||
},
|
||||
"sv-SE": {
|
||||
"from": "SEK",
|
||||
"to": "USD"
|
||||
},
|
||||
"th-TH": {
|
||||
"from": "USD",
|
||||
"to": "THB"
|
||||
},
|
||||
"zh-CN": {
|
||||
"from": "USD",
|
||||
"to": "CNY"
|
||||
},
|
||||
"zh-HK": {
|
||||
"from": "USD",
|
||||
"to": "HKD"
|
||||
}
|
||||
}
|
||||
22
src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h
Normal file
22
src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace DataLoaders
|
||||
{
|
||||
class ICurrencyHttpClient
|
||||
{
|
||||
public:
|
||||
virtual ~ICurrencyHttpClient() {}
|
||||
|
||||
virtual void SetSourceCurrencyCode(Platform::String^ sourceCurrencyCode) = 0;
|
||||
virtual void SetResponseLanguage(Platform::String^ responseLanguage) = 0;
|
||||
|
||||
virtual Windows::Foundation::IAsyncOperationWithProgress<Platform::String^, Windows::Web::Http::HttpProgress>^ GetCurrencyMetadata() = 0;
|
||||
virtual Windows::Foundation::IAsyncOperationWithProgress<Platform::String^, Windows::Web::Http::HttpProgress>^ GetCurrencyRatios() = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
168
src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h
Normal file
168
src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
private enum UnitConverterUnits
|
||||
{
|
||||
UnitStart = 0,
|
||||
Area_Acre = UnitStart + 1,
|
||||
Area_Hectare = UnitStart + 2,
|
||||
Area_SquareCentimeter = UnitStart + 3,
|
||||
Area_SquareFoot = UnitStart + 4,
|
||||
Area_SquareInch = UnitStart + 5,
|
||||
Area_SquareKilometer = UnitStart + 6,
|
||||
Area_SquareMeter = UnitStart + 7,
|
||||
Area_SquareMile = UnitStart + 8,
|
||||
Area_SquareMillimeter = UnitStart + 9,
|
||||
Area_SquareYard = UnitStart + 10,
|
||||
Data_Bit = UnitStart + 11,
|
||||
Data_Byte = UnitStart + 12,
|
||||
Data_Gigabit = UnitStart + 13,
|
||||
Data_Gigabyte = UnitStart + 14,
|
||||
Data_Kilobit = UnitStart + 15,
|
||||
Data_Kilobyte = UnitStart + 16,
|
||||
Data_Megabit = UnitStart + 17,
|
||||
Data_Megabyte = UnitStart + 18,
|
||||
Data_Petabit = UnitStart + 19,
|
||||
Data_Petabyte = UnitStart + 20,
|
||||
Data_Terabit = UnitStart + 21,
|
||||
Data_Terabyte = UnitStart + 22,
|
||||
Energy_BritishThermalUnit = UnitStart + 23,
|
||||
Energy_Calorie = UnitStart + 24,
|
||||
Energy_ElectronVolt = UnitStart + 25,
|
||||
Energy_FootPound = UnitStart + 26,
|
||||
Energy_Joule = UnitStart + 27,
|
||||
Energy_Kilocalorie = UnitStart + 28,
|
||||
Energy_Kilojoule = UnitStart + 29,
|
||||
Length_Centimeter = UnitStart + 30,
|
||||
Length_Foot = UnitStart + 31,
|
||||
Length_Inch = UnitStart + 32,
|
||||
Length_Kilometer = UnitStart + 33,
|
||||
Length_Meter = UnitStart + 34,
|
||||
Length_Micron = UnitStart + 35,
|
||||
Length_Mile = UnitStart + 36,
|
||||
Length_Millimeter = UnitStart + 37,
|
||||
Length_Nanometer = UnitStart + 38,
|
||||
Length_NauticalMile = UnitStart + 39,
|
||||
Length_Yard = UnitStart + 40,
|
||||
Power_BritishThermalUnitPerMinute = UnitStart + 41,
|
||||
Power_FootPoundPerMinute = UnitStart + 42,
|
||||
Power_Horsepower = UnitStart + 43,
|
||||
Power_Kilowatt = UnitStart + 44,
|
||||
Power_Watt = UnitStart + 45,
|
||||
Temperature_DegreesCelsius = UnitStart + 46,
|
||||
Temperature_DegreesFahrenheit = UnitStart + 47,
|
||||
Temperature_Kelvin = UnitStart + 48,
|
||||
Time_Day = UnitStart + 49,
|
||||
Time_Hour = UnitStart + 50,
|
||||
Time_Microsecond = UnitStart + 51,
|
||||
Time_Millisecond = UnitStart + 52,
|
||||
Time_Minute = UnitStart + 53,
|
||||
Time_Second = UnitStart + 54,
|
||||
Time_Week = UnitStart + 55,
|
||||
Time_Year = UnitStart + 56,
|
||||
Speed_CentimetersPerSecond = UnitStart + 57,
|
||||
Speed_FeetPerSecond = UnitStart + 58,
|
||||
Speed_KilometersPerHour = UnitStart + 59,
|
||||
Speed_Knot = UnitStart + 60,
|
||||
Speed_Mach = UnitStart + 61,
|
||||
Speed_MetersPerSecond = UnitStart + 62,
|
||||
Speed_MilesPerHour = UnitStart + 63,
|
||||
Volume_CubicCentimeter = UnitStart + 64,
|
||||
Volume_CubicFoot = UnitStart + 65,
|
||||
Volume_CubicInch = UnitStart + 66,
|
||||
Volume_CubicMeter = UnitStart + 67,
|
||||
Volume_CubicYard = UnitStart + 68,
|
||||
Volume_CupUS = UnitStart + 69,
|
||||
Volume_FluidOunceUK = UnitStart + 70,
|
||||
Volume_FluidOunceUS = UnitStart + 71,
|
||||
Volume_GallonUK = UnitStart + 72,
|
||||
Volume_GallonUS = UnitStart + 73,
|
||||
Volume_Liter = UnitStart + 74,
|
||||
Volume_Milliliter = UnitStart + 75,
|
||||
Volume_PintUK = UnitStart + 76,
|
||||
Volume_PintUS = UnitStart + 77,
|
||||
Volume_TablespoonUS = UnitStart + 78,
|
||||
Volume_TeaspoonUS = UnitStart + 79,
|
||||
Volume_QuartUK = UnitStart + 80,
|
||||
Volume_QuartUS = UnitStart + 81,
|
||||
Weight_Carat = UnitStart + 82,
|
||||
Weight_Centigram = UnitStart + 83,
|
||||
Weight_Decigram = UnitStart + 84,
|
||||
Weight_Decagram = UnitStart + 85,
|
||||
Weight_Gram = UnitStart + 86,
|
||||
Weight_Hectogram = UnitStart + 87,
|
||||
Weight_Kilogram = UnitStart + 88,
|
||||
Weight_LongTon = UnitStart + 89,
|
||||
Weight_Milligram = UnitStart + 90,
|
||||
Weight_Ounce = UnitStart + 91,
|
||||
Weight_Pound = UnitStart + 92,
|
||||
Weight_ShortTon = UnitStart + 93,
|
||||
Weight_Stone = UnitStart + 94,
|
||||
Weight_Tonne = UnitStart + 95,
|
||||
Area_SoccerField = UnitStart + 99,
|
||||
Data_FloppyDisk = UnitStart + 100,
|
||||
Data_CD = UnitStart + 101,
|
||||
Data_DVD = UnitStart + 102,
|
||||
Energy_Battery = UnitStart + 103,
|
||||
Length_Paperclip = UnitStart + 105,
|
||||
Length_JumboJet = UnitStart + 107,
|
||||
Power_LightBulb = UnitStart + 108,
|
||||
Power_Horse = UnitStart + 109,
|
||||
Volume_Bathtub = UnitStart + 111,
|
||||
Weight_Snowflake = UnitStart + 113,
|
||||
Weight_Elephant = UnitStart + 114,
|
||||
Volume_TeaspoonUK = UnitStart + 115,
|
||||
Volume_TablespoonUK = UnitStart + 116,
|
||||
Area_Hand = UnitStart + 118,
|
||||
Speed_Turtle = UnitStart + 121,
|
||||
Speed_Jet = UnitStart + 122,
|
||||
Volume_CoffeeCup = UnitStart + 124,
|
||||
Weight_Whale = UnitStart + 123,
|
||||
Volume_SwimmingPool = UnitStart + 125,
|
||||
Speed_Horse = UnitStart + 126,
|
||||
Area_Paper = UnitStart + 127,
|
||||
Area_Castle = UnitStart + 128,
|
||||
Energy_Banana = UnitStart + 129,
|
||||
Energy_SliceOfCake = UnitStart + 130,
|
||||
Length_Hand = UnitStart + 131,
|
||||
Power_TrainEngine = UnitStart + 132,
|
||||
Weight_SoccerBall = UnitStart + 133,
|
||||
Angle_Degree = UnitStart + 134,
|
||||
Angle_Radian = UnitStart + 135,
|
||||
Angle_Gradian = UnitStart + 136,
|
||||
Pressure_Atmosphere = UnitStart + 137,
|
||||
Pressure_Bar = UnitStart + 138,
|
||||
Pressure_KiloPascal = UnitStart + 139,
|
||||
Pressure_MillimeterOfMercury = UnitStart + 140,
|
||||
Pressure_Pascal = UnitStart + 141,
|
||||
Pressure_PSI = UnitStart + 142,
|
||||
Data_Exabits = UnitStart + 143,
|
||||
Data_Exabytes = UnitStart + 144,
|
||||
Data_Exbibits = UnitStart + 145,
|
||||
Data_Exbibytes = UnitStart + 146,
|
||||
Data_Gibibits = UnitStart + 147,
|
||||
Data_Gibibytes = UnitStart + 148,
|
||||
Data_Kibibits = UnitStart + 149,
|
||||
Data_Kibibytes = UnitStart + 150,
|
||||
Data_Mebibits = UnitStart + 151,
|
||||
Data_Mebibytes = UnitStart + 152,
|
||||
Data_Pebibits = UnitStart + 153,
|
||||
Data_Pebibytes = UnitStart + 154,
|
||||
Data_Tebibits = UnitStart + 155,
|
||||
Data_Tebibytes = UnitStart + 156,
|
||||
Data_Yobibits = UnitStart + 157,
|
||||
Data_Yobibytes = UnitStart + 158,
|
||||
Data_Yottabit = UnitStart + 159,
|
||||
Data_Yottabyte = UnitStart + 160,
|
||||
Data_Zebibits = UnitStart + 161,
|
||||
Data_Zebibytes = UnitStart + 162,
|
||||
Data_Zetabits = UnitStart + 163,
|
||||
Data_Zetabytes = UnitStart + 164,
|
||||
UnitEnd = Data_Zetabytes
|
||||
};
|
||||
}
|
||||
}
|
||||
598
src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp
Normal file
598
src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp
Normal file
@@ -0,0 +1,598 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Common\AppResourceProvider.h"
|
||||
#include "UnitConverterDataLoader.h"
|
||||
#include "UnitConverterDataConstants.h"
|
||||
#include "CurrencyDataLoader.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::DataLoaders;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::ApplicationModel::Resources::Core;
|
||||
using namespace Windows::Globalization;
|
||||
|
||||
static constexpr bool CONVERT_WITH_OFFSET_FIRST = true;
|
||||
|
||||
UnitConverterDataLoader::UnitConverterDataLoader(GeographicRegion^ region) :
|
||||
m_currentRegionCode(region->CodeTwoLetter)
|
||||
{
|
||||
m_categoryList = make_shared<vector<UCM::Category>>();
|
||||
m_categoryToUnits = make_shared<UCM::CategoryToUnitVectorMap>();
|
||||
m_ratioMap = make_shared<UCM::UnitToUnitToConversionDataMap>();
|
||||
}
|
||||
|
||||
vector<UCM::Category> UnitConverterDataLoader::LoadOrderedCategories()
|
||||
{
|
||||
return *m_categoryList;
|
||||
}
|
||||
|
||||
vector<UCM::Unit> UnitConverterDataLoader::LoadOrderedUnits(const UCM::Category& category)
|
||||
{
|
||||
return m_categoryToUnits->at(category);
|
||||
}
|
||||
|
||||
unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> UnitConverterDataLoader::LoadOrderedRatios(const UCM::Unit& unit)
|
||||
{
|
||||
return m_ratioMap->at(unit);
|
||||
}
|
||||
|
||||
bool UnitConverterDataLoader::SupportsCategory(const UCM::Category& target)
|
||||
{
|
||||
shared_ptr<vector<UCM::Category>> supportedCategories = nullptr;
|
||||
if (!m_categoryList->empty())
|
||||
{
|
||||
supportedCategories = m_categoryList;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetCategories(supportedCategories);
|
||||
}
|
||||
|
||||
static int currencyId = NavCategory::Serialize(ViewMode::Currency);
|
||||
auto itr = find_if(supportedCategories->begin(), supportedCategories->end(),
|
||||
[&](const UCM::Category& category)
|
||||
{
|
||||
return currencyId != category.id && target.id == category.id;
|
||||
});
|
||||
|
||||
return itr != supportedCategories->end();
|
||||
}
|
||||
|
||||
void UnitConverterDataLoader::LoadData()
|
||||
{
|
||||
unordered_map<int, OrderedUnit> idToUnit;
|
||||
|
||||
unordered_map<ViewMode, vector<OrderedUnit>> orderedUnitMap{};
|
||||
unordered_map<ViewMode, unordered_map<int, double>> categoryToUnitConversionDataMap{};
|
||||
unordered_map<int, unordered_map<int, UCM::ConversionData>> explicitConversionData{};
|
||||
|
||||
// Load categories, units and conversion data into data structures. This will be then used to populate hashmaps used by CalcEngine and UI layer
|
||||
GetCategories(m_categoryList);
|
||||
GetUnits(orderedUnitMap);
|
||||
GetConversionData(categoryToUnitConversionDataMap);
|
||||
GetExplicitConversionData(explicitConversionData); // This is needed for temperature conversions
|
||||
|
||||
m_categoryToUnits->clear();
|
||||
m_ratioMap->clear();
|
||||
for (UCM::Category objectCategory : *m_categoryList)
|
||||
{
|
||||
ViewMode categoryViewMode = NavCategory::Deserialize(objectCategory.id);
|
||||
assert(NavCategory::IsConverterViewMode(categoryViewMode));
|
||||
if (categoryViewMode == ViewMode::Currency)
|
||||
{
|
||||
// Currency is an ordered category but we do not want to process it here
|
||||
// because this function is not thread-safe and currency data is asynchronously
|
||||
// loaded.
|
||||
m_categoryToUnits->insert(pair<UCM::Category, std::vector<UCM::Unit>>(objectCategory, {}));
|
||||
continue;
|
||||
}
|
||||
|
||||
vector<OrderedUnit> orderedUnits = orderedUnitMap[categoryViewMode];
|
||||
vector<UCM::Unit> unitList;
|
||||
|
||||
// Sort the units by order
|
||||
sort(orderedUnits.begin(), orderedUnits.end(), [](const OrderedUnit& first, const OrderedUnit& second){ return first.order < second.order; });
|
||||
|
||||
for (OrderedUnit u : orderedUnits)
|
||||
{
|
||||
unitList.push_back(static_cast<UCM::Unit>(u));
|
||||
idToUnit.insert(pair<int, OrderedUnit>(u.id, u));
|
||||
}
|
||||
|
||||
// Save units per category
|
||||
m_categoryToUnits->insert(pair<UCM::Category, std::vector<UCM::Unit>>(objectCategory, unitList));
|
||||
|
||||
// For each unit, populate the conversion data
|
||||
for (UCM::Unit unit : unitList)
|
||||
{
|
||||
unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash> conversions;
|
||||
|
||||
if (explicitConversionData.find(unit.id) == explicitConversionData.end())
|
||||
{
|
||||
// Get the associated units for a category id
|
||||
unordered_map<int, double> unitConversions = categoryToUnitConversionDataMap.at(categoryViewMode);
|
||||
double unitFactor = unitConversions[unit.id];
|
||||
|
||||
for (auto itr = unitConversions.begin(); itr != unitConversions.end(); ++itr)
|
||||
{
|
||||
UCM::ConversionData parsedData = { 1.0, 0.0, false };
|
||||
assert(itr->second > 0); // divide by zero assert
|
||||
parsedData.ratio = unitFactor / itr->second;
|
||||
conversions.insert(pair<UCM::Unit, UCM::ConversionData>(idToUnit.at(itr->first), parsedData));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unordered_map<int, UCM::ConversionData> unitConversions = explicitConversionData.at(unit.id);
|
||||
for (auto itr = unitConversions.begin(); itr != unitConversions.end(); ++itr)
|
||||
{
|
||||
conversions.insert(pair<UCM::Unit, UCM::ConversionData>(idToUnit.at(itr->first), itr->second));
|
||||
}
|
||||
}
|
||||
|
||||
m_ratioMap->insert(pair<UCM::Unit, unordered_map<UCM::Unit, UCM::ConversionData, UCM::UnitHash>>(unit, conversions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnitConverterDataLoader::GetCategories(_In_ shared_ptr<vector<UCM::Category>> categoriesList)
|
||||
{
|
||||
categoriesList->clear();
|
||||
auto converterCategory = NavCategoryGroup::CreateConverterCategory();
|
||||
for (auto const& category : converterCategory->Categories)
|
||||
{
|
||||
/* Id, CategoryName, SupportsNegative */
|
||||
categoriesList->emplace_back(
|
||||
NavCategory::Serialize(category->Mode),
|
||||
category->Name->Data(),
|
||||
category->SupportsNegative);
|
||||
}
|
||||
}
|
||||
|
||||
void UnitConverterDataLoader::GetUnits(_In_ unordered_map<ViewMode, vector<OrderedUnit>>& unitMap)
|
||||
{
|
||||
bool USSource, USTarget;
|
||||
bool UKSource, UKTarget;
|
||||
bool Source, Target;
|
||||
|
||||
USSource = (GetRegion() == L"US") ? true : false;
|
||||
USTarget = USSource;
|
||||
|
||||
UKSource = (GetRegion() == L"UK") ? true : false;
|
||||
UKTarget = UKSource;
|
||||
|
||||
Source = (GetRegion() == L"Others") ? true : false;
|
||||
Target = Source;
|
||||
|
||||
vector<OrderedUnit> areaUnits;
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Acre, GetLocalizedStringName(L"UnitName_Acre"), GetLocalizedStringName(L"UnitAbbreviation_Acre"), 9 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Hectare, GetLocalizedStringName(L"UnitName_Hectare"), GetLocalizedStringName(L"UnitAbbreviation_Hectare"), 4 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareCentimeter, GetLocalizedStringName(L"UnitName_SquareCentimeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareCentimeter"), 2 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareFoot, GetLocalizedStringName(L"UnitName_SquareFoot"), GetLocalizedStringName(L"UnitAbbreviation_SquareFoot"), 7, (UKSource || Source), USTarget, false });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareInch, GetLocalizedStringName(L"UnitName_SquareInch"), GetLocalizedStringName(L"UnitAbbreviation_SquareInch"), 6 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareKilometer, GetLocalizedStringName(L"UnitName_SquareKilometer"), GetLocalizedStringName(L"UnitAbbreviation_SquareKilometer"), 5 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMeter, GetLocalizedStringName(L"UnitName_SquareMeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareMeter"), 3, USSource, (UKTarget || Target), false});
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMile, GetLocalizedStringName(L"UnitName_SquareMile"), GetLocalizedStringName(L"UnitAbbreviation_SquareMile"), 10 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMillimeter, GetLocalizedStringName(L"UnitName_SquareMillimeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareMillimeter"), 1 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareYard, GetLocalizedStringName(L"UnitName_SquareYard"), GetLocalizedStringName(L"UnitAbbreviation_SquareYard"), 8 });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 11, false, false, true});
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Paper, GetLocalizedStringName(L"UnitName_Paper"), GetLocalizedStringName(L"UnitAbbreviation_Paper"), 12, false, false, true });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SoccerField, GetLocalizedStringName(L"UnitName_SoccerField"), GetLocalizedStringName(L"UnitAbbreviation_SoccerField"),13, false, false, true });
|
||||
areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Castle, GetLocalizedStringName(L"UnitName_Castle"), GetLocalizedStringName(L"UnitAbbreviation_Castle"), 14, false, false, true });
|
||||
unitMap.emplace(ViewMode::Area, areaUnits);
|
||||
|
||||
vector<OrderedUnit> dataUnits;
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Bit, GetLocalizedStringName(L"UnitName_Bit"), GetLocalizedStringName(L"UnitAbbreviation_Bit"), 1 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Byte, GetLocalizedStringName(L"UnitName_Byte"), GetLocalizedStringName(L"UnitAbbreviation_Byte"), 2 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exabits, GetLocalizedStringName(L"UnitName_Exabits"), GetLocalizedStringName(L"UnitAbbreviation_Exabits"), 23 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exabytes, GetLocalizedStringName(L"UnitName_Exabytes"), GetLocalizedStringName(L"UnitAbbreviation_Exabytes"), 25 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exbibits, GetLocalizedStringName(L"UnitName_Exbibits"), GetLocalizedStringName(L"UnitAbbreviation_Exbibits"), 24 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exbibytes, GetLocalizedStringName(L"UnitName_Exbibytes"), GetLocalizedStringName(L"UnitAbbreviation_Exbibytes"), 26 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gibibits, GetLocalizedStringName(L"UnitName_Gibibits"), GetLocalizedStringName(L"UnitAbbreviation_Gibibits"), 12 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gibibytes, GetLocalizedStringName(L"UnitName_Gibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Gibibytes"), 14 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gigabit, GetLocalizedStringName(L"UnitName_Gigabit"), GetLocalizedStringName(L"UnitAbbreviation_Gigabit"), 11 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gigabyte, GetLocalizedStringName(L"UnitName_Gigabyte"), GetLocalizedStringName(L"UnitAbbreviation_Gigabyte"),13, true, false, false});
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kibibits, GetLocalizedStringName(L"UnitName_Kibibits"), GetLocalizedStringName(L"UnitAbbreviation_Kibibits"), 4 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kibibytes, GetLocalizedStringName(L"UnitName_Kibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Kibibytes"),6 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kilobit, GetLocalizedStringName(L"UnitName_Kilobit"), GetLocalizedStringName(L"UnitAbbreviation_Kilobit"), 3 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kilobyte, GetLocalizedStringName(L"UnitName_Kilobyte"), GetLocalizedStringName(L"UnitAbbreviation_Kilobyte"), 5 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Mebibits, GetLocalizedStringName(L"UnitName_Mebibits"), GetLocalizedStringName(L"UnitAbbreviation_Mebibits"), 8 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Mebibytes, GetLocalizedStringName(L"UnitName_Mebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Mebibytes"), 10 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Megabit, GetLocalizedStringName(L"UnitName_Megabit"), GetLocalizedStringName(L"UnitAbbreviation_Megabit"), 7 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Megabyte, GetLocalizedStringName(L"UnitName_Megabyte"), GetLocalizedStringName(L"UnitAbbreviation_Megabyte"), 9, false, true, false});
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Pebibits, GetLocalizedStringName(L"UnitName_Pebibits"), GetLocalizedStringName(L"UnitAbbreviation_Pebibits"), 20 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Pebibytes, GetLocalizedStringName(L"UnitName_Pebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Pebibytes"), 22 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Petabit, GetLocalizedStringName(L"UnitName_Petabit"), GetLocalizedStringName(L"UnitAbbreviation_Petabit"), 19 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Petabyte, GetLocalizedStringName(L"UnitName_Petabyte"), GetLocalizedStringName(L"UnitAbbreviation_Petabyte"), 21 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Tebibits, GetLocalizedStringName(L"UnitName_Tebibits"), GetLocalizedStringName(L"UnitAbbreviation_Tebibits"), 16 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Tebibytes, GetLocalizedStringName(L"UnitName_Tebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Tebibytes"), 18 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Terabit, GetLocalizedStringName(L"UnitName_Terabit"), GetLocalizedStringName(L"UnitAbbreviation_Terabit"), 15 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Terabyte, GetLocalizedStringName(L"UnitName_Terabyte"), GetLocalizedStringName(L"UnitAbbreviation_Terabyte"), 17 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yobibits, GetLocalizedStringName(L"UnitName_Yobibits"), GetLocalizedStringName(L"UnitAbbreviation_Yobibits"), 32 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yobibytes, GetLocalizedStringName(L"UnitName_Yobibytes"), GetLocalizedStringName(L"UnitAbbreviation_Yobibytes"), 34 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yottabit, GetLocalizedStringName(L"UnitName_Yottabit"), GetLocalizedStringName(L"UnitAbbreviation_Yottabit"), 31 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yottabyte, GetLocalizedStringName(L"UnitName_Yottabyte"), GetLocalizedStringName(L"UnitAbbreviation_Yottabyte"), 33 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zebibits, GetLocalizedStringName(L"UnitName_Zebibits"), GetLocalizedStringName(L"UnitAbbreviation_Zebibits"), 28 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zebibytes, GetLocalizedStringName(L"UnitName_Zebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Zebibytes"), 30 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zetabits, GetLocalizedStringName(L"UnitName_Zetabits"), GetLocalizedStringName(L"UnitAbbreviation_Zetabits"), 27 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zetabytes, GetLocalizedStringName(L"UnitName_Zetabytes"), GetLocalizedStringName(L"UnitAbbreviation_Zetabytes"),29 });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_FloppyDisk, GetLocalizedStringName(L"UnitName_FloppyDisk"), GetLocalizedStringName(L"UnitAbbreviation_FloppyDisk"), 13, false, false, true });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_CD, GetLocalizedStringName(L"UnitName_CD"), GetLocalizedStringName(L"UnitAbbreviation_CD"), 14, false, false, true });
|
||||
dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_DVD, GetLocalizedStringName(L"UnitName_DVD"), GetLocalizedStringName(L"UnitAbbreviation_DVD"), 15, false, false, true });
|
||||
unitMap.emplace(ViewMode::Data, dataUnits);
|
||||
|
||||
vector<OrderedUnit> energyUnits;
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_BritishThermalUnit, GetLocalizedStringName(L"UnitName_BritishThermalUnit"), GetLocalizedStringName(L"UnitAbbreviation_BritishThermalUnit"), 7 });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Calorie, GetLocalizedStringName(L"UnitName_Calorie"), GetLocalizedStringName(L"UnitAbbreviation_Calorie"), 4 });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_ElectronVolt, GetLocalizedStringName(L"UnitName_Electron-Volt"), GetLocalizedStringName(L"UnitAbbreviation_Electron-Volt"), 1 });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_FootPound, GetLocalizedStringName(L"UnitName_Foot-Pound"), GetLocalizedStringName(L"UnitAbbreviation_Foot-Pound"), 6 });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Joule, GetLocalizedStringName(L"UnitName_Joule"), GetLocalizedStringName(L"UnitAbbreviation_Joule"), 2, true, false, false});
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Kilocalorie, GetLocalizedStringName(L"UnitName_Kilocalorie"), GetLocalizedStringName(L"UnitAbbreviation_Kilocalorie"), 5, false, true, false });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Kilojoule, GetLocalizedStringName(L"UnitName_Kilojoule"), GetLocalizedStringName(L"UnitAbbreviation_Kilojoule"), 3 });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Battery, GetLocalizedStringName(L"UnitName_Battery"), GetLocalizedStringName(L"UnitAbbreviation_Battery"), 8, false, false, true });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Banana, GetLocalizedStringName(L"UnitName_Banana"), GetLocalizedStringName(L"UnitAbbreviation_Banana"), 9, false, false, true });
|
||||
energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_SliceOfCake, GetLocalizedStringName(L"UnitName_SliceOfCake"), GetLocalizedStringName(L"UnitAbbreviation_SliceOfCake"),10, false, false, true });
|
||||
unitMap.emplace(ViewMode::Energy, energyUnits);
|
||||
|
||||
vector<OrderedUnit> lengthUnits;
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Centimeter, GetLocalizedStringName(L"UnitName_Centimeter"), GetLocalizedStringName(L"UnitAbbreviation_Centimeter"), 4 , USSource, (Target || UKTarget), false});
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Foot, GetLocalizedStringName(L"UnitName_Foot"), GetLocalizedStringName(L"UnitAbbreviation_Foot"), 8 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Inch, GetLocalizedStringName(L"UnitName_Inch"), GetLocalizedStringName(L"UnitAbbreviation_Inch"), 7 , (Source|| UKSource), USTarget, false });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Kilometer, GetLocalizedStringName(L"UnitName_Kilometer"), GetLocalizedStringName(L"UnitAbbreviation_Kilometer"), 6 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Meter, GetLocalizedStringName(L"UnitName_Meter"), GetLocalizedStringName(L"UnitAbbreviation_Meter"), 5 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Micron, GetLocalizedStringName(L"UnitName_Micron"), GetLocalizedStringName(L"UnitAbbreviation_Micron"), 2 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Mile, GetLocalizedStringName(L"UnitName_Mile"), GetLocalizedStringName(L"UnitAbbreviation_Mile"), 10 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Millimeter, GetLocalizedStringName(L"UnitName_Millimeter"), GetLocalizedStringName(L"UnitAbbreviation_Millimeter"), 3 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Nanometer, GetLocalizedStringName(L"UnitName_Nanometer"), GetLocalizedStringName(L"UnitAbbreviation_Nanometer"), 1 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_NauticalMile, GetLocalizedStringName(L"UnitName_NauticalMile"), GetLocalizedStringName(L"UnitAbbreviation_NauticalMile"), 11 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Yard, GetLocalizedStringName(L"UnitName_Yard"), GetLocalizedStringName(L"UnitAbbreviation_Yard"), 9 });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Paperclip, GetLocalizedStringName(L"UnitName_Paperclip"), GetLocalizedStringName(L"UnitAbbreviation_Paperclip"), 12 ,false, false, true });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 13 ,false, false, true });
|
||||
lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_JumboJet, GetLocalizedStringName(L"UnitName_JumboJet"), GetLocalizedStringName(L"UnitAbbreviation_JumboJet"), 14 , false, false, true });
|
||||
unitMap.emplace(ViewMode::Length, lengthUnits);
|
||||
|
||||
vector<OrderedUnit> powerUnits;
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_BritishThermalUnitPerMinute, GetLocalizedStringName(L"UnitName_BTUPerMinute"), GetLocalizedStringName(L"UnitAbbreviation_BTUPerMinute"), 5 });
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_FootPoundPerMinute, GetLocalizedStringName(L"UnitName_Foot-PoundPerMinute"), GetLocalizedStringName(L"UnitAbbreviation_Foot-PoundPerMinute"), 4 });
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Horsepower, GetLocalizedStringName(L"UnitName_Horsepower"), GetLocalizedStringName(L"UnitAbbreviation_Horsepower") , 3 , false, true, false });
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Kilowatt, GetLocalizedStringName(L"UnitName_Kilowatt"), GetLocalizedStringName(L"UnitAbbreviation_Kilowatt"), 2 , (Source|| USSource), false, false});
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Watt, GetLocalizedStringName(L"UnitName_Watt"), GetLocalizedStringName(L"UnitAbbreviation_Watt"), 1, UKSource });
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_LightBulb, GetLocalizedStringName(L"UnitName_LightBulb"), GetLocalizedStringName(L"UnitAbbreviation_LightBulb"), 6 ,false, false, true});
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"), 7 ,false, false, true});
|
||||
powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_TrainEngine, GetLocalizedStringName(L"UnitName_TrainEngine"), GetLocalizedStringName(L"UnitAbbreviation_TrainEngine"), 8 ,false, false, true });
|
||||
unitMap.emplace(ViewMode::Power, powerUnits);
|
||||
|
||||
vector<OrderedUnit> tempUnits;
|
||||
tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesCelsius, GetLocalizedStringName(L"UnitName_DegreesCelsius"), GetLocalizedStringName(L"UnitAbbreviation_DegreesCelsius"), 1, USSource, (Target || UKTarget), false });
|
||||
tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesFahrenheit, GetLocalizedStringName(L"UnitName_DegreesFahrenheit"), GetLocalizedStringName(L"UnitAbbreviation_DegreesFahrenheit"), 2 , (Source || UKSource), USTarget, false });
|
||||
tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_Kelvin, GetLocalizedStringName(L"UnitName_Kelvin"), GetLocalizedStringName(L"UnitAbbreviation_Kelvin"), 3 });
|
||||
unitMap.emplace(ViewMode::Temperature, tempUnits);
|
||||
|
||||
vector<OrderedUnit> timeUnits;
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Day, GetLocalizedStringName(L"UnitName_Day"), GetLocalizedStringName(L"UnitAbbreviation_Day"), 6 });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Hour, GetLocalizedStringName(L"UnitName_Hour"), GetLocalizedStringName(L"UnitAbbreviation_Hour"), 5 ,true, false, false });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Microsecond, GetLocalizedStringName(L"UnitName_Microsecond"), GetLocalizedStringName(L"UnitAbbreviation_Microsecond"), 1 });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Millisecond, GetLocalizedStringName(L"UnitName_Millisecond"), GetLocalizedStringName(L"UnitAbbreviation_Millisecond"), 2 });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Minute, GetLocalizedStringName(L"UnitName_Minute"), GetLocalizedStringName(L"UnitAbbreviation_Minute"), 4 ,false, true, false });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Second, GetLocalizedStringName(L"UnitName_Second"), GetLocalizedStringName(L"UnitAbbreviation_Second"), 3 });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Week, GetLocalizedStringName(L"UnitName_Week"), GetLocalizedStringName(L"UnitAbbreviation_Week"), 7 });
|
||||
timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Year, GetLocalizedStringName(L"UnitName_Year"), GetLocalizedStringName(L"UnitAbbreviation_Year"), 8 });
|
||||
unitMap.emplace(ViewMode::Time, timeUnits);
|
||||
|
||||
vector<OrderedUnit> speedUnits;
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_CentimetersPerSecond, GetLocalizedStringName(L"UnitName_CentimetersPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_CentimetersPerSecond"), 1 });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_FeetPerSecond, GetLocalizedStringName(L"UnitName_FeetPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_FeetPerSecond"), 4 });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_KilometersPerHour, GetLocalizedStringName(L"UnitName_KilometersPerHour"), GetLocalizedStringName(L"UnitAbbreviation_KilometersPerHour"), 3 ,(USSource || UKSource), Target, false });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Knot, GetLocalizedStringName(L"UnitName_Knot"), GetLocalizedStringName(L"UnitAbbreviation_Knot"), 6 });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Mach, GetLocalizedStringName(L"UnitName_Mach"), GetLocalizedStringName(L"UnitAbbreviation_Mach"), 7 });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MetersPerSecond, GetLocalizedStringName(L"UnitName_MetersPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_MetersPerSecond"), 2 });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MilesPerHour, GetLocalizedStringName(L"UnitName_MilesPerHour"), GetLocalizedStringName(L"UnitAbbreviation_MilesPerHour"), 5, Source, (UKTarget || USTarget), false });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Turtle, GetLocalizedStringName(L"UnitName_Turtle"), GetLocalizedStringName(L"UnitAbbreviation_Turtle"), 8 ,false, false, true });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"),9 , false, false, true });
|
||||
speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Jet, GetLocalizedStringName(L"UnitName_Jet"), GetLocalizedStringName(L"UnitAbbreviation_Jet"), 10, false, false, true });
|
||||
unitMap.emplace(ViewMode::Speed, speedUnits);
|
||||
|
||||
vector<OrderedUnit> volumeUnits;
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicCentimeter, GetLocalizedStringName(L"UnitName_CubicCentimeter"), GetLocalizedStringName(L"UnitAbbreviation_CubicCentimeter"), 2 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicFoot, GetLocalizedStringName(L"UnitName_CubicFoot"), GetLocalizedStringName(L"UnitAbbreviation_CubicFoot"), 13 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicInch, GetLocalizedStringName(L"UnitName_CubicInch"), GetLocalizedStringName(L"UnitAbbreviation_CubicInch"), 12 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicMeter, GetLocalizedStringName(L"UnitName_CubicMeter"), GetLocalizedStringName(L"UnitAbbreviation_CubicMeter"), 4 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicYard, GetLocalizedStringName(L"UnitName_CubicYard"), GetLocalizedStringName(L"UnitAbbreviation_CubicYard"), 14 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CupUS, GetLocalizedStringName(L"UnitName_CupUS"), GetLocalizedStringName(L"UnitAbbreviation_CupUS"), 8 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUK, GetLocalizedStringName(L"UnitName_FluidOunceUK"), GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUK"), 17 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUS, GetLocalizedStringName(L"UnitName_FluidOunceUS"), GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUS"), 7 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_GallonUK, GetLocalizedStringName(L"UnitName_GallonUK"), GetLocalizedStringName(L"UnitAbbreviation_GallonUK"), 20 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_GallonUS, GetLocalizedStringName(L"UnitName_GallonUS"), GetLocalizedStringName(L"UnitAbbreviation_GallonUS"), 11 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Liter, GetLocalizedStringName(L"UnitName_Liter"), GetLocalizedStringName(L"UnitAbbreviation_Liter"), 3 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Milliliter, GetLocalizedStringName(L"UnitName_Milliliter"), GetLocalizedStringName(L"UnitAbbreviation_Milliliter"), 1 , USSource, (Target || UKTarget), false});
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_PintUK, GetLocalizedStringName(L"UnitName_PintUK"), GetLocalizedStringName(L"UnitAbbreviation_PintUK"), 18 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_PintUS, GetLocalizedStringName(L"UnitName_PintUS"), GetLocalizedStringName(L"UnitAbbreviation_PintUS"), 9 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUS, GetLocalizedStringName(L"UnitName_TablespoonUS"), GetLocalizedStringName(L"UnitAbbreviation_TablespoonUS"), 6 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUS, GetLocalizedStringName(L"UnitName_TeaspoonUS"), GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUS"), 5 ,Source, USTarget, false });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_QuartUK, GetLocalizedStringName(L"UnitName_QuartUK"), GetLocalizedStringName(L"UnitAbbreviation_QuartUK"), 19 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_QuartUS, GetLocalizedStringName(L"UnitName_QuartUS"), GetLocalizedStringName(L"UnitAbbreviation_QuartUS"), 10 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUK, GetLocalizedStringName(L"UnitName_TeaspoonUK"), GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUK"), 15, UKSource });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUK, GetLocalizedStringName(L"UnitName_TablespoonUK"), GetLocalizedStringName(L"UnitAbbreviation_TablespoonUK"), 16 });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CoffeeCup, GetLocalizedStringName(L"UnitName_CoffeeCup"), GetLocalizedStringName(L"UnitAbbreviation_CoffeeCup"), 22 ,false, false, true });
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Bathtub, GetLocalizedStringName(L"UnitName_Bathtub"), GetLocalizedStringName(L"UnitAbbreviation_Bathtub"), 23 ,false, false, true});
|
||||
volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_SwimmingPool, GetLocalizedStringName(L"UnitName_SwimmingPool"), GetLocalizedStringName(L"UnitAbbreviation_SwimmingPool"), 24 ,false, false, true });
|
||||
unitMap.emplace(ViewMode::Volume, volumeUnits);
|
||||
|
||||
vector<OrderedUnit> weightUnits;
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Carat, GetLocalizedStringName(L"UnitName_Carat"), GetLocalizedStringName(L"UnitAbbreviation_Carat"), 1 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Centigram, GetLocalizedStringName(L"UnitName_Centigram"), GetLocalizedStringName(L"UnitAbbreviation_Centigram"), 3 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Decigram, GetLocalizedStringName(L"UnitName_Decigram"), GetLocalizedStringName(L"UnitAbbreviation_Decigram"), 4 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Decagram, GetLocalizedStringName(L"UnitName_Decagram"), GetLocalizedStringName(L"UnitAbbreviation_Decagram"), 6 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Gram, GetLocalizedStringName(L"UnitName_Gram"), GetLocalizedStringName(L"UnitAbbreviation_Gram"), 5 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Hectogram, GetLocalizedStringName(L"UnitName_Hectogram"), GetLocalizedStringName(L"UnitAbbreviation_Hectogram"), 7 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Kilogram, GetLocalizedStringName(L"UnitName_Kilogram"), GetLocalizedStringName(L"UnitAbbreviation_Kilogram"), 8 ,(USSource || UKSource), Target, false});
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_LongTon, GetLocalizedStringName(L"UnitName_LongTon"), GetLocalizedStringName(L"UnitAbbreviation_LongTon"), 14 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Milligram, GetLocalizedStringName(L"UnitName_Milligram"), GetLocalizedStringName(L"UnitAbbreviation_Milligram"), 2 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Ounce, GetLocalizedStringName(L"UnitName_Ounce"), GetLocalizedStringName(L"UnitAbbreviation_Ounce"), 10 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Pound, GetLocalizedStringName(L"UnitName_Pound"), GetLocalizedStringName(L"UnitAbbreviation_Pound"), 11 , Source, (USTarget ||UKTarget), false });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_ShortTon, GetLocalizedStringName(L"UnitName_ShortTon"), GetLocalizedStringName(L"UnitAbbreviation_ShortTon"), 13 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Stone, GetLocalizedStringName(L"UnitName_Stone"), GetLocalizedStringName(L"UnitAbbreviation_Stone"), 12 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Tonne, GetLocalizedStringName(L"UnitName_Tonne"), GetLocalizedStringName(L"UnitAbbreviation_Tonne"), 9 });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Snowflake, GetLocalizedStringName(L"UnitName_Snowflake"), GetLocalizedStringName(L"UnitAbbreviation_Snowflake"), 15 ,false, false, true });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_SoccerBall, GetLocalizedStringName(L"UnitName_SoccerBall"), GetLocalizedStringName(L"UnitAbbreviation_SoccerBall"), 16 , false, false, true });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Elephant, GetLocalizedStringName(L"UnitName_Elephant"), GetLocalizedStringName(L"UnitAbbreviation_Elephant"), 17 ,false, false, true });
|
||||
weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Whale, GetLocalizedStringName(L"UnitName_Whale"), GetLocalizedStringName(L"UnitAbbreviation_Whale"), 18 ,false, false, true });
|
||||
unitMap.emplace(ViewMode::Weight, weightUnits);
|
||||
|
||||
vector<OrderedUnit> pressureUnits;
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Atmosphere, GetLocalizedStringName(L"UnitName_Atmosphere"), GetLocalizedStringName(L"UnitAbbreviation_Atmosphere"), 1 , true, false, false });
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Bar, GetLocalizedStringName(L"UnitName_Bar"), GetLocalizedStringName(L"UnitAbbreviation_Bar"), 2, false, true, false});
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_KiloPascal, GetLocalizedStringName(L"UnitName_KiloPascal"), GetLocalizedStringName(L"UnitAbbreviation_KiloPascal"), 3 });
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_MillimeterOfMercury, GetLocalizedStringName(L"UnitName_MillimeterOfMercury "), GetLocalizedStringName(L"UnitAbbreviation_MillimeterOfMercury "), 4 });
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Pascal, GetLocalizedStringName(L"UnitName_Pascal"), GetLocalizedStringName(L"UnitAbbreviation_Pascal"), 5 });
|
||||
pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_PSI, GetLocalizedStringName(L"UnitName_PSI"), GetLocalizedStringName(L"UnitAbbreviation_PSI"), 6, false, false, false });
|
||||
unitMap.emplace(ViewMode::Pressure, pressureUnits);
|
||||
|
||||
vector<OrderedUnit> angleUnits;
|
||||
angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Degree, GetLocalizedStringName(L"UnitName_Degree"), GetLocalizedStringName(L"UnitAbbreviation_Degree"), 1, true, false, false });
|
||||
angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Radian, GetLocalizedStringName(L"UnitName_Radian"), GetLocalizedStringName(L"UnitAbbreviation_Radian"), 2, false, true, false });
|
||||
angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Gradian, GetLocalizedStringName(L"UnitName_Gradian"), GetLocalizedStringName(L"UnitAbbreviation_Gradian"), 3});
|
||||
unitMap.emplace(ViewMode::Angle, angleUnits);
|
||||
}
|
||||
|
||||
void UnitConverterDataLoader::GetConversionData(_In_ unordered_map<ViewMode, unordered_map<int, double>>& categoryToUnitConversionMap)
|
||||
{
|
||||
/*categoryId, UnitId, factor*/
|
||||
static const vector<UnitData> unitDataList = {
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_Acre, 4046.8564224 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareMeter, 1 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareFoot, 0.09290304 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareYard, 0.83612736 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareMillimeter, 0.000001 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareCentimeter, 0.0001 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareInch, 0.00064516 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareMile, 2589988.110336 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SquareKilometer, 1000000 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_Hectare, 10000 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_Hand, 0.012516104 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_Paper, 0.06032246 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_SoccerField, 10869.66 },
|
||||
{ ViewMode::Area, UnitConverterUnits::Area_Castle, 100000 },
|
||||
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Bit, 0.000000125 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Byte, 0.000001 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Kilobyte, 0.001 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Megabyte, 1 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Gigabyte, 1000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Terabyte, 1000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Petabyte, 1000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Exabytes, 1000000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Zetabytes, 1000000000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Yottabyte, 1000000000000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Kilobit, 0.000125 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Megabit, 0.125 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Gigabit, 125 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Terabit, 125000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Petabit, 125000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Exabits, 125000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Zetabits, 125000000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Yottabit, 125000000000000000 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Gibibits, 134.217728 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Gibibytes, 1073.741824 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Kibibits, 0.000128 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Kibibytes, 0.001024 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Mebibits, 0.131072 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Mebibytes, 1.048576 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Pebibits, 140737488.355328 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Pebibytes, 1125899906.842624 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Tebibits, 137438.953472 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Tebibytes, 1099511.627776 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Exbibits, 144115188075.855872 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Exbibytes, 1152921504606.846976 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Zebibits, 147573952589676.412928 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Zebibytes, 1180591620717411.303424 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Yobibits, 151115727451828646.838272 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_Yobibytes, 1208925819614629174.706176 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_FloppyDisk, 1.509949 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_CD, 734.003200 },
|
||||
{ ViewMode::Data, UnitConverterUnits::Data_DVD, 5046.586573 },
|
||||
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Calorie, 4.184 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Kilocalorie, 4184},
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_BritishThermalUnit, 1055.056 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Kilojoule, 1000 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_ElectronVolt, 0.0000000000000000001602176565 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Joule, 1 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_FootPound, 1.3558179483314 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Battery, 9000 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_Banana, 439614 },
|
||||
{ ViewMode::Energy, UnitConverterUnits::Energy_SliceOfCake, 1046700 },
|
||||
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Inch, 0.0254 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Foot, 0.3048 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Yard, 0.9144 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Mile, 1609.344 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Micron, 0.000001 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Millimeter, 0.001 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Nanometer, 0.000000001 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Centimeter, 0.01 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Meter, 1 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Kilometer, 1000 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_NauticalMile, 1852 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Paperclip, 0.035052 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_Hand, 0.18669 },
|
||||
{ ViewMode::Length, UnitConverterUnits::Length_JumboJet, 76 },
|
||||
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_BritishThermalUnitPerMinute, 17.58426666666667 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_FootPoundPerMinute, 0.0225969658055233 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_Watt, 1 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_Kilowatt, 1000 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_Horsepower, 745.69987158227022 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_LightBulb, 60 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_Horse, 745.7 },
|
||||
{ ViewMode::Power, UnitConverterUnits::Power_TrainEngine, 2982799.486329081 },
|
||||
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Day, 86400 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Second, 1 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Week, 604800 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Year, 31557600 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Millisecond, 0.001 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Microsecond, 0.000001 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Minute, 60 },
|
||||
{ ViewMode::Time, UnitConverterUnits::Time_Hour, 3600 },
|
||||
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CupUS, 236.588237 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_PintUS, 473.176473 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_PintUK, 568.26125 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_QuartUS, 946.352946 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_QuartUK, 1136.5225 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_GallonUS, 3785.411784 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_GallonUK, 4546.09 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_Liter, 1000 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUS, 4.928922 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUS, 14.786765 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CubicCentimeter, 1 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CubicYard, 764554.857984 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CubicMeter, 1000000 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_Milliliter, 1 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CubicInch, 16.387064 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CubicFoot, 28316.846592 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUS, 29.5735295625 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUK, 28.4130625 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUK, 5.91938802083333333333 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUK, 17.7581640625 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_CoffeeCup, 236.5882 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_Bathtub, 378541.2 },
|
||||
{ ViewMode::Volume, UnitConverterUnits::Volume_SwimmingPool, 3750000000 },
|
||||
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Kilogram, 1 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Hectogram, 0.1 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Decagram, 0.01 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Gram, 0.001 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Pound, 0.45359237 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Ounce, 0.028349523125 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Milligram, 0.000001 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Centigram, 0.00001 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Decigram, 0.0001 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_LongTon, 1016.0469088 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Tonne, 1000 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Stone, 6.35029318 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Carat, 0.0002 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_ShortTon, 907.18474 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Snowflake, 0.000002 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_SoccerBall, 0.4325 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Elephant, 4000 },
|
||||
{ ViewMode::Weight, UnitConverterUnits::Weight_Whale, 90000 },
|
||||
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_CentimetersPerSecond, 1 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_FeetPerSecond, 30.48 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_KilometersPerHour, 27.777777777777777777778 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_Knot, 51.44 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_Mach, 34030 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_MetersPerSecond, 100 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_MilesPerHour, 44.7 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_Turtle, 8.94 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_Horse, 2011.5 },
|
||||
{ ViewMode::Speed, UnitConverterUnits::Speed_Jet, 24585 },
|
||||
|
||||
{ ViewMode::Angle, UnitConverterUnits::Angle_Degree, 1 },
|
||||
{ ViewMode::Angle, UnitConverterUnits::Angle_Radian, 57.29577951308233 },
|
||||
{ ViewMode::Angle, UnitConverterUnits::Angle_Gradian, 0.9 },
|
||||
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_Atmosphere, 1 },
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_Bar, 0.9869232667160128 },
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_KiloPascal, 0.0098692326671601 },
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_MillimeterOfMercury, 0.0013155687145324 },
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_Pascal, 9.869232667160128e-6 },
|
||||
{ ViewMode::Pressure, UnitConverterUnits::Pressure_PSI, 0.068045961016531 }
|
||||
};
|
||||
|
||||
// Populate the hash map and return;
|
||||
for (UnitData unitdata : unitDataList)
|
||||
{
|
||||
if (categoryToUnitConversionMap.find(unitdata.categoryId) == categoryToUnitConversionMap.end())
|
||||
{
|
||||
unordered_map<int, double> conversionData;
|
||||
conversionData.insert(pair<int, double>(unitdata.unitId, unitdata.factor));
|
||||
categoryToUnitConversionMap.insert(pair<ViewMode, unordered_map<int, double>>(unitdata.categoryId, conversionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryToUnitConversionMap.at(unitdata.categoryId).insert(pair<int, double>(unitdata.unitId, unitdata.factor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wstring UnitConverterDataLoader::GetLocalizedStringName(String^ stringId)
|
||||
{
|
||||
return AppResourceProvider::GetInstance().GetResourceString(stringId)->Data();
|
||||
}
|
||||
|
||||
wstring UnitConverterDataLoader::GetRegion()
|
||||
{
|
||||
if ((m_currentRegionCode == L"US") ||
|
||||
(m_currentRegionCode == L"LR") ||
|
||||
(m_currentRegionCode == L"MM"))
|
||||
{
|
||||
return L"US";
|
||||
}
|
||||
else if (m_currentRegionCode == L"GB")
|
||||
{
|
||||
return L"UK";
|
||||
}
|
||||
else
|
||||
{
|
||||
return L"Others";
|
||||
}
|
||||
}
|
||||
|
||||
void UnitConverterDataLoader::GetExplicitConversionData(_In_ unordered_map<int, unordered_map<int, UCM::ConversionData>>& unitToUnitConversionList)
|
||||
{
|
||||
/* categoryId, ParentUnitId, UnitId, ratio, offset, offsetfirst*/
|
||||
ExplicitUnitConversionData conversionDataList[] = {
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesCelsius , 1, 0 },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, 32 },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_Kelvin, 1, 273.15 },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_DegreesCelsius, 0.55555555555555555555555555555556, -32, CONVERT_WITH_OFFSET_FIRST },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_DegreesFahrenheit, 1, 0 },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_Kelvin, 0.55555555555555555555555555555556, 459.67, CONVERT_WITH_OFFSET_FIRST },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_DegreesCelsius, 1, -273.15, CONVERT_WITH_OFFSET_FIRST },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, -459.67 },
|
||||
{ ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_Kelvin, 1, 0 }
|
||||
};
|
||||
|
||||
// Populate the hash map and return;
|
||||
for (ExplicitUnitConversionData data : conversionDataList)
|
||||
{
|
||||
if (unitToUnitConversionList.find(data.parentUnitId) == unitToUnitConversionList.end())
|
||||
{
|
||||
unordered_map<int, UCM::ConversionData> conversionData;
|
||||
conversionData.insert(pair<int, UCM::ConversionData>(data.unitId, static_cast<UCM::ConversionData>(data)));
|
||||
unitToUnitConversionList.insert(pair<int, unordered_map<int, UCM::ConversionData>>(data.parentUnitId, conversionData));
|
||||
}
|
||||
else
|
||||
{
|
||||
unitToUnitConversionList.at(data.parentUnitId).insert(pair<int, UCM::ConversionData>(data.unitId, static_cast<UCM::ConversionData>(data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h
Normal file
72
src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
struct OrderedUnit : UnitConversionManager::Unit
|
||||
{
|
||||
OrderedUnit(){}
|
||||
|
||||
OrderedUnit(int id, std::wstring name, std::wstring abbreviation, int order, bool isConversionSource = false, bool isConversionTarget = false, bool isWhimsical = false)
|
||||
: UnitConversionManager::Unit(id, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical), order(order)
|
||||
{
|
||||
}
|
||||
|
||||
int order;
|
||||
};
|
||||
|
||||
struct UnitData
|
||||
{
|
||||
CalculatorApp::Common::ViewMode categoryId;
|
||||
int unitId;
|
||||
double factor;
|
||||
};
|
||||
|
||||
struct ExplicitUnitConversionData : UnitConversionManager::ConversionData
|
||||
{
|
||||
ExplicitUnitConversionData(){}
|
||||
ExplicitUnitConversionData(CalculatorApp::Common::ViewMode categoryId, int parentUnitId, int unitId, double ratio, double offset, bool offsetFirst = false) :
|
||||
categoryId(categoryId), parentUnitId(parentUnitId), unitId(unitId), UnitConversionManager::ConversionData(ratio, offset, offsetFirst)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CalculatorApp::Common::ViewMode categoryId;
|
||||
int parentUnitId;
|
||||
int unitId;
|
||||
};
|
||||
|
||||
class UnitConverterDataLoader : public UnitConversionManager::IConverterDataLoader,
|
||||
public std::enable_shared_from_this<UnitConverterDataLoader>
|
||||
{
|
||||
public:
|
||||
UnitConverterDataLoader(Windows::Globalization::GeographicRegion^ region);
|
||||
|
||||
private:
|
||||
// IConverterDataLoader
|
||||
void LoadData() override;
|
||||
std::vector<UnitConversionManager::Category> LoadOrderedCategories() override;
|
||||
std::vector<UnitConversionManager::Unit> LoadOrderedUnits(const UnitConversionManager::Category& c) override;
|
||||
std::unordered_map<UnitConversionManager::Unit, UnitConversionManager::ConversionData, UnitConversionManager::UnitHash> LoadOrderedRatios(const UnitConversionManager::Unit& unit) override;
|
||||
bool SupportsCategory(const UnitConversionManager::Category& target) override;
|
||||
// IConverterDataLoader
|
||||
|
||||
void GetCategories(_In_ std::shared_ptr<std::vector<UnitConversionManager::Category>> categoriesList);
|
||||
void GetUnits(_In_ std::unordered_map<CalculatorApp::Common::ViewMode, std::vector<CalculatorApp::ViewModel::OrderedUnit>>& unitMap);
|
||||
void GetConversionData(_In_ std::unordered_map<CalculatorApp::Common::ViewMode, std::unordered_map<int, double>>& categoryToUnitConversionMap);
|
||||
void GetExplicitConversionData(_In_ std::unordered_map<int, std::unordered_map<int, UnitConversionManager::ConversionData>>& unitToUnitConversionList);
|
||||
|
||||
std::wstring GetLocalizedStringName(_In_ Platform::String^ stringId);
|
||||
std::wstring GetRegion();
|
||||
|
||||
std::shared_ptr<std::vector<UnitConversionManager::Category>> m_categoryList;
|
||||
std::shared_ptr<UnitConversionManager::CategoryToUnitVectorMap> m_categoryToUnits;
|
||||
std::shared_ptr<UnitConversionManager::UnitToUnitToConversionDataMap> m_ratioMap;
|
||||
Platform::String^ m_currentRegionCode;
|
||||
};
|
||||
}
|
||||
}
|
||||
392
src/CalcViewModel/DateCalculatorViewModel.cpp
Normal file
392
src/CalcViewModel/DateCalculatorViewModel.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "Common\LocalizationStringUtil.h"
|
||||
#include "Common\LocalizationService.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
#include "Common\CopyPasteManager.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::DateCalculation;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::Globalization::DateTimeFormatting;
|
||||
using namespace Windows::System::UserProfile;
|
||||
|
||||
namespace CalculatorApp::ViewModel::DateCalculatorViewModelProperties
|
||||
{
|
||||
StringReference StrDateDiffResult(L"StrDateDiffResult");
|
||||
StringReference StrDateDiffResultAutomationName(L"StrDateDiffResultAutomationName");
|
||||
StringReference StrDateDiffResultInDays(L"StrDateDiffResultInDays");
|
||||
StringReference StrDateResult(L"StrDateResult");
|
||||
StringReference StrDateResultAutomationName(L"StrDateResultAutomationName");
|
||||
StringReference IsDiffInDays(L"IsDiffInDays");
|
||||
}
|
||||
|
||||
DateCalculatorViewModel::DateCalculatorViewModel() :
|
||||
m_IsDateDiffMode(true),
|
||||
m_IsAddMode(true),
|
||||
m_isOutOfBound(false),
|
||||
m_DaysOffset(0),
|
||||
m_MonthsOffset(0),
|
||||
m_YearsOffset(0),
|
||||
m_StrDateDiffResult(L""),
|
||||
m_StrDateDiffResultAutomationName(L""),
|
||||
m_StrDateDiffResultInDays(L""),
|
||||
m_StrDateResult(L""),
|
||||
m_StrDateResultAutomationName(L""),
|
||||
m_fromDate({ 0 }),
|
||||
m_toDate({ 0 }),
|
||||
m_startDate({ 0 }),
|
||||
m_dateResult({ 0 })
|
||||
{
|
||||
const auto& localizationSettings = LocalizationSettings::GetInstance();
|
||||
|
||||
// Initialize Date Output format instances
|
||||
InitializeDateOutputFormats(localizationSettings.GetCalendarIdentifier());
|
||||
|
||||
// Initialize Date Calc engine
|
||||
m_dateCalcEngine = make_shared<DateCalculationEngine>(localizationSettings.GetCalendarIdentifier());
|
||||
|
||||
// Initialize dates of DatePicker controls to today's date
|
||||
auto calendar = ref new Calendar();
|
||||
auto today = calendar->GetDateTime();
|
||||
|
||||
// FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC)
|
||||
m_fromDate = today;
|
||||
m_toDate = today;
|
||||
FromDate = ClipTime(today);
|
||||
ToDate = ClipTime(today);
|
||||
|
||||
// StartDate should not be clipped
|
||||
StartDate = today;
|
||||
m_dateResult = today;
|
||||
|
||||
// Initialize the list separator delimiter appended with a space at the end, e.g. ", "
|
||||
// This will be used for date difference formatting: Y years, M months, W weeks, D days
|
||||
m_listSeparator = ref new String((localizationSettings.GetListSeparator() + L" ").c_str());
|
||||
|
||||
// Initialize the output results
|
||||
UpdateDisplayResult();
|
||||
|
||||
m_offsetValues = ref new Vector<String^>();
|
||||
for (int i = 0; i <= c_maxOffsetValue; i++)
|
||||
{
|
||||
wstring numberStr(to_wstring(i));
|
||||
localizationSettings.LocalizeDisplayValue(&numberStr);
|
||||
m_offsetValues->Append(ref new String(numberStr.c_str()));
|
||||
}
|
||||
|
||||
/* In the ClipTime function, we used to change timezone to UTC before clipping the time.
|
||||
The comment from the previous delopers said this was done to eliminate the effects of
|
||||
Daylight Savings Time. We can't think of a good reason why this change in timezone is
|
||||
necessary and did find bugs related to the change, therefore, we have removed the
|
||||
change. Just in case, we will see if the clipped time is ever a different day from the
|
||||
original day, which would hopefully indicate the change in timezone was actually
|
||||
necessary. We will collect telemetry if we find this case. If we don't see any
|
||||
telemetry events after the application has been used for some time, we will feel safe
|
||||
and can remove this function. */
|
||||
DayOfWeek trueDayOfWeek = calendar->DayOfWeek;
|
||||
|
||||
DateTime clippedTime = ClipTime(today);
|
||||
calendar->SetDateTime(clippedTime);
|
||||
if (calendar->DayOfWeek != trueDayOfWeek)
|
||||
{
|
||||
calendar->SetDateTime(today);
|
||||
TraceLogger::GetInstance().LogDateClippedTimeDifferenceFound(
|
||||
from_cx<winrt::Windows::Globalization::Calendar>(calendar),
|
||||
winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ clippedTime.UniversalTime } });
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnPropertyChanged(_In_ String^ prop)
|
||||
{
|
||||
if (prop == DateCalculatorViewModelProperties::StrDateDiffResult)
|
||||
{
|
||||
UpdateStrDateDiffResultAutomationName();
|
||||
}
|
||||
else if (prop == DateCalculatorViewModelProperties::StrDateResult)
|
||||
{
|
||||
UpdateStrDateResultAutomationName();
|
||||
}
|
||||
else if (prop != DateCalculatorViewModelProperties::StrDateDiffResultAutomationName
|
||||
&& prop != DateCalculatorViewModelProperties::StrDateDiffResultInDays
|
||||
&& prop != DateCalculatorViewModelProperties::StrDateResultAutomationName
|
||||
&& prop != DateCalculatorViewModelProperties::IsDiffInDays)
|
||||
{
|
||||
OnInputsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnInputsChanged()
|
||||
{
|
||||
DateDifference dateDiff;
|
||||
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
DateTime clippedFromDate = ClipTime(FromDate);
|
||||
DateTime clippedToDate = ClipTime(ToDate);
|
||||
|
||||
// Calculate difference between two dates
|
||||
m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_allDateUnitsOutputFormat, &dateDiff);
|
||||
DateDiffResult = dateDiff;
|
||||
|
||||
m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_daysOutputFormat, &dateDiff);
|
||||
DateDiffResultInDays = dateDiff;
|
||||
}
|
||||
else
|
||||
{
|
||||
dateDiff.day = DaysOffset;
|
||||
dateDiff.month = MonthsOffset;
|
||||
dateDiff.year = YearsOffset;
|
||||
|
||||
DateTime dateTimeResult;
|
||||
|
||||
if (m_IsAddMode)
|
||||
{
|
||||
// Add number of Days, Months and Years to a Date
|
||||
IsOutOfBound = !m_dateCalcEngine->AddDuration(StartDate, dateDiff, &dateTimeResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract number of Days, Months and Years from a Date
|
||||
IsOutOfBound = !m_dateCalcEngine->SubtractDuration(StartDate, dateDiff, &dateTimeResult);
|
||||
}
|
||||
|
||||
if (!m_isOutOfBound)
|
||||
{
|
||||
DateResult = dateTimeResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateDisplayResult()
|
||||
{
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
// Are to and from dates the same
|
||||
if (m_dateDiffResultInDays.day == 0)
|
||||
{
|
||||
IsDiffInDays = true;
|
||||
StrDateDiffResultInDays = L"";
|
||||
StrDateDiffResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_SameDates");
|
||||
}
|
||||
else if ((m_dateDiffResult.year == 0) &&
|
||||
(m_dateDiffResult.month == 0) &&
|
||||
(m_dateDiffResult.week == 0))
|
||||
{
|
||||
IsDiffInDays = true;
|
||||
StrDateDiffResultInDays = L"";
|
||||
|
||||
// Display result in number of days
|
||||
StrDateDiffResult = GetDateDiffStringInDays();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsDiffInDays = false;
|
||||
|
||||
// Display result in days, weeks, months and years
|
||||
StrDateDiffResult = GetDateDiffString();
|
||||
|
||||
// Display result in number of days
|
||||
StrDateDiffResultInDays = GetDateDiffStringInDays();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_isOutOfBound)
|
||||
{
|
||||
// Display Date out of bound message
|
||||
StrDateResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_OutOfBoundMessage");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Display the resulting date in long format
|
||||
StrDateResult = m_dateTimeFormatter->Format(DateResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateStrDateDiffResultAutomationName()
|
||||
{
|
||||
String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_DifferenceResultAutomationName");
|
||||
wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateDiffResult->Data());
|
||||
StrDateDiffResultAutomationName = ref new String(localizedAutomationName.c_str());
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateStrDateResultAutomationName()
|
||||
{
|
||||
String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_ResultingDateAutomationName");
|
||||
wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateResult->Data());
|
||||
StrDateResultAutomationName = ref new String(localizedAutomationName.c_str());
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String^ calendarIdentifier)
|
||||
{
|
||||
// Format for Add/Subtract days
|
||||
m_dateTimeFormatter = LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(
|
||||
L"longdate",
|
||||
calendarIdentifier,
|
||||
ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used
|
||||
|
||||
// Format for Date Difference
|
||||
m_allDateUnitsOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Week | DateUnit::Day;
|
||||
m_daysOutputFormat = DateUnit::Day;
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetDateDiffString() const
|
||||
{
|
||||
String^ result = L"";
|
||||
bool addDelimiter = false;
|
||||
AppResourceProvider resourceLoader = AppResourceProvider::GetInstance();
|
||||
|
||||
auto yearCount = m_dateDiffResult.year;
|
||||
if (yearCount > 0)
|
||||
{
|
||||
result = String::Concat(GetLocalizedNumberString(yearCount), L" ");
|
||||
|
||||
if (yearCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Years"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Year"));
|
||||
}
|
||||
|
||||
// set the flags to add a delimiter whenever the next unit is added
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
auto monthCount = m_dateDiffResult.month;
|
||||
if (monthCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(monthCount), L" "));
|
||||
|
||||
if (monthCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Months"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Month"));
|
||||
}
|
||||
}
|
||||
|
||||
auto weekCount = m_dateDiffResult.week;
|
||||
if (weekCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(weekCount), L" "));
|
||||
|
||||
if (weekCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Weeks"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Week"));
|
||||
}
|
||||
}
|
||||
|
||||
auto dayCount = m_dateDiffResult.day;
|
||||
if (dayCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(dayCount), L" "));
|
||||
|
||||
if (dayCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Days"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Day"));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetDateDiffStringInDays() const
|
||||
{
|
||||
String^ strDateUnit;
|
||||
|
||||
// Display the result as '1 day' or 'N days'
|
||||
if (m_dateDiffResultInDays.day > 1)
|
||||
{
|
||||
strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Days");
|
||||
}
|
||||
else
|
||||
{
|
||||
strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Day");
|
||||
}
|
||||
|
||||
return String::Concat(GetLocalizedNumberString(m_dateDiffResultInDays.day), String::Concat(L" ", strDateUnit));
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnCopyCommand(Platform::Object^ parameter)
|
||||
{
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
CopyPasteManager::CopyToClipboard(m_StrDateDiffResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
CopyPasteManager::CopyToClipboard(m_StrDateResult);
|
||||
}
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetLocalizedNumberString(int value) const
|
||||
{
|
||||
wstring numberStr(to_wstring(value));
|
||||
LocalizationSettings::GetInstance().LocalizeDisplayValue(&numberStr);
|
||||
return ref new String(numberStr.c_str());
|
||||
}
|
||||
|
||||
// Adjusts the given DateTime to 12AM of the same day
|
||||
DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime)
|
||||
{
|
||||
auto calendar = ref new Calendar();
|
||||
calendar->SetDateTime(dateTime);
|
||||
calendar->Period = 1;
|
||||
calendar->Hour = 12;
|
||||
calendar->Minute = 0;
|
||||
calendar->Second = 0;
|
||||
calendar->Nanosecond = 0;
|
||||
|
||||
return calendar->GetDateTime();
|
||||
}
|
||||
151
src/CalcViewModel/DateCalculatorViewModel.h
Normal file
151
src/CalcViewModel/DateCalculatorViewModel.h
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common\DateCalculator.h"
|
||||
|
||||
const int c_maxOffsetValue = 999;
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class DateCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
DateCalculatorViewModel();
|
||||
|
||||
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
|
||||
|
||||
// Input Properties
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDateDiffMode);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsAddMode);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDiffInDays); // If diff is only in days or the dates are the same,
|
||||
// then show only one result and avoid redundancy
|
||||
|
||||
OBSERVABLE_PROPERTY_RW(int, DaysOffset);
|
||||
OBSERVABLE_PROPERTY_RW(int, MonthsOffset);
|
||||
OBSERVABLE_PROPERTY_RW(int, YearsOffset);
|
||||
|
||||
// Read only property for offset values
|
||||
property Windows::Foundation::Collections::IVector<Platform::String^>^ OffsetValues
|
||||
{
|
||||
Windows::Foundation::Collections::IVector<Platform::String^>^ get() { return m_offsetValues; }
|
||||
}
|
||||
|
||||
// From date for Date Diff
|
||||
property Windows::Foundation::DateTime FromDate
|
||||
{
|
||||
Windows::Foundation::DateTime get() { return m_fromDate; }
|
||||
|
||||
void set(Windows::Foundation::DateTime value)
|
||||
{
|
||||
if (m_fromDate.UniversalTime != value.UniversalTime)
|
||||
{
|
||||
m_fromDate = value;
|
||||
RaisePropertyChanged("FromDate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To date for Date Diff
|
||||
property Windows::Foundation::DateTime ToDate
|
||||
{
|
||||
Windows::Foundation::DateTime get() { return m_toDate; }
|
||||
|
||||
void set(Windows::Foundation::DateTime value)
|
||||
{
|
||||
if (m_toDate.UniversalTime != value.UniversalTime)
|
||||
{
|
||||
m_toDate = value;
|
||||
RaisePropertyChanged("ToDate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start date for Add/Subtract date
|
||||
property Windows::Foundation::DateTime StartDate
|
||||
{
|
||||
Windows::Foundation::DateTime get() { return m_startDate; }
|
||||
|
||||
void set(Windows::Foundation::DateTime value)
|
||||
{
|
||||
if (m_startDate.UniversalTime != value.UniversalTime)
|
||||
{
|
||||
m_startDate = value;
|
||||
RaisePropertyChanged("StartDate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Output Properties
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResult);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultAutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultInDays);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResult);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResultAutomationName);
|
||||
|
||||
COMMAND_FOR_METHOD(CopyCommand, DateCalculatorViewModel::OnCopyCommand);
|
||||
|
||||
void OnCopyCommand(Platform::Object^ parameter);
|
||||
|
||||
private:
|
||||
void OnPropertyChanged(_In_ Platform::String^ prop);
|
||||
void OnInputsChanged();
|
||||
void UpdateDisplayResult();
|
||||
void UpdateStrDateDiffResultAutomationName();
|
||||
void UpdateStrDateResultAutomationName();
|
||||
void InitializeDateOutputFormats(Platform::String^ calendarIdentifer);
|
||||
Platform::String^ GetDateDiffString() const;
|
||||
Platform::String^ GetDateDiffStringInDays() const;
|
||||
Platform::String^ GetLocalizedNumberString(int value) const;
|
||||
static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime);
|
||||
|
||||
static void CheckClipTimeSameDay(Windows::Globalization::Calendar^ reference);
|
||||
|
||||
property bool IsOutOfBound
|
||||
{
|
||||
bool get() { return m_isOutOfBound; }
|
||||
void set(bool value) { m_isOutOfBound = value; UpdateDisplayResult(); }
|
||||
}
|
||||
|
||||
property CalculatorApp::Common::DateCalculation::DateDifference DateDiffResult
|
||||
{
|
||||
CalculatorApp::Common::DateCalculation::DateDifference get() { return m_dateDiffResult; }
|
||||
void set(CalculatorApp::Common::DateCalculation::DateDifference value) { m_dateDiffResult = value; UpdateDisplayResult(); }
|
||||
}
|
||||
|
||||
property CalculatorApp::Common::DateCalculation::DateDifference DateDiffResultInDays
|
||||
{
|
||||
CalculatorApp::Common::DateCalculation::DateDifference get() { return m_dateDiffResultInDays; }
|
||||
void set(CalculatorApp::Common::DateCalculation::DateDifference value) { m_dateDiffResultInDays = value; UpdateDisplayResult(); }
|
||||
}
|
||||
|
||||
property Windows::Foundation::DateTime DateResult
|
||||
{
|
||||
Windows::Foundation::DateTime get() { return m_dateResult; }
|
||||
void set(Windows::Foundation::DateTime value) { m_dateResult = value; UpdateDisplayResult();}
|
||||
}
|
||||
|
||||
private:
|
||||
// Property variables
|
||||
bool m_isOutOfBound;
|
||||
Platform::Collections::Vector<Platform::String^>^ m_offsetValues;
|
||||
Windows::Foundation::DateTime m_fromDate;
|
||||
Windows::Foundation::DateTime m_toDate;
|
||||
Windows::Foundation::DateTime m_startDate;
|
||||
Windows::Foundation::DateTime m_dateResult;
|
||||
CalculatorApp::Common::DateCalculation::DateDifference m_dateDiffResult;
|
||||
CalculatorApp::Common::DateCalculation::DateDifference m_dateDiffResultInDays;
|
||||
|
||||
// Private members
|
||||
std::shared_ptr<CalculatorApp::Common::DateCalculation::DateCalculationEngine> m_dateCalcEngine;
|
||||
CalculatorApp::Common::DateCalculation::DateUnit m_daysOutputFormat;
|
||||
CalculatorApp::Common::DateCalculation::DateUnit m_allDateUnitsOutputFormat;
|
||||
Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ m_dateTimeFormatter;
|
||||
Platform::String^ m_listSeparator;
|
||||
};
|
||||
}
|
||||
}
|
||||
65
src/CalcViewModel/HistoryItemViewModel.cpp
Normal file
65
src/CalcViewModel/HistoryItemViewModel.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "HistoryItemViewModel.h"
|
||||
#include "Common\LocalizationService.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace std;
|
||||
using namespace Platform;
|
||||
|
||||
|
||||
HistoryItemViewModel::HistoryItemViewModel(String^ expression, String^ result,
|
||||
_In_ const shared_ptr<CalculatorVector <pair<wstring, int>>> &spTokens,
|
||||
_In_ const shared_ptr<CalculatorVector<shared_ptr<IExpressionCommand>>> &spCommands) :m_expression(expression), m_result(result), m_spTokens(spTokens), m_spCommands(spCommands)
|
||||
{
|
||||
// updating accessibility names for expression and result
|
||||
m_accExpression = HistoryItemViewModel::GetAccessibleExpressionFromTokens(spTokens, m_expression);
|
||||
m_accResult = LocalizationService::GetNarratorReadableString(m_result);
|
||||
}
|
||||
|
||||
String^ HistoryItemViewModel::GetAccessibleExpressionFromTokens(_In_ shared_ptr< CalculatorVector< pair< wstring, int > > > const &spTokens, _In_ String^ fallbackExpression)
|
||||
{
|
||||
// updating accessibility names for expression and result
|
||||
wstringstream accExpression{};
|
||||
accExpression << L"";
|
||||
|
||||
unsigned int nTokens;
|
||||
HRESULT hr = spTokens->GetSize(&nTokens);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pair<wstring, int> tokenItem;
|
||||
for (unsigned int i = 0; i < nTokens; i++)
|
||||
{
|
||||
hr = spTokens->GetAt(i, &tokenItem);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
wstring token = tokenItem.first;
|
||||
accExpression << LocalizationService::GetNarratorReadableToken(StringReference(token.c_str()))->Data();
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wstring expressionSuffix{};
|
||||
hr = spTokens->GetExpressionSuffix(&expressionSuffix);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
accExpression << expressionSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return LocalizationService::GetNarratorReadableString(fallbackExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ref new String(accExpression.str().c_str());
|
||||
}
|
||||
}
|
||||
90
src/CalcViewModel/HistoryItemViewModel.h
Normal file
90
src/CalcViewModel/HistoryItemViewModel.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class HistoryItemViewModel sealed : Windows::UI::Xaml::Data::ICustomPropertyProvider
|
||||
{
|
||||
|
||||
internal:
|
||||
|
||||
HistoryItemViewModel(Platform::String^ expression,
|
||||
Platform::String^ result,
|
||||
_In_ std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const &spTokens,
|
||||
_In_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &spCommands);
|
||||
|
||||
std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const& GetTokens()
|
||||
{
|
||||
return m_spTokens;
|
||||
}
|
||||
|
||||
std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const& GetCommands()
|
||||
{
|
||||
return m_spCommands;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
property Platform::String^ Expression
|
||||
{
|
||||
Platform::String^ get() { return m_expression; }
|
||||
}
|
||||
|
||||
property Platform::String^ AccExpression
|
||||
{
|
||||
Platform::String^ get() { return m_accExpression; }
|
||||
}
|
||||
|
||||
property Platform::String^ Result
|
||||
{
|
||||
Platform::String^ get() { return m_result; }
|
||||
}
|
||||
|
||||
property Platform::String^ AccResult
|
||||
{
|
||||
Platform::String^ get() { return m_accResult; }
|
||||
}
|
||||
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual property Windows::UI::Xaml::Interop::TypeName Type
|
||||
{
|
||||
Windows::UI::Xaml::Interop::TypeName get()
|
||||
{
|
||||
return this->GetType();
|
||||
}
|
||||
}
|
||||
|
||||
virtual Platform::String^ GetStringRepresentation()
|
||||
{
|
||||
return m_accExpression + " " + m_accResult;
|
||||
}
|
||||
|
||||
private:
|
||||
static Platform::String^ GetAccessibleExpressionFromTokens(
|
||||
_In_ std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> const &spTokens,
|
||||
_In_ Platform::String^ fallbackExpression);
|
||||
|
||||
private:
|
||||
Platform::String^ m_expression;
|
||||
Platform::String^ m_accExpression;
|
||||
Platform::String^ m_accResult;
|
||||
Platform::String^ m_result;
|
||||
std::shared_ptr<CalculatorVector <std::pair<std::wstring, int>>> m_spTokens;
|
||||
std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> m_spCommands;
|
||||
};
|
||||
}
|
||||
}
|
||||
373
src/CalcViewModel/HistoryViewModel.cpp
Normal file
373
src/CalcViewModel/HistoryViewModel.cpp
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "HistoryViewModel.h"
|
||||
#include "Common\LocalizationStringUtil.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using CalculatorApp::TraceLogger;
|
||||
|
||||
static StringReference HistoryVectorLengthKey{ L"HistoryVectorLength" };
|
||||
|
||||
namespace CalculatorApp::ViewModel::HistoryResourceKeys
|
||||
{
|
||||
StringReference HistoryCleared(L"HistoryList_Cleared");
|
||||
}
|
||||
|
||||
HistoryViewModel::HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager) :
|
||||
m_calculatorManager(calculatorManager),
|
||||
m_localizedHistoryCleared(nullptr)
|
||||
{
|
||||
AreHistoryShortcutsEnabled = true;
|
||||
|
||||
Items = ref new Platform::Collections::Vector<HistoryItemViewModel^>();
|
||||
ItemSize = 0;
|
||||
}
|
||||
|
||||
void HistoryViewModel::RestoreCompleteHistory()
|
||||
{
|
||||
RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_STD);
|
||||
RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_SCI);
|
||||
}
|
||||
|
||||
// this will reload Items with the history list based on current mode
|
||||
void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode)
|
||||
{
|
||||
if (currentMode == ViewMode::Standard)
|
||||
{
|
||||
m_currentMode = CalculationManager::CALCULATOR_MODE::CM_STD;
|
||||
}
|
||||
else if (currentMode == ViewMode::Scientific)
|
||||
{
|
||||
m_currentMode = CalculationManager::CALCULATOR_MODE::CM_SCI;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto historyListModel = m_calculatorManager->GetHistoryItems(m_currentMode);
|
||||
auto historyListVM = ref new Platform::Collections::Vector<HistoryItemViewModel^>();
|
||||
const auto& localizer = LocalizationSettings::GetInstance();
|
||||
if (historyListModel.size() > 0)
|
||||
{
|
||||
for (auto ritr = historyListModel.rbegin(); ritr != historyListModel.rend(); ++ritr)
|
||||
{
|
||||
wstring expression = (*ritr)->historyItemVector.expression;
|
||||
wstring result = (*ritr)->historyItemVector.result;
|
||||
localizer.LocalizeDisplayValue(&expression);
|
||||
localizer.LocalizeDisplayValue(&result);
|
||||
|
||||
auto item = ref new HistoryItemViewModel(ref new Platform::String( expression.c_str()),
|
||||
ref new Platform::String(result.c_str()),
|
||||
(*ritr)->historyItemVector.spTokens, (*ritr)->historyItemVector.spCommands);
|
||||
historyListVM->Append(item);
|
||||
}
|
||||
}
|
||||
|
||||
Items = historyListVM;
|
||||
UpdateItemSize();
|
||||
}
|
||||
|
||||
void HistoryViewModel::OnHistoryItemAdded(_In_ unsigned int addedItemIndex)
|
||||
{
|
||||
auto newItem = m_calculatorManager->GetHistoryItem(addedItemIndex);
|
||||
const auto& localizer = LocalizationSettings::GetInstance();
|
||||
wstring expression = newItem->historyItemVector.expression;
|
||||
wstring result = newItem->historyItemVector.result;
|
||||
localizer.LocalizeDisplayValue(&expression);
|
||||
localizer.LocalizeDisplayValue(&result);
|
||||
auto item = ref new HistoryItemViewModel(ref new Platform::String(expression.c_str()),
|
||||
ref new Platform::String(result.c_str()),
|
||||
newItem->historyItemVector.spTokens, newItem->historyItemVector.spCommands );
|
||||
|
||||
// check if we have not hit the max items
|
||||
if (Items->Size >= m_calculatorManager->MaxHistorySize())
|
||||
{
|
||||
// this means the item already exists
|
||||
Items->RemoveAt(Items->Size -1);
|
||||
}
|
||||
|
||||
assert(addedItemIndex <= m_calculatorManager->MaxHistorySize() && addedItemIndex >= 0);
|
||||
Items->InsertAt(0, item);
|
||||
UpdateItemSize();
|
||||
SaveHistory();
|
||||
}
|
||||
|
||||
void HistoryViewModel::SetCalculatorDisplay(CalculatorDisplay &calculatorDisplay)
|
||||
{
|
||||
WeakReference historyViewModel(this);
|
||||
calculatorDisplay.SetHistoryCallback(historyViewModel);
|
||||
}
|
||||
|
||||
void HistoryViewModel::ShowItem(_In_ HistoryItemViewModel^ e)
|
||||
{
|
||||
HistoryItemClicked(e);
|
||||
}
|
||||
|
||||
void HistoryViewModel::DeleteItem(_In_ HistoryItemViewModel^ e)
|
||||
{
|
||||
uint32_t itemIndex;
|
||||
if (Items->IndexOf(e, &itemIndex))
|
||||
{
|
||||
if (m_calculatorManager->RemoveHistoryItem(itemIndex))
|
||||
{
|
||||
// Keys for the history container are index based.
|
||||
// SaveHistory() re-inserts the items anyway, so it's faster to just clear out the container.
|
||||
CalculationManager::CALCULATOR_MODE currentMode = m_currentMode;
|
||||
ApplicationDataContainer^ historyContainer = GetHistoryContainer(currentMode);
|
||||
historyContainer->Values->Clear();
|
||||
|
||||
Items->RemoveAt(itemIndex);
|
||||
UpdateItemSize();
|
||||
SaveHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryViewModel::OnHideCommand(_In_ Platform::Object^ e)
|
||||
{
|
||||
// added at VM layer so that the views do not have to individually raise events
|
||||
HideHistoryClicked();
|
||||
}
|
||||
|
||||
void HistoryViewModel::OnClearCommand(_In_ Platform::Object^ e)
|
||||
{
|
||||
TraceLogger::GetInstance().LogClearHistory();
|
||||
if (AreHistoryShortcutsEnabled == true)
|
||||
{
|
||||
m_calculatorManager->ClearHistory();
|
||||
|
||||
if (Items->Size > 0)
|
||||
{
|
||||
CalculationManager::CALCULATOR_MODE currentMode = m_currentMode;
|
||||
ClearHistoryContainer(currentMode);
|
||||
Items->Clear();
|
||||
UpdateItemSize();
|
||||
}
|
||||
|
||||
MakeHistoryClearedNarratorAnnouncement(HistoryResourceKeys::HistoryCleared, m_localizedHistoryCleared);
|
||||
}
|
||||
}
|
||||
|
||||
// this method restores history vector per mode
|
||||
void HistoryViewModel::RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode)
|
||||
{
|
||||
ApplicationDataContainer^ historyContainer = GetHistoryContainer(cMode);
|
||||
std::shared_ptr<std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>>> historyVector = std::make_shared<std::vector<std::shared_ptr<CalculationManager::HISTORYITEM>>>();
|
||||
auto historyVectorLength = static_cast<int>(historyContainer->Values->Lookup(HistoryVectorLengthKey));
|
||||
bool failure = false;
|
||||
|
||||
if (historyVectorLength > 0)
|
||||
{
|
||||
for (int i = 0; i < historyVectorLength; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// deserialize each item
|
||||
auto item = DeserializeHistoryItem(i.ToString(), historyContainer);
|
||||
std::shared_ptr<CalculationManager::HISTORYITEM> Item = std::make_shared<CalculationManager::HISTORYITEM>(item);
|
||||
historyVector->push_back(Item);
|
||||
}
|
||||
catch (Platform::Exception^ e)
|
||||
{
|
||||
failure = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!failure)
|
||||
{
|
||||
// if task has been cancelled set history to 0
|
||||
m_calculatorManager->SetHistory(cMode, *historyVector);
|
||||
|
||||
// update length once again for consistency between stored number of items and length
|
||||
UpdateHistoryVectorLength(static_cast<int>(historyVector->size()), cMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in case of failure do not show any item
|
||||
UpdateHistoryVectorLength(0, cMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::String^ HistoryViewModel::GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode)
|
||||
{
|
||||
Platform::ValueType^ modeValue = static_cast<int>(cMode);
|
||||
return Platform::String::Concat(modeValue->ToString(), L"_History");
|
||||
}
|
||||
|
||||
ApplicationDataContainer^ HistoryViewModel::GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode)
|
||||
{
|
||||
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
|
||||
ApplicationDataContainer^ historyContainer;
|
||||
|
||||
// naming container based on mode
|
||||
Platform::String^ historyContainerKey = GetHistoryContainerKey(cMode);
|
||||
|
||||
if (localSettings->Containers->HasKey(historyContainerKey))
|
||||
{
|
||||
historyContainer = localSettings->Containers->Lookup(historyContainerKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// create container for adding data
|
||||
historyContainer = localSettings->CreateContainer(historyContainerKey, ApplicationDataCreateDisposition::Always);
|
||||
int initialHistoryVectorLength = 0;
|
||||
historyContainer->Values->Insert(HistoryVectorLengthKey, initialHistoryVectorLength);
|
||||
}
|
||||
|
||||
return historyContainer;
|
||||
}
|
||||
|
||||
void HistoryViewModel::ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode)
|
||||
{
|
||||
ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings;
|
||||
localSettings->DeleteContainer(GetHistoryContainerKey(cMode));
|
||||
}
|
||||
|
||||
// this method will be used to update the history item length
|
||||
void HistoryViewModel::UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode)
|
||||
{
|
||||
ApplicationDataContainer^ historyContainer = GetHistoryContainer(cMode);
|
||||
historyContainer->Values->Remove(HistoryVectorLengthKey);
|
||||
historyContainer->Values->Insert(HistoryVectorLengthKey, newValue);
|
||||
}
|
||||
|
||||
void HistoryViewModel::ClearHistory()
|
||||
{
|
||||
ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_STD);
|
||||
ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_SCI);
|
||||
}
|
||||
|
||||
void HistoryViewModel::SaveHistory()
|
||||
{
|
||||
ApplicationDataContainer^ historyContainer = GetHistoryContainer(m_currentMode);
|
||||
auto currentHistoryVector = m_calculatorManager->GetHistoryItems(m_currentMode);
|
||||
bool failure = false;
|
||||
int index = 0;
|
||||
Platform::String^ serializedHistoryItem;
|
||||
|
||||
for (auto iter = currentHistoryVector.begin(); iter != currentHistoryVector.end(); ++iter)
|
||||
{
|
||||
try
|
||||
{
|
||||
serializedHistoryItem = SerializeHistoryItem(*iter);
|
||||
historyContainer->Values->Insert(index.ToString(), serializedHistoryItem);
|
||||
}
|
||||
catch (Platform::Exception^)
|
||||
{
|
||||
failure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
if (!failure)
|
||||
{
|
||||
// insertion is successful
|
||||
UpdateHistoryVectorLength(static_cast<int>(currentHistoryVector.size()), m_currentMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateHistoryVectorLength(0, m_currentMode);
|
||||
}
|
||||
}
|
||||
|
||||
// this serializes a history item into a base64 encoded string
|
||||
Platform::String^ HistoryViewModel::SerializeHistoryItem(_In_ std::shared_ptr<CalculationManager::HISTORYITEM> const &item)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
DataWriter^ writer = ref new DataWriter();
|
||||
auto expr = item->historyItemVector.expression;
|
||||
auto result = item->historyItemVector.result;
|
||||
auto platformExpr = ref new Platform::String(expr.c_str());
|
||||
writer->WriteUInt32(writer->MeasureString(platformExpr));
|
||||
writer->WriteString(platformExpr);
|
||||
auto platformResult = ref new Platform::String(result.c_str());
|
||||
writer->WriteUInt32(writer->MeasureString(platformResult));
|
||||
writer->WriteString(platformResult);
|
||||
|
||||
Utils::SerializeCommandsAndTokens(item->historyItemVector.spTokens, item->historyItemVector.spCommands, writer);
|
||||
|
||||
IBuffer^ buffer = writer->DetachBuffer();
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL"));
|
||||
}
|
||||
|
||||
return CryptographicBuffer::EncodeToBase64String(buffer);
|
||||
}
|
||||
|
||||
CalculationManager::HISTORYITEM HistoryViewModel::DeserializeHistoryItem(_In_ Platform::String^ historyItemKey, _In_ ApplicationDataContainer^ historyContainer)
|
||||
{
|
||||
CalculationManager::HISTORYITEM historyItem;
|
||||
if (historyContainer->Values->HasKey(historyItemKey))
|
||||
{
|
||||
Object^ historyItemValues = historyContainer->Values->Lookup(historyItemKey);
|
||||
|
||||
if (historyItemValues == nullptr)
|
||||
{
|
||||
throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL"));
|
||||
}
|
||||
|
||||
String^ historyData = safe_cast<Platform::String^>(historyItemValues);
|
||||
IBuffer^ buffer = CryptographicBuffer::DecodeFromBase64String(historyData);
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL"));
|
||||
}
|
||||
|
||||
DataReader^ reader = DataReader::FromBuffer(buffer);
|
||||
auto exprLen = reader->ReadUInt32();
|
||||
auto expression = reader->ReadString(exprLen);
|
||||
historyItem.historyItemVector.expression = expression->Data();
|
||||
|
||||
auto resultLen = reader->ReadUInt32();
|
||||
auto result = reader->ReadString(resultLen);
|
||||
historyItem.historyItemVector.result = result->Data();
|
||||
|
||||
historyItem.historyItemVector.spCommands = Utils::DeserializeCommands(reader);
|
||||
historyItem.historyItemVector.spTokens = Utils::DeserializeTokens(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ref new Platform::Exception(E_ACCESSDENIED, ref new Platform::String(L"History Item not found"));
|
||||
}
|
||||
return historyItem;
|
||||
}
|
||||
|
||||
bool HistoryViewModel::IsValid(_In_ CalculationManager::HISTORYITEM item)
|
||||
{
|
||||
return (!item.historyItemVector.expression.empty() &&
|
||||
!item.historyItemVector.result.empty() &&
|
||||
(bool)item.historyItemVector.spCommands &&
|
||||
(bool)item.historyItemVector.spTokens);
|
||||
}
|
||||
|
||||
void HistoryViewModel::UpdateItemSize()
|
||||
{
|
||||
ItemSize = Items->Size;
|
||||
}
|
||||
|
||||
void HistoryViewModel::MakeHistoryClearedNarratorAnnouncement(String^ resourceKey, String^& formatVariable)
|
||||
{
|
||||
String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(resourceKey, formatVariable);
|
||||
|
||||
HistoryAnnouncement = CalculatorAnnouncement::GetHistoryClearedAnnouncement(announcement);
|
||||
}
|
||||
74
src/CalcViewModel/HistoryViewModel.h
Normal file
74
src/CalcViewModel/HistoryViewModel.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "Common\Automation\NarratorAnnouncement.h"
|
||||
#include "Common\CalculatorDisplay.h"
|
||||
#include "HistoryItemViewModel.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace CM = CalculationManager;
|
||||
|
||||
namespace ViewModel
|
||||
{
|
||||
public delegate void HideHistoryClickedHandler();
|
||||
public delegate void HistoryItemClickedHandler(CalculatorApp::ViewModel::HistoryItemViewModel^ e);
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class HistoryViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(int, ItemSize);
|
||||
OBSERVABLE_PROPERTY_RW(Windows::UI::Xaml::Interop::IBindableObservableVector^, Items);
|
||||
OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, HistoryAnnouncement);
|
||||
|
||||
void OnHistoryItemAdded(_In_ unsigned int addedItemIndex);
|
||||
|
||||
COMMAND_FOR_METHOD(HideCommand, HistoryViewModel::OnHideCommand);
|
||||
void OnHideCommand(_In_ Platform::Object^ e);
|
||||
COMMAND_FOR_METHOD(ClearCommand, HistoryViewModel::OnClearCommand);
|
||||
void OnClearCommand(_In_ Platform::Object^ e);
|
||||
|
||||
// events that are created
|
||||
event HideHistoryClickedHandler^ HideHistoryClicked;
|
||||
event HistoryItemClickedHandler^ HistoryItemClicked;
|
||||
void ShowItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel^ e);
|
||||
void ClearHistory();
|
||||
void RestoreCompleteHistory();
|
||||
|
||||
internal:
|
||||
HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager);
|
||||
void SetCalculatorDisplay(CalculatorDisplay &calculatorDisplay);
|
||||
void ReloadHistory(_In_ CalculatorApp::Common::ViewMode currentMode);
|
||||
|
||||
void DeleteItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel^ e);
|
||||
|
||||
// store history in app data functions
|
||||
Platform::String^ SerializeHistoryItem(_In_ std::shared_ptr<CalculationManager::HISTORYITEM> const &item);
|
||||
void SaveHistory();
|
||||
|
||||
private:
|
||||
CalculationManager::CalculatorManager* const m_calculatorManager;
|
||||
CalculatorDisplay m_calculatorDisplay;
|
||||
CalculationManager::CALCULATOR_MODE m_currentMode;
|
||||
Platform::String^ m_localizedHistoryCleared;
|
||||
|
||||
void RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode);
|
||||
CalculationManager::HISTORYITEM DeserializeHistoryItem(_In_ Platform::String^ historyItemKey, _In_ Windows::Storage::ApplicationDataContainer^ historyContainer);
|
||||
Windows::Storage::ApplicationDataContainer^ GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode);
|
||||
Platform::String^ GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode);
|
||||
void ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode);
|
||||
void UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode);
|
||||
bool IsValid(_In_ CalculationManager::HISTORYITEM item);
|
||||
|
||||
void MakeHistoryClearedNarratorAnnouncement(Platform::String^ resourceKey, Platform::String^& formatVariable);
|
||||
|
||||
friend class CalculatorDisplay;
|
||||
void UpdateItemSize();
|
||||
};
|
||||
}
|
||||
}
|
||||
33
src/CalcViewModel/MemoryItemViewModel.cpp
Normal file
33
src/CalcViewModel/MemoryItemViewModel.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "MemoryItemViewModel.h"
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Storage;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
void MemoryItemViewModel::Clear()
|
||||
{
|
||||
m_calcVM->OnMemoryClear(Position);
|
||||
};
|
||||
|
||||
void MemoryItemViewModel::MemoryAdd()
|
||||
{
|
||||
m_calcVM->OnMemoryAdd(Position);
|
||||
};
|
||||
|
||||
void MemoryItemViewModel::MemorySubtract()
|
||||
{
|
||||
m_calcVM->OnMemorySubtract(Position);
|
||||
};
|
||||
57
src/CalcViewModel/MemoryItemViewModel.h
Normal file
57
src/CalcViewModel/MemoryItemViewModel.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
ref class StandardCalculatorViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Model representation of a single item in the Memory list
|
||||
/// </summary>
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class MemoryItemViewModel sealed :
|
||||
public Windows::UI::Xaml::Data::INotifyPropertyChanged,
|
||||
Windows::UI::Xaml::Data::ICustomPropertyProvider
|
||||
{
|
||||
public:
|
||||
MemoryItemViewModel(StandardCalculatorViewModel^ calcVM) : m_Position(-1), m_calcVM(calcVM) {}
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(int, Position);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Value);
|
||||
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual property Windows::UI::Xaml::Interop::TypeName Type
|
||||
{
|
||||
Windows::UI::Xaml::Interop::TypeName get()
|
||||
{
|
||||
return this->GetType();
|
||||
}
|
||||
}
|
||||
|
||||
virtual Platform::String^ GetStringRepresentation()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
void Clear();
|
||||
void MemoryAdd();
|
||||
void MemorySubtract();
|
||||
|
||||
private:
|
||||
StandardCalculatorViewModel^ m_calcVM;
|
||||
};
|
||||
}
|
||||
}
|
||||
2010
src/CalcViewModel/StandardCalculatorViewModel.cpp
Normal file
2010
src/CalcViewModel/StandardCalculatorViewModel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
397
src/CalcViewModel/StandardCalculatorViewModel.h
Normal file
397
src/CalcViewModel/StandardCalculatorViewModel.h
Normal file
@@ -0,0 +1,397 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "Common\Automation\NarratorAnnouncement.h"
|
||||
#include "Common\DisplayExpressionToken.h"
|
||||
#include "Common\CalculatorDisplay.h"
|
||||
#include "HistoryViewModel.h"
|
||||
#include "MemoryItemViewModel.h"
|
||||
#include "Common\EngineResourceProvider.h"
|
||||
|
||||
namespace CalculatorFunctionalTests
|
||||
{
|
||||
class HistoryTests;
|
||||
}
|
||||
|
||||
namespace CalculatorUnitTests
|
||||
{
|
||||
class MultiWindowUnitTests;
|
||||
class TimerTests;
|
||||
}
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace WS = Windows::System;
|
||||
namespace CM = CalculationManager;
|
||||
|
||||
namespace ViewModel
|
||||
{
|
||||
#define ASCII_0 48
|
||||
public delegate void HideMemoryClickedHandler();
|
||||
public delegate void ProgModeRadixChangeHandler();
|
||||
namespace CalculatorViewModelProperties
|
||||
{
|
||||
extern Platform::StringReference IsMemoryEmpty;
|
||||
extern Platform::StringReference IsInError;
|
||||
extern Platform::StringReference BinaryDisplayValue;
|
||||
}
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
StandardCalculatorViewModel();
|
||||
void UpdateOperand(int pos, Platform::String^ text);
|
||||
void UpdatecommandsInRecordingMode();
|
||||
int GetBitLengthType();
|
||||
int GetNumberBase();
|
||||
|
||||
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, DisplayValue);
|
||||
OBSERVABLE_PROPERTY_RW(HistoryViewModel^, HistoryVM);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsInError);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsOperatorCommand);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, DisplayStringExpression);
|
||||
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IVector<Common::DisplayExpressionToken^>^, ExpressionTokens);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, DecimalDisplayValue);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, HexDisplayValue);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, OctalDisplayValue);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, BinaryDisplayValue);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, HexDisplayValue_AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, DecDisplayValue_AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, OctDisplayValue_AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, BinDisplayValue_AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsBinaryOperatorEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsUnaryOperatorEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsNegateEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsCurrentViewPinned);
|
||||
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IVector<MemoryItemViewModel^>^, MemorizedNumbers);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsMemoryEmpty);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsFToEChecked);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsFToEEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsHyperbolicChecked);
|
||||
OBSERVABLE_PROPERTY_RW(bool, AreHEXButtonsEnabled);
|
||||
NAMED_OBSERVABLE_PROPERTY_RW(Platform::String^, CalculationResultAutomationName);
|
||||
NAMED_OBSERVABLE_PROPERTY_RW(Platform::String^, CalculationExpressionAutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsShiftProgrammerChecked);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsQwordEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDwordEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsWordEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsByteEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, OpenParenthesisCount);
|
||||
OBSERVABLE_PROPERTY_RW(int, CurrentRadixType);
|
||||
OBSERVABLE_PROPERTY_RW(bool, AreTokensUpdated);
|
||||
OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, AreProgrammerRadixOperatorsEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement);
|
||||
|
||||
COMMAND_FOR_METHOD(CopyCommand, StandardCalculatorViewModel::OnCopyCommand);
|
||||
COMMAND_FOR_METHOD(PasteCommand, StandardCalculatorViewModel::OnPasteCommand);
|
||||
COMMAND_FOR_METHOD(ButtonPressed, StandardCalculatorViewModel::OnButtonPressed);
|
||||
COMMAND_FOR_METHOD(ClearMemoryCommand, StandardCalculatorViewModel::OnClearMemoryCommand);
|
||||
COMMAND_FOR_METHOD(PinUnpinAppBarButtonOnClicked, StandardCalculatorViewModel::OnPinUnpinCommand);
|
||||
COMMAND_FOR_METHOD(MemoryItemPressed, StandardCalculatorViewModel::OnMemoryItemPressed);
|
||||
COMMAND_FOR_METHOD(MemoryAdd, StandardCalculatorViewModel::OnMemoryAdd);
|
||||
COMMAND_FOR_METHOD(MemorySubtract, StandardCalculatorViewModel::OnMemorySubtract);
|
||||
|
||||
event HideMemoryClickedHandler^ HideMemoryClicked;
|
||||
event ProgModeRadixChangeHandler^ ProgModeRadixChange;
|
||||
|
||||
property bool IsShiftChecked {
|
||||
bool get() { return m_isShiftChecked; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isShiftChecked != value)
|
||||
{
|
||||
m_isShiftChecked = value;
|
||||
RaisePropertyChanged(L"IsShiftChecked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsBitFlipChecked {
|
||||
bool get() { return m_isBitFlipChecked; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isBitFlipChecked != value)
|
||||
{
|
||||
m_isBitFlipChecked = value;
|
||||
IsBinaryBitFlippingEnabled = IsProgrammer && m_isBitFlipChecked;
|
||||
AreProgrammerRadixOperatorsEnabled = IsProgrammer && !m_isBitFlipChecked;
|
||||
RaisePropertyChanged(L"IsBitFlipChecked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsBinaryBitFlippingEnabled {
|
||||
bool get() { return m_isBinaryBitFlippingEnabled; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isBinaryBitFlippingEnabled != value)
|
||||
{
|
||||
m_isBinaryBitFlippingEnabled = value;
|
||||
RaisePropertyChanged(L"IsBinaryBitFlippingEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsStandard {
|
||||
bool get() { return m_isStandard; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isStandard != value)
|
||||
{
|
||||
m_isStandard = value;
|
||||
if (value)
|
||||
{
|
||||
IsScientific = false;
|
||||
IsProgrammer = false;
|
||||
}
|
||||
RaisePropertyChanged(L"IsStandard");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsScientific {
|
||||
bool get() { return m_isScientific; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isScientific != value)
|
||||
{
|
||||
m_isScientific = value;
|
||||
if (value)
|
||||
{
|
||||
IsStandard = false;
|
||||
IsProgrammer = false;
|
||||
}
|
||||
RaisePropertyChanged(L"IsScientific");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsProgrammer {
|
||||
bool get() { return m_isProgrammer; }
|
||||
void set(bool value)
|
||||
{
|
||||
if (m_isProgrammer != value)
|
||||
{
|
||||
m_isProgrammer = value;
|
||||
if (!m_isProgrammer)
|
||||
{
|
||||
IsBitFlipChecked = false;
|
||||
}
|
||||
IsBinaryBitFlippingEnabled = m_isProgrammer && IsBitFlipChecked;
|
||||
AreProgrammerRadixOperatorsEnabled = m_isProgrammer && !IsBitFlipChecked;
|
||||
if (value)
|
||||
{
|
||||
IsStandard = false;
|
||||
IsScientific = false;
|
||||
}
|
||||
RaisePropertyChanged(L"IsProgrammer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsEditingEnabled {
|
||||
bool get() { return m_isEditingEnabled; }
|
||||
void set(bool value) {
|
||||
if (m_isEditingEnabled != value)
|
||||
{
|
||||
// Numbers::Common::KeyboardShortcutManager::IsCalculatorInEditingMode = value;
|
||||
m_isEditingEnabled = value;
|
||||
bool currentEditToggleValue = !m_isEditingEnabled;
|
||||
IsBinaryOperatorEnabled = currentEditToggleValue;
|
||||
IsUnaryOperatorEnabled = currentEditToggleValue;
|
||||
IsOperandEnabled = currentEditToggleValue;
|
||||
IsNegateEnabled = currentEditToggleValue;
|
||||
IsDecimalEnabled = currentEditToggleValue;
|
||||
RaisePropertyChanged(L"IsEditingEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsEngineRecording {
|
||||
bool get() { return m_standardCalculatorManager->IsEngineRecording(); }
|
||||
}
|
||||
|
||||
property bool IsOperandEnabled {
|
||||
bool get() { return m_isOperandEnabled; }
|
||||
void set(bool value) {
|
||||
if (m_isOperandEnabled != value)
|
||||
{
|
||||
m_isOperandEnabled = value;
|
||||
IsDecimalEnabled = value;
|
||||
AreHEXButtonsEnabled = IsProgrammer;
|
||||
IsFToEEnabled = value;
|
||||
RaisePropertyChanged(L"IsOperandEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property int TokenPosition
|
||||
{
|
||||
int get() { return m_tokenPosition; }
|
||||
void set(int value) { m_tokenPosition = value; }
|
||||
}
|
||||
|
||||
property Platform::String^ SelectedExpressionLastData
|
||||
{
|
||||
Platform::String^ get() { return m_selectedExpressionLastData; }
|
||||
void set(Platform::String^ value) { m_selectedExpressionLastData = value; }
|
||||
}
|
||||
|
||||
property bool KeyPressed
|
||||
{
|
||||
bool get() { return m_keyPressed; }
|
||||
void set(bool value) { m_keyPressed = value; }
|
||||
}
|
||||
|
||||
property bool IsOperandUpdatedUsingViewModel
|
||||
{
|
||||
bool get() { return m_operandUpdated; }
|
||||
void set(bool value) { m_operandUpdated = value; }
|
||||
}
|
||||
|
||||
property bool IsOperandTextCompletelySelected
|
||||
{
|
||||
bool get() { return m_completeTextSelection; }
|
||||
void set(bool value) { m_completeTextSelection = value; }
|
||||
}
|
||||
|
||||
property Platform::String^ LeftParenthesisAutomationName
|
||||
{
|
||||
Platform::String^ get()
|
||||
{
|
||||
return GetLeftParenthesisAutomationName();
|
||||
}
|
||||
}
|
||||
|
||||
internal:
|
||||
void OnPaste(Platform::String^ pastedString, CalculatorApp::Common::ViewMode mode);
|
||||
void OnCopyCommand(Platform::Object^ parameter);
|
||||
void OnPasteCommand(Platform::Object^ parameter);
|
||||
|
||||
NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate);
|
||||
|
||||
//Memory feature related methods. They are internal because they need to called from the MainPage code-behind
|
||||
void OnMemoryButtonPressed();
|
||||
void OnMemoryItemPressed(Platform::Object^ memoryItemPosition);
|
||||
void OnMemoryAdd(Platform::Object^ memoryItemPosition);
|
||||
void OnMemorySubtract(Platform::Object^ memoryItemPosition);
|
||||
void OnMemoryClear(_In_ Platform::Object^ memoryItemPosition);
|
||||
void OnPinUnpinCommand(Platform::Object^ parameter);
|
||||
|
||||
void SetPrimaryDisplay(_In_ std::wstring const&displayString, _In_ bool isError);
|
||||
void DisplayPasteError();
|
||||
void SetTokens(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens);
|
||||
void SetExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector<std::shared_ptr<IExpressionCommand>>> const &commands);
|
||||
void SetHistoryExpressionDisplay(_Inout_ std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> const &tokens, _Inout_ std::shared_ptr<CalculatorVector <std::shared_ptr<IExpressionCommand>>> const &commands);
|
||||
void SetParenthesisCount(_In_ const std::wstring& parenthesisCount);
|
||||
void OnMaxDigitsReached();
|
||||
void OnBinaryOperatorReceived();
|
||||
void OnMemoryItemChanged(unsigned int indexOfMemory);
|
||||
|
||||
Platform::Array<unsigned char>^ Serialize();
|
||||
void Deserialize(Platform::Array<unsigned char>^ state);
|
||||
|
||||
Platform::String^ GetLocalizedStringFormat(Platform::String^ format, Platform::String^ displayValue);
|
||||
void OnPropertyChanged(Platform::String^ propertyname);
|
||||
void SetCalculatorType(CalculatorApp::Common::ViewMode targetState);
|
||||
|
||||
Platform::String^ GetRawDisplayValue();
|
||||
void Recalculate(bool fromHistory = false);
|
||||
bool IsOperator(CalculationManager::Command cmdenum);
|
||||
void FtoEButtonToggled();
|
||||
void SwitchProgrammerModeBase(RADIX_TYPE calculatorBase);
|
||||
void SetMemorizedNumbersString();
|
||||
void SwitchAngleType(NumbersAndOperatorsEnum num);
|
||||
void ResetDisplay();
|
||||
RADIX_TYPE GetCurrentRadixType() { return (RADIX_TYPE)m_CurrentRadixType; }
|
||||
void SetPrecision(int32_t precision);
|
||||
void UpdateMaxIntDigits() { m_standardCalculatorManager->UpdateMaxIntDigits(); }
|
||||
NumbersAndOperatorsEnum GetCurrentAngleType() { return m_CurrentAngleType; }
|
||||
|
||||
private:
|
||||
void SetMemorizedNumbers(const std::vector<std::wstring>& memorizedNumbers);
|
||||
void UpdateProgrammerPanelDisplay();
|
||||
void HandleUpdatedOperandData(CalculationManager::Command cmdenum);
|
||||
NumbersAndOperatorsEnum ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter);
|
||||
NumbersAndOperatorsEnum m_CurrentAngleType;
|
||||
wchar_t m_decimalSeparator;
|
||||
CalculatorDisplay m_calculatorDisplay;
|
||||
CalculatorApp::EngineResourceProvider m_resourceProvider;
|
||||
std::unique_ptr<CalculationManager::CalculatorManager> m_standardCalculatorManager;
|
||||
Platform::String^ m_expressionAutomationNameFormat;
|
||||
Platform::String^ m_localizedCalculationResultAutomationFormat;
|
||||
Platform::String^ m_localizedCalculationResultDecimalAutomationFormat;
|
||||
Platform::String^ m_localizedHexaDecimalAutomationFormat;
|
||||
Platform::String^ m_localizedDecimalAutomationFormat;
|
||||
Platform::String^ m_localizedOctalAutomationFormat;
|
||||
Platform::String^ m_localizedBinaryAutomationFormat;
|
||||
Platform::String^ m_localizedMaxDigitsReachedAutomationFormat;
|
||||
Platform::String^ m_localizedButtonPressFeedbackAutomationFormat;
|
||||
Platform::String^ m_localizedMemorySavedAutomationFormat;
|
||||
Platform::String^ m_localizedMemoryItemChangedAutomationFormat;
|
||||
Platform::String^ m_localizedMemoryItemClearedAutomationFormat;
|
||||
Platform::String^ m_localizedMemoryCleared;
|
||||
|
||||
bool m_pinned;
|
||||
bool m_isOperandEnabled;
|
||||
bool m_isEditingEnabled;
|
||||
bool m_isStandard;
|
||||
bool m_isScientific;
|
||||
bool m_isProgrammer;
|
||||
bool m_isBinaryBitFlippingEnabled;
|
||||
bool m_isBitFlipChecked;
|
||||
bool m_isShiftChecked;
|
||||
bool m_isRtlLanguage;
|
||||
int m_tokenPosition;
|
||||
bool m_keyPressed;
|
||||
bool m_operandUpdated;
|
||||
bool m_completeTextSelection;
|
||||
bool m_isLastOperationHistoryLoad;
|
||||
Platform::String^ m_selectedExpressionLastData;
|
||||
Common::DisplayExpressionToken^ m_selectedExpressionToken;
|
||||
Platform::String^ m_leftParenthesisAutomationFormat;
|
||||
|
||||
Platform::String^ LocalizeDisplayValue(_In_ std::wstring const &displayValue, _In_ bool isError);
|
||||
Platform::String^ CalculateNarratorDisplayValue(_In_ std::wstring const &displayValue, _In_ Platform::String^ localizedDisplayValue, _In_ bool isError);
|
||||
CalculatorApp::Common::Automation::NarratorAnnouncement^ GetDisplayUpdatedNarratorAnnouncement();
|
||||
Platform::String^ GetCalculatorExpressionAutomationName();
|
||||
Platform::String^ GetNarratorStringReadRawNumbers(_In_ Platform::String^ localizedDisplayValue);
|
||||
|
||||
CalculationManager::Command ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation);
|
||||
void DisableButtons(CalculationManager::CommandType selectedExpressionCommandType);
|
||||
Platform::String^ GetLeftParenthesisAutomationName();
|
||||
|
||||
Platform::String^ m_feedbackForButtonPress;
|
||||
void OnButtonPressed(Platform::Object^ parameter);
|
||||
void OnClearMemoryCommand(Platform::Object^ parameter);
|
||||
std::wstring AddPadding(std::wstring);
|
||||
size_t LengthWithoutPadding(std::wstring);
|
||||
|
||||
std::shared_ptr<CalculatorVector<std::pair<std::wstring, int>>> m_tokens;
|
||||
std::shared_ptr<CalculatorVector <std::shared_ptr<IExpressionCommand>>> m_commands;
|
||||
|
||||
// Token types
|
||||
bool IsUnaryOp(int nOpCode);
|
||||
bool IsBinOp(int nOpcode);
|
||||
bool IsTrigOp(int nOpCode);
|
||||
bool IsOpnd(int nOpCode);
|
||||
bool IsRecoverableCommand(int nOpCode);
|
||||
|
||||
CalculationManager::CommandType GetSelectedTokenType(_In_ unsigned int);
|
||||
void SaveEditedCommand(_In_ unsigned int index, _In_ CalculationManager::Command command);
|
||||
|
||||
bool IsViewPinned();
|
||||
void SetViewPinnedState(bool pinned);
|
||||
|
||||
friend class CalculatorDisplay;
|
||||
friend class CalculatorFunctionalTests::HistoryTests;
|
||||
friend class CalculatorUnitTests::MultiWindowUnitTests;
|
||||
friend class CalculatorUnitTests::TimerTests;
|
||||
};
|
||||
}
|
||||
}
|
||||
1092
src/CalcViewModel/UnitConverterViewModel.cpp
Normal file
1092
src/CalcViewModel/UnitConverterViewModel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
420
src/CalcViewModel/UnitConverterViewModel.h
Normal file
420
src/CalcViewModel/UnitConverterViewModel.h
Normal file
@@ -0,0 +1,420 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common\NetworkManager.h"
|
||||
#include "Common\Automation\NarratorAnnouncement.h"
|
||||
#include "Common\ConversionResultTaskHelper.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewModel
|
||||
{
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class Category sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
Category(const UnitConversionManager::Category& category) :
|
||||
m_original(category)
|
||||
{ }
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
|
||||
property Platform::String^ Name
|
||||
{
|
||||
Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); }
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility NegateVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return m_original.supportsNegative
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
internal:
|
||||
const UnitConversionManager::Category& GetModelCategory() const { return m_original; }
|
||||
|
||||
private:
|
||||
const UnitConversionManager::Category m_original;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class Unit sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
Unit(const UnitConversionManager::Unit& unit) :
|
||||
m_original(unit)
|
||||
{ }
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
|
||||
property Platform::String^ Name
|
||||
{
|
||||
Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); }
|
||||
}
|
||||
|
||||
property Platform::String^ AccessibleName
|
||||
{
|
||||
Platform::String^ get() { return ref new Platform::String(m_original.accessibleName.c_str()); }
|
||||
}
|
||||
|
||||
property Platform::String^ Abbreviation
|
||||
{
|
||||
Platform::String^ get() { return ref new Platform::String(m_original.abbreviation.c_str()); }
|
||||
}
|
||||
|
||||
// This method is used to return the desired autonamtion name for default unit in UnitConveter combo box.
|
||||
Platform::String^ ToString() override
|
||||
{
|
||||
return AccessibleName;
|
||||
}
|
||||
|
||||
internal:
|
||||
const UnitConversionManager::Unit& GetModelUnit() const { return m_original; }
|
||||
|
||||
private:
|
||||
const UnitConversionManager::Unit m_original;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class SupplementaryResult sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
SupplementaryResult(Platform::String^ value, Unit^ unit) :
|
||||
m_Value(value),
|
||||
m_Unit(unit)
|
||||
{}
|
||||
|
||||
bool IsWhimsical() const
|
||||
{
|
||||
return m_Unit->GetModelUnit().isWhimsical;
|
||||
}
|
||||
|
||||
Platform::String^ GetLocalizedAutomationName();
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
|
||||
OBSERVABLE_PROPERTY_R(Platform::String^, Value);
|
||||
OBSERVABLE_PROPERTY_R(CalculatorApp::ViewModel::Unit^, Unit);
|
||||
};
|
||||
|
||||
interface class IActivatable
|
||||
{
|
||||
virtual property bool IsActive;
|
||||
};
|
||||
|
||||
template<typename TActivatable>
|
||||
ref class Activatable sealed : public IActivatable
|
||||
{
|
||||
private:
|
||||
TActivatable m_activatable;
|
||||
|
||||
public:
|
||||
Activatable(TActivatable activatable) : m_activatable(activatable)
|
||||
{ }
|
||||
|
||||
virtual property bool IsActive
|
||||
{
|
||||
bool get() { return m_activatable->IsActive; }
|
||||
void set(bool value) { m_activatable->IsActive = value; }
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TActivatable>
|
||||
IActivatable^ AsActivatable(TActivatable activatable)
|
||||
{
|
||||
return ref new Activatable<TActivatable>(activatable);
|
||||
}
|
||||
|
||||
namespace UnitConverterViewModelProperties
|
||||
{
|
||||
extern Platform::StringReference CurrentCategory;
|
||||
extern Platform::StringReference Unit1;
|
||||
extern Platform::StringReference Unit2;
|
||||
extern Platform::StringReference Value1Active;
|
||||
extern Platform::StringReference Value2Active;
|
||||
extern Platform::StringReference SupplementaryVisibility;
|
||||
extern Platform::StringReference SupplementaryResults;
|
||||
extern Platform::StringReference CurrencySymbol1;
|
||||
extern Platform::StringReference CurrencySymbol2;
|
||||
extern Platform::StringReference CurrencySymbolVisibility;
|
||||
extern Platform::StringReference CurrencyRatioEquality;
|
||||
extern Platform::StringReference NetworkBehavior;
|
||||
extern Platform::StringReference CurrencyDataLoadFailed;
|
||||
extern Platform::StringReference CurrencyDataIsWeekOld;
|
||||
extern Platform::StringReference IsCurrencyLoadingVisible;
|
||||
}
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable]
|
||||
public ref class UnitConverterViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
internal:
|
||||
UnitConverterViewModel(const std::shared_ptr<UnitConversionManager::IUnitConverter>& model);
|
||||
|
||||
public:
|
||||
OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged);
|
||||
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<Category^>^, Categories);
|
||||
OBSERVABLE_PROPERTY_RW(Category^, CurrentCategory);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, Mode);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<Unit^>^, Units);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol1);
|
||||
OBSERVABLE_PROPERTY_RW(Unit^, Unit1);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Value1);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol2);
|
||||
OBSERVABLE_PROPERTY_RW(Unit^, Unit2);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Value2);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<SupplementaryResult^>^, SupplementaryResults);
|
||||
OBSERVABLE_PROPERTY_RW(bool, Value1Active);
|
||||
OBSERVABLE_PROPERTY_RW(bool, Value2Active);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Value1AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Value2AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Unit1AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, Unit2AutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDropDownOpen);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsDropDownEnabled);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsCurrencyLoadingVisible);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsCurrencyCurrentCategory);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEquality);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEqualityAutomationName);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyTimestamp);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::NetworkAccessBehavior, NetworkBehavior);
|
||||
OBSERVABLE_PROPERTY_RW(bool, CurrencyDataLoadFailed);
|
||||
OBSERVABLE_PROPERTY_RW(bool, CurrencyDataIsWeekOld);
|
||||
|
||||
property Windows::UI::Xaml::Visibility SupplementaryVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return SupplementaryResults->Size > 0
|
||||
? Windows::UI::Xaml::Visibility::Visible
|
||||
: Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
property Windows::UI::Xaml::Visibility CurrencySymbolVisibility
|
||||
{
|
||||
Windows::UI::Xaml::Visibility get()
|
||||
{
|
||||
return (CurrencySymbol1->IsEmpty() || CurrencySymbol2->IsEmpty())
|
||||
? Windows::UI::Xaml::Visibility::Collapsed
|
||||
: Windows::UI::Xaml::Visibility::Visible;
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_FOR_METHOD(CategoryChanged, UnitConverterViewModel::OnCategoryChanged);
|
||||
COMMAND_FOR_METHOD(UnitChanged, UnitConverterViewModel::OnUnitChanged);
|
||||
COMMAND_FOR_METHOD(SwitchActive, UnitConverterViewModel::OnSwitchActive);
|
||||
COMMAND_FOR_METHOD(ButtonPressed, UnitConverterViewModel::OnButtonPressed);
|
||||
COMMAND_FOR_METHOD(CopyCommand, UnitConverterViewModel::OnCopyCommand);
|
||||
COMMAND_FOR_METHOD(PasteCommand, UnitConverterViewModel::OnPasteCommand);
|
||||
|
||||
void AnnounceConversionResult();
|
||||
|
||||
internal:
|
||||
void ResetView();
|
||||
void PopulateData();
|
||||
NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate);
|
||||
void DisplayPasteError();
|
||||
void OnValueActivated(IActivatable^ control);
|
||||
void OnPaste(Platform::String^ stringToPaste, CalculatorApp::Common::ViewMode mode);
|
||||
|
||||
void OnCopyCommand(Platform::Object^ parameter);
|
||||
void OnPasteCommand(Platform::Object^ parameter);
|
||||
|
||||
Platform::String^ GetLocalizedAutomationName(_In_ Platform::String^ displayvalue, _In_ Platform::String^ unitname, _In_ Platform::String^ format);
|
||||
Platform::String^ GetLocalizedConversionResultStringFormat(_In_ Platform::String^ fromValue, _In_ Platform::String^ fromUnit, _In_ Platform::String^ toValue, _In_ Platform::String^ toUnit);
|
||||
void UpdateValue1AutomationName();
|
||||
void UpdateValue2AutomationName();
|
||||
Platform::String^ Serialize();
|
||||
void Deserialize(Platform::String^ state);
|
||||
|
||||
//Saving And Restoring User Prefernces of Category and Associated-Units across Sessions.
|
||||
void SaveUserPreferences();
|
||||
void RestoreUserPreferences();
|
||||
|
||||
void OnCurrencyDataLoadFinished(bool didLoad);
|
||||
void OnCurrencyTimestampUpdated(_In_ const std::wstring& timestamp, bool isWeekOld);
|
||||
void RefreshCurrencyRatios();
|
||||
void OnNetworkBehaviorChanged(_In_ CalculatorApp::NetworkAccessBehavior newBehavior);
|
||||
|
||||
const std::wstring& GetValueFromUnlocalized() const
|
||||
{
|
||||
return m_valueFromUnlocalized;
|
||||
}
|
||||
const std::wstring& GetValueToUnlocalized() const
|
||||
{
|
||||
return m_valueToUnlocalized;
|
||||
}
|
||||
|
||||
// used by UnitConverterVMCallback
|
||||
void UpdateDisplay(const std::wstring& from, const std::wstring& to);
|
||||
void UpdateSupplementaryResults(const std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>>& suggestedValues);
|
||||
void OnMaxDigitsReached();
|
||||
|
||||
void BuildUnitList(const std::vector<UnitConversionManager::Unit>& modelUnitList);
|
||||
Unit^ FindUnitInList(UnitConversionManager::Unit target);
|
||||
void SetSelectedUnits();
|
||||
|
||||
private:
|
||||
void InitializeView();
|
||||
void OnPropertyChanged(Platform::String^ prop);
|
||||
void OnCategoryChanged(Platform::Object^ unused);
|
||||
void OnUnitChanged(Platform::Object^ unused);
|
||||
void OnSwitchActive(Platform::Object^ unused);
|
||||
UnitConversionManager::Command CommandFromButtonId(CalculatorApp::NumbersAndOperatorsEnum button);
|
||||
void SupplementaryResultsTimerTick(Windows::System::Threading::ThreadPoolTimer^ timer);
|
||||
void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer^ timer);
|
||||
void RefreshSupplementaryResults();
|
||||
void UpdateInputBlocked(_In_ const std::wstring& currencyInput);
|
||||
bool UnitsAreValid();
|
||||
|
||||
void OnButtonPressed(Platform::Object^ parameter);
|
||||
Platform::String^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings);
|
||||
|
||||
void StartConversionResultTimer();
|
||||
|
||||
std::shared_ptr<UnitConversionManager::IUnitConverter> m_model;
|
||||
wchar_t m_decimalSeparator;
|
||||
|
||||
enum class ConversionParameter
|
||||
{
|
||||
Source,
|
||||
Target
|
||||
} m_value1cp;
|
||||
property Platform::String^ ValueFrom
|
||||
{
|
||||
Platform::String^ get() { return m_value1cp == ConversionParameter::Source ? Value1 : Value2; }
|
||||
void set(Platform::String^ value) { m_value1cp == ConversionParameter::Source ? Value1 = value : Value2 = value; }
|
||||
}
|
||||
property Unit^ UnitFrom
|
||||
{
|
||||
Unit^ get() { return m_value1cp == ConversionParameter::Source ? Unit1 : Unit2; }
|
||||
void set(Unit^ value) { m_value1cp == ConversionParameter::Source ? Unit1 = value : Unit2 = value; }
|
||||
}
|
||||
property Platform::String^ ValueTo
|
||||
{
|
||||
Platform::String^ get() { return m_value1cp == ConversionParameter::Target ? Value1 : Value2; }
|
||||
void set(Platform::String^ value) { m_value1cp == ConversionParameter::Target ? Value1 = value : Value2 = value; }
|
||||
}
|
||||
property Unit^ UnitTo
|
||||
{
|
||||
Unit^ get() { return m_value1cp == ConversionParameter::Target ? Unit1 : Unit2; }
|
||||
void set(Unit^ value) { m_value1cp == ConversionParameter::Target ? Unit1 = value : Unit2 = value; }
|
||||
}
|
||||
void SwitchConversionParameters()
|
||||
{
|
||||
m_value1cp = m_value1cp == ConversionParameter::Source ? ConversionParameter::Target : ConversionParameter::Source;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isInputBlocked;
|
||||
Windows::System::Threading::ThreadPoolTimer^ m_supplementaryResultsTimer;
|
||||
bool m_resettingTimer;
|
||||
std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>> m_cachedSuggestedValues;
|
||||
std::mutex m_cacheMutex;
|
||||
Windows::Globalization::NumberFormatting::DecimalFormatter^ m_decimalFormatter;
|
||||
Windows::Globalization::NumberFormatting::CurrencyFormatter^ m_currencyFormatter;
|
||||
int m_currencyMaxFractionDigits;
|
||||
std::wstring m_valueFromUnlocalized;
|
||||
std::wstring m_valueToUnlocalized;
|
||||
bool m_relocalizeStringOnSwitch;
|
||||
// For Saving the User Preferences only if the Unit convertyer ViewModel is initialised for the first time
|
||||
bool m_IsFirstTime;
|
||||
|
||||
Platform::String^ m_localizedValueFromFormat;
|
||||
Platform::String^ m_localizedValueFromDecimalFormat;
|
||||
Platform::String^ m_localizedValueToFormat;
|
||||
Platform::String^ m_localizedConversionResultFormat;
|
||||
Platform::String^ m_localizedInputUnitName;
|
||||
Platform::String^ m_localizedOutputUnitName;
|
||||
|
||||
bool m_isValue1Updating;
|
||||
bool m_isValue2Updating;
|
||||
std::wstring m_lastAnnouncedFrom;
|
||||
std::wstring m_lastAnnouncedTo;
|
||||
Platform::String^ m_lastAnnouncedConversionResult;
|
||||
|
||||
bool m_isCurrencyDataLoaded;
|
||||
|
||||
std::unique_ptr<CalculatorApp::Common::ConversionResultTaskHelper> m_conversionResultTaskHelper;
|
||||
};
|
||||
|
||||
class UnitConverterVMCallback : public UnitConversionManager::IUnitConverterVMCallback
|
||||
{
|
||||
public:
|
||||
UnitConverterVMCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel)
|
||||
{}
|
||||
|
||||
void DisplayCallback(const std::wstring& from, const std::wstring& to) override
|
||||
{
|
||||
m_viewModel->UpdateDisplay(from, to);
|
||||
}
|
||||
|
||||
void SuggestedValueCallback(
|
||||
const std::vector<std::tuple<std::wstring, UnitConversionManager::Unit>>& suggestedValues) override
|
||||
{
|
||||
m_viewModel->UpdateSupplementaryResults(suggestedValues);
|
||||
}
|
||||
|
||||
void MaxDigitsReached()
|
||||
{
|
||||
m_viewModel->OnMaxDigitsReached();
|
||||
}
|
||||
|
||||
private:
|
||||
UnitConverterViewModel^ m_viewModel;
|
||||
};
|
||||
|
||||
class ViewModelCurrencyCallback : public UnitConversionManager::IViewModelCurrencyCallback
|
||||
{
|
||||
public:
|
||||
ViewModelCurrencyCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel)
|
||||
{}
|
||||
|
||||
void CurrencyDataLoadFinished(bool didLoad) override
|
||||
{
|
||||
m_viewModel->OnCurrencyDataLoadFinished(didLoad);
|
||||
}
|
||||
|
||||
void CurrencySymbolsCallback(const std::wstring& symbol1, const std::wstring& symbol2) override
|
||||
{
|
||||
Platform::String^ sym1 = Platform::StringReference(symbol1.c_str());
|
||||
Platform::String^ sym2 = Platform::StringReference(symbol2.c_str());
|
||||
|
||||
bool value1Active = m_viewModel->Value1Active;
|
||||
m_viewModel->CurrencySymbol1 = value1Active ? sym1 : sym2;
|
||||
m_viewModel->CurrencySymbol2 = value1Active ? sym2 : sym1;
|
||||
}
|
||||
|
||||
void CurrencyRatiosCallback(_In_ const std::wstring& ratioEquality, _In_ const std::wstring& accRatioEquality) override
|
||||
{
|
||||
m_viewModel->CurrencyRatioEquality = ref new Platform::String(ratioEquality.c_str());
|
||||
m_viewModel->CurrencyRatioEqualityAutomationName = ref new Platform::String(accRatioEquality.c_str());
|
||||
}
|
||||
|
||||
void CurrencyTimestampCallback(_In_ const std::wstring& timestamp, bool isWeekOld) override
|
||||
{
|
||||
m_viewModel->OnCurrencyTimestampUpdated(timestamp, isWeekOld);
|
||||
}
|
||||
|
||||
void NetworkBehaviorChanged(_In_ int newBehavior) override
|
||||
{
|
||||
m_viewModel->OnNetworkBehaviorChanged(static_cast<CalculatorApp::NetworkAccessBehavior>(newBehavior));
|
||||
}
|
||||
|
||||
private:
|
||||
UnitConverterViewModel^ m_viewModel;
|
||||
};
|
||||
}
|
||||
}
|
||||
19
src/CalcViewModel/ViewState.cpp
Normal file
19
src/CalcViewModel/ViewState.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ViewState.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewState
|
||||
{
|
||||
Platform::StringReference Snap(L"Snap");
|
||||
Platform::StringReference DockedView(L"DockedView");
|
||||
|
||||
bool IsValidViewState(Platform::String^ viewState)
|
||||
{
|
||||
return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/CalcViewModel/ViewState.h
Normal file
15
src/CalcViewModel/ViewState.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewState
|
||||
{
|
||||
extern Platform::StringReference Snap;
|
||||
extern Platform::StringReference DockedView;
|
||||
|
||||
bool IsValidViewState(Platform::String^ viewState);
|
||||
}
|
||||
}
|
||||
4
src/CalcViewModel/packages.config
Normal file
4
src/CalcViewModel/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.UI.Xaml" version="2.0.181018003.1" targetFramework="native" />
|
||||
</packages>
|
||||
5
src/CalcViewModel/pch.cpp
Normal file
5
src/CalcViewModel/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
61
src/CalcViewModel/pch.h
Normal file
61
src/CalcViewModel/pch.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <collection.h>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <ppltasks.h>
|
||||
#include <pplawait.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cassert>
|
||||
#include <locale>
|
||||
#include <sal.h>
|
||||
#include <sstream>
|
||||
#include <concrt.h>
|
||||
#include <regex>
|
||||
|
||||
// C++\WinRT Headers
|
||||
#include "winrt\base.h"
|
||||
#include "winrt\Windows.Foundation.Diagnostics.h"
|
||||
#include "winrt\Windows.Globalization.h"
|
||||
#include "winrt\Windows.Globalization.DateTimeFormatting.h"
|
||||
#include "winrt\Windows.System.UserProfile.h"
|
||||
#include "winrt\Windows.UI.Xaml.h"
|
||||
|
||||
// The following namespaces exist as a convenience to resolve
|
||||
// ambiguity for Windows types in the Windows::UI::Xaml::Automation::Peers
|
||||
// namespace that only exist on RS3.
|
||||
// Once the app switches to min version RS3, the namespaces can be removed.
|
||||
// TODO - MSFT 12735088
|
||||
namespace StandardPeers = Windows::UI::Xaml::Automation::Peers;
|
||||
namespace CalculatorApp::Common::Automation {}
|
||||
namespace CustomPeers = CalculatorApp::Common::Automation;
|
||||
|
||||
// CalcManager Headers
|
||||
#include "CalcManager\CalculatorVector.h"
|
||||
#include "CalcManager\ExpressionCommand.h"
|
||||
#include "CalcManager\CalculatorResource.h"
|
||||
#include "CalcManager\CalculatorManager.h"
|
||||
#include "CalcManager\UnitConverter.h"
|
||||
|
||||
// Project Headers
|
||||
#include "Common\DelegateCommand.h"
|
||||
#include "Common\Utils.h"
|
||||
#include "Common\MyVirtualKey.h"
|
||||
#include "Common\NavCategory.h"
|
||||
#include "Common\TraceLogger.h"
|
||||
#include "Common\CalculatorButtonUser.h"
|
||||
|
||||
12
src/CalcViewModel/targetver.h
Normal file
12
src/CalcViewModel/targetver.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
|
||||
Reference in New Issue
Block a user