Hello GitHub

This commit is contained in:
Howard Wolosky
2019-01-28 16:24:37 -08:00
parent 456fe5e355
commit c13b8a099e
822 changed files with 276650 additions and 75 deletions

View 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();
}

View 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;
};
}
}

View 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>

View 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>

View 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;
}
};
}}

View 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);
}

View 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;
};
}

View File

@@ -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);
};
}

View 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);
}
}

View 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;
};
}

View 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);
}

View 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);
};
}

View File

@@ -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();
}

View File

@@ -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;
};
}

View 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));
}
}

View 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;
};
}

View 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;
}

View 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;
};
}

View 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();
}

View 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);
};
}
}

View File

@@ -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;
}
}

View 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);
};
}
}

View 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,
};
}

View 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);
}
}
}

View 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;
};
}

View 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)

View 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;
};
}
}

View 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;
}

View 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;
};
}

View 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();
}

View 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);
};
}
}
}

View 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));
}
}
}

View 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;
};
}

View 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();
}
}

View 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;
};
}

View 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);
}

View 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();
};
}
}

View 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 &paraCmd)
{
int parenthesisCmd = paraCmd.GetCommand();
m_dataWriter->WriteInt32(parenthesisCmd);
}

View 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 &paraCmd);
private:
Windows::Storage::Streams::DataWriter^ m_dataWriter;
};
}
}

View 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);
}

View 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);
};
}
}

View 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());
}

View 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;
};
}}

View 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)&currencyTrailingDigits,
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)&currencySymbolPrecedence,
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;
};
}
}

View 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);
}
};
}
}

View 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
};
}
}

View 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;
}

View 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);
};
}
}

View 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;
}

View 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;
};
}

View 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;
};
}

View 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);
}
}

View 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;
};
}

View 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;
}

View 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;
}
}

View 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;
}
};
}}

View 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,
&currencyCode,
&currencyName,
&currencySymbol
};
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()));
}
}

View 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;
};
}
}

View 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);
}

View 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;
};
}
}

View 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"
}
}

View 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;
};
}
}

View 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
};
}
}

View 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)));
}
}
}

View 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;
};
}
}

View 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();
}

View 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;
};
}
}

View 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());
}
}

View 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;
};
}
}

View 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);
}

View 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();
};
}
}

View 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);
};

View 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;
};
}
}

File diff suppressed because it is too large Load Diff

View 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;
};
}
}

File diff suppressed because it is too large Load Diff

View 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;
};
}
}

View 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);
}
}
}

View 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);
}
}

View 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>

View 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
View 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"

View 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>