Merge branch 'master' into Fix605

This commit is contained in:
Pepe Rivera 2020-12-16 14:12:06 -08:00 committed by GitHub
commit 8505aa99aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
509 changed files with 80655 additions and 21725 deletions

9
.gitignore vendored
View file

@ -291,6 +291,9 @@ __pycache__/
Generated Files/
src/GraphControl/GraphingImplOverrides.props
src/CalcViewModel/DataLoaders/DataLoaderConstants.h
!/build/config/TRexDefs/**
!src/Calculator/TemporaryKey.pfx
!src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx
!src/Calculator/WindowsDev_TemporaryKey.pfx
!src/CalculatorUnitTests/WindowsDev_TemporaryKey.pfx
!src/x64
!src/x86
!src/out
!src/ARM

View file

@ -51,6 +51,10 @@ We also welcome [issues submitted on GitHub](https://github.com/Microsoft/calcul
## Roadmap
For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md).
### Graphing Mode
Adding graphing calculator functionality [is on the project roadmap](https://github.com/Microsoft/calculator/issues/338) and we hope that this project can create a great end-user experience around graphing. To that end, the UI from the official in-box Windows Calculator is currently part of this repository, although the proprietary Microsoft-built graphing engine, which also drives graphing in Microsoft Mathematics and OneNote, is not. Community members can still be involved in the creation of the UI, however developer builds will not have graphing functionality due to the use of a [mock implementation of the engine](/src/GraphingImpl/Mocks) built on top of a
[common graphing API](/src/GraphingInterfaces).
## Diagnostic Data
This project collects usage data and sends it to Microsoft to help improve our products and services.
Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkId=521839) to learn more.

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Modules>
<Module
name="Microsoft.Windows.Apps.Calculator"
tdbuildteamid="86">
<File
location="Calculator"
path="%TFS_SOURCESDIRECTORY%\src\Calculator\Resources\en-US\Resources.resw" />
<File
location="Calculator"
path="%TFS_SOURCESDIRECTORY%\src\Calculator\Resources\en-US\CEngineStrings.resw" />
</Module>
</Modules>

View file

@ -1,5 +0,0 @@
<SignConfigXML>
<job platform="" configuration="" certSubject="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" jobname="EngFunSimpleSign" approvers="">
<file src="__INPATHROOT__\Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle" signType="136020001" dest="__OUTPATHROOT__\Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle" />
</job>
</SignConfigXML>

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE AMD64 Desktop WinPerf Test Run</BuildLogName>
<TemplateId>134858</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Performance_AMD64.testlist</RelativeFilePath>
<!-- Needed to be able to process multiple results for the same test case but not have reliability run checked on the task -->
<OptionalMetadataItems>
<OptionalMetadataItem>
<Key>VSTS\IsReliabilityRun</Key>
<Value>True</Value>
</OptionalMetadataItem>
</OptionalMetadataItems>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<TReXRunConfiguration>
<BuildLogName>RS4_RELEASE AMD64 DesktopVM Test Run</BuildLogName>
<TemplateId>139642</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_AMD64.testlist</RelativeFilePath>
<Branch>rs4_release</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!-- RS5_RELEASE Desktop x64 VM run -->
<TReXRunConfiguration>
<BuildLogName>RS5_RELEASE AMD64 DesktopVM Test Run</BuildLogName>
<TemplateId>139642</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_AMD64.testlist</RelativeFilePath>
<Branch>rs5_release</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE AMD64 DesktopVM Test Run</BuildLogName>
<TemplateId>139642</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_AMD64.testlist</RelativeFilePath>
<Branch>rs_apps_validate</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE AMD64 WCOS Test Run</BuildLogName>
<TemplateId>153648</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>WCOS_AMD64.testlist</RelativeFilePath>
<Branch>rs_apps_validate</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!--RS_PRERELEASE ARM WinCoreOS WinPerf run. -->
<TReXRunConfiguration>
<BuildLogName>RS_PRERELEASE ARM WindowsCore WinPerf Test Run</BuildLogName>
<TemplateId>135258</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Performance_ARM.testlist</RelativeFilePath>
<!-- Needed to be able to process multiple results for the same test case but not have reliability run checked on the task -->
<OptionalMetadataItems>
<OptionalMetadataItem>
<Key>VSTS\IsReliabilityRun</Key>
<Value>True</Value>
</OptionalMetadataItem>
</OptionalMetadataItems>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!-- RS_APPS_VALIDATE WindowsCore ARM Device run -->
<!-- Disabling this until we have a testlist template -->
<!--<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE ARM WCOS Test Run</BuildLogName>
<TemplateId>140487</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_ARM.testlist</RelativeFilePath>
<Branch>rs_apps_validate</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>-->
</TReXRunConfigurations>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!-- RS_APPS_VALIDATE Desktop x86 VM run -->
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE x86 DesktopVM Test Run</BuildLogName>
<TemplateId>139643</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_x86.testlist</RelativeFilePath>
<Branch>rs4_release</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!-- RS_PRERELEASE Desktop x86 VM run -->
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE x86 DesktopVM Test Run</BuildLogName>
<TemplateId>139643</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_x86.testlist</RelativeFilePath>
<Branch>rs5_release</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<TReXRunConfigurations>
<!-- RS_APPS_VALIDATE Desktop x86 VM run -->
<TReXRunConfiguration>
<BuildLogName>RS_APPS_VALIDATE x86 DesktopVM Test Run</BuildLogName>
<TemplateId>139643</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>Desktop_x86.testlist</RelativeFilePath>
<Branch>rs_apps_validate</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
<!-- RS_APPS_VALIDATE WindowsCore x86 VM run -->
<TReXRunConfiguration>
<BuildLogName>RS_PRERELEASE x86 WindowsCoreVM Test Run</BuildLogName>
<TemplateId>153715</TemplateId>
<Owner>paxeedev</Owner>
<PrivateRun>true</PrivateRun>
<CopyFirstJobSettingWithJobOverride>true</CopyFirstJobSettingWithJobOverride>
<RelativeFilePath>WCOS_x86.testlist</RelativeFilePath>
<Branch>rs_apps_validate</Branch>
<WorkflowParameters>
<WorkflowParameter>
<Key>AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
<WorkflowParameter>
<Key>_AlternatePackageRoot</Key>
<Value>\\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst</Value>
</WorkflowParameter>
</WorkflowParameters>
</TReXRunConfiguration>
</TReXRunConfigurations>

View file

@ -1,12 +0,0 @@
{
"$schema": "http://universaltest/schema/testlist-2.json",
"TestMDs": [
{
"FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd",
"ExecutionProfile": "All"
},
{
"FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm\\fre\\CalculatorUnitTests.testmd",
}
]
}

View file

@ -1,9 +0,0 @@
{
"$schema": "http://universaltest/schema/testlist-2.json",
"TestMDs": [
{
"FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd",
"ExecutionProfile": "Performance"
}
]
}

View file

@ -1,12 +0,0 @@
{
"$schema": "http://universaltest/schema/testlist-2.json",
"TestMDs": [
{
"FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd",
"ExecutionProfile": "All"
},
{
"FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm\\fre\\CalculatorUnitTests.testmd",
}
]
}

View file

@ -1,12 +0,0 @@
{
"$schema": "http://universaltest/schema/testlist-2.json",
"TestMDs": [
{
"FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm64\\fre\\Calculator.UITests.testmd",
"ExecutionProfile": "All"
},
{
"FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm64\\fre\\CalculatorUnitTests.testmd",
}
]
}

View file

@ -1,12 +0,0 @@
{
"$schema": "http://universaltest/schema/testlist-2.json",
"TestMDs": [
{
"FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm64\\fre\\Calculator.UITests.testmd",
"ExecutionProfile": "All"
},
{
"FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm64\\fre\\CalculatorUnitTests.testmd",
}
]
}

View file

@ -1,28 +0,0 @@
{
"Branch": [
{
"collection":"microsoft",
"project":"OS",
"repo":"os",
"name":"official/rs_apps_utils",
"CheckinFiles": [
{
"source":"vpack/app/calculator.app.man",
"path": "/redist/mspartners/ipa/Calculator",
"type": "File"
},
{
"source":"vpack/app/calculator.app.man",
"path": "/onecoreuap/redist/mspartners/ipa/Calculator",
"type": "File"
}
]
}
],
"Email": [
{
"sendTo":"paxeedev",
"sendOnErrorOnly": "True"
}
]
}

View file

@ -1,28 +0,0 @@
{
"Branch": [
{
"collection":"microsoft",
"project":"OS",
"repo":"os",
"name":"official/rs_apps_validate",
"CheckinFiles": [
{
"source":"vpack/app/calculator.app.man",
"path": "/redist/mspartners/ipa/Calculator",
"type": "File"
},
{
"source":"vpack/app/calculator.app.man",
"path": "/onecoreuap/redist/mspartners/ipa/Calculator",
"type": "File"
}
]
}
],
"Email": [
{
"sendTo":"paxeedev",
"sendOnErrorOnly": "True"
}
]
}

View file

@ -6,11 +6,11 @@
trigger:
- master
- servicing/*
- release/*
- feature/*
pr:
- master
- servicing/*
- release/*
- feature/*
name: 0.$(Date:yyMM).$(DayOfMonth)$(Rev:rr).0
@ -23,6 +23,7 @@ jobs:
- template: ./templates/build-app-public.yaml
parameters:
platform: x86
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
- template: ./templates/build-app-public.yaml
parameters:

View file

@ -4,13 +4,20 @@
# Store and the Windows image. This pipeline relies on Microsoft-internal resources to run.
#
schedules:
- cron: "0 7 * * *"
displayName: Daily midnight build
branches:
include:
- master
trigger: none
pr: none
variables:
versionMajor: 10
versionMinor: 1910
versionBuild: $[counter('10.1910.*', 0)]
versionMinor: 2012
versionBuild: $[counter('10.2012.*', 0)]
versionPatch: 0
name: '$(versionMajor).$(versionMinor).$(versionBuild).$(versionPatch)'
@ -30,11 +37,6 @@ jobs:
platform: ARM
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
- template: ./templates/build-app-internal.yaml
parameters:
platform: ARM64
condition: not(eq(variables['Build.Reason'], 'PullRequest'))
- template: ./templates/run-ui-tests.yaml
parameters:
platform: x64
@ -54,5 +56,7 @@ jobs:
platform: x86
- template: ./templates/package-appxbundle.yaml
parameters:
signBundle: true
- template: ./templates/prepare-release-internalonly.yaml

View file

@ -16,20 +16,18 @@ jobs:
variables:
BuildConfiguration: Release
BuildPlatform: ${{ parameters.platform }}
workspace:
clean: outputs
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: UniversalPackages@0
displayName: Download internals package
inputs:
command: download
downloadDirectory: $(Build.SourcesDirectory)
vstsFeed: WindowsApps
vstsFeed: WindowsInboxApps
vstsFeedPackage: calculator-internals
vstsPackageVersion: 0.0.31
vstsPackageVersion: 0.0.53
- template: ./build-single-architecture.yaml
parameters:

View file

@ -14,10 +14,8 @@ jobs:
variables:
BuildConfiguration: Release
BuildPlatform: ${{ parameters.platform }}
workspace:
clean: outputs
steps:
- checkout: self
clean: true
fetchDepth: 1
- template: ./build-single-architecture.yaml

View file

@ -5,11 +5,10 @@ parameters:
extraMsBuildArgs: ''
steps:
- task: NuGetToolInstaller@0
displayName: Use NuGet 5.0.2
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.x
inputs:
versionSpec: 5.0.2
checkLatest: true
versionSpec: 5.x
# In most accounts, you can just use 'NuGetCommand' instead of this GUID.
# In the microsoft.visualstudio.com account, NuGetCommand is ambiguous so the GUID is needed.
@ -17,7 +16,7 @@ steps:
displayName: NuGet restore src/Calculator.sln
inputs:
command: custom
arguments: restore src/Calculator.sln -Verbosity Detailed -NonInteractive
arguments: restore src/Calculator.sln -Verbosity Detailed
- task: PowerShell@2
displayName: Set version number in AppxManifest
@ -33,7 +32,6 @@ steps:
msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) /t:Publish /p:PublishDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\publish\ ${{ parameters.extraMsBuildArgs }}
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: true
maximumCpuCount: true
- task: PublishBuildArtifacts@1

View file

@ -1,30 +1,29 @@
# This template contains a job which takes .appx packages which were built separately for each
# architecture (arm, x86, etc.) and combines them into a single .appxbundle.
parameters:
signBundle: false
jobs:
- job: Package
dependsOn:
- Buildx64
- Buildx86
- BuildARM
- BuildARM64
condition: |
and
(
in(dependencies.Buildx64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
in(dependencies.Buildx86.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
in(dependencies.BuildARM.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
in(dependencies.BuildARM64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
in(dependencies.BuildARM.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
)
pool:
vmImage: windows-2019
workspace:
clean: outputs
variables:
skipComponentGovernanceDetection: true
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: DownloadBuildArtifacts@0
displayName: Download all .appx artifacts
@ -60,3 +59,43 @@ jobs:
inputs:
artifactName: appxBundle
pathToPublish: $(Build.ArtifactStagingDirectory)\appxBundle
- ${{ if eq(parameters.signBundle, true) }}:
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
displayName: Send appxbundle to code signing service
inputs:
ConnectedServiceName: Essential Experiences Codesign
FolderPath: $(Build.ArtifactStagingDirectory)\appxBundle
Pattern: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle
signConfigType: inlineSignParams
inlineOperation: |
[
{
"CertTemplateName": "WINMSAPP1ST",
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
"KeyCode": "Dynamic",
"OperationCode": "SigntoolvNextSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"CertTemplateName": "WINMSAPP1ST",
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
"KeyCode": "Dynamic",
"OperationCode": "SigntoolvNextVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
- task: PublishBuildArtifacts@1
displayName: Publish AppxBundleSigned artifact
inputs:
pathtoPublish: $(Build.ArtifactStagingDirectory)\appxBundle
artifactName: appxBundleSigned

View file

@ -2,8 +2,6 @@
# Windows using Microsoft-internal systems. It relies on Microsoft-internal resources and will not
# work outside of Microsoft.
# Specifically, this job:
# - Signs the bundle using a secure system. If you want to build your own, use SignTool following
# the example in the continuous integration pipeline.
# - Builds VPacks for including the app in the Windows OS build. Azure DevOps Universal Packages
# offers similar capabilities.
# - Creates StoreBroker packages containing Microsoft Store assets. Although the Store assets for
@ -14,7 +12,7 @@ jobs:
- job: WindowsInternalRelease
dependsOn: Package
pool:
name: Package ES Lab E
name: Package ES Standard Build
workspace:
clean: outputs
variables:
@ -39,36 +37,21 @@ jobs:
env:
XES_DISABLEPROV: true
- task: NuGetToolInstaller@0
displayName: Use NuGet 4.7.1
- task: NuGetToolInstaller@1
displayName: Use NuGet 5.x
inputs:
versionSpec: 4.7.1
checkLatest: true
versionSpec: 5.x
- task: DownloadBuildArtifacts@0
displayName: Download appxBundle artifact
displayName: Download appxBundleSigned artifact
inputs:
artifactName: appxBundle
- task: PkgESCodeSign@10
displayName: Send bundle to Package ES code signing service
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
signConfigXml: build\config\SignConfig.xml
inPathRoot: $(Build.ArtifactStagingDirectory)\appxBundle
outPathRoot: $(Build.ArtifactStagingDirectory)\appxBundleSigned
- task: PublishBuildArtifacts@1
displayName: Publish AppxBundleSigned artifact
inputs:
pathtoPublish: $(Build.ArtifactStagingDirectory)\appxBundleSigned
artifactName: AppxBundleSigned
artifactName: appxBundleSigned
- task: CopyFiles@2
displayName: Copy signed AppxBundle to vpack staging folder
inputs:
sourceFolder: $(Build.ArtifactStagingDirectory)\appxBundleSigned
contents: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle
targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle
- task: PkgESVPack@10
@ -81,6 +64,8 @@ jobs:
pushPkgName: calculator.app
version: $(versionMajor).$(versionMinor).$(versionBuild)
owner: paxeeapps
provData: false
- task: PublishBuildArtifacts@1
displayName: Publish vpack\app artifact with vpack manifest
@ -88,16 +73,14 @@ jobs:
pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME)
artifactName: vpack\app
# TODO (macool): create and push internal test packages and test config
- task: UniversalPackages@0
displayName: Download internals package
inputs:
command: download
downloadDirectory: $(Build.SourcesDirectory)
vstsFeed: WindowsApps
vstsFeed: WindowsInboxApps
vstsFeedPackage: calculator-internals
vstsPackageVersion: 0.0.22
vstsPackageVersion: 0.0.53
- powershell: |
# Just modify this line to indicate where your en-us PDP file is. Leave the other lines alone.
@ -151,6 +134,8 @@ jobs:
skipPolling: true
targetPublishMode: Immediate
logPath: '$(SBLogPath)'
deletePackages: true
numberOfPackagesToKeep: 0
- task: PkgESStoreBrokerAeroUpload@10
displayName: Upload to Aero flighting dashboard

View file

@ -48,3 +48,5 @@ jobs:
runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/${{ parameters.runsettingsFileName }}
platform: ${{ parameters.platform }}
configuration: Release
${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
testFiltercriteria: Priority=0

View file

@ -260,17 +260,19 @@ Steps:
4. While in the Menu: Check the About Page
*Expected: Everything in the about page fits into its window*
5. For Scientific Mode: At a Larger Scale
*Expected: All buttons are present and the up arrow is grayed out.*
*Expected: All buttons are present and the 2nd button is grayed out.*
6. For Scientific Mode: At a Smaller Scale
*Expected: All buttons are present and the up arrow is able to be toggled.*
*Expected: All buttons are present and the 2nd button is able to be toggled.*
7. For Programmer Mode: At a Any Scale
*Expected: All buttons are present and the up arrow is able to be toggled.*
*Expected: All buttons are present and the 2nd button is able to be toggled.*
8. For Converter Mode: While Scaling
*Expected: The number pad and input areas move around each other gracefully.*
9. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default.
10. Set the system number format preference: Open a Run dialog (WIN + R) > type intl.cpl > Enter > In the format dropdown list > Select Hebrew > Apply.
11. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build)
12. Repeat Steps 2-6 again in a (RTL) language.
9. For Graphing Mode: While Scaling
*Expected: The number pad, graph area, and input areas move around each other gracefully.*
10. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default.
11. Set the system number format preference: Open a Run dialog (WIN + R) > type intl.cpl > Enter > In the format dropdown list > Select Hebrew > Apply.
12. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build)
13. Repeat Steps 2-6 again in a (RTL) language.
*Expected: No elements fall out of intended boundaries.*
@ -302,11 +304,60 @@ Verify the following:
11. "Bin" Binary:
*Expected: A B C D E F 2 3 4 5 6 7 8 9 are inactive. A maximum of 64 characters can be entered.*
**Graphing Mode Test: Verify Graphing mode functions**
Steps:
1. Launch the "Calculator" app
2. Navigate to "Graphing" Calculator
3. Enter a function of x in the input field <br>
*Expected: Function is plotted in the graph area. Line color matches the colored square next to the input field*
4. Select the "+" button below the function input and enter more functions in the fields that appear <br>
*Expected: All functions are plotted in the graph area and match the colors of the input field squares*
5. Select the colored square for any function <br>
*Expected: Visibility of the function in the graph is toggled off/on*
6. Select the "Zoom In", "Zoom Out", and "Reset View' buttons in the graph area <br>
*Expected: Both X and Y axes zoom in, out, and revert to default settings, respectively*
7. Select the Trace button, then click + drag the graph until the red square is near a graphed function<br>
*Expected: Closest (X, Y) coordinates of the function to the red square are displayed with a black dot to indicate the location*
8. Enter "y=mx+b" into a function input field, then select "Variables" button <br>
*Expected: y=x+1 function is plotted in the graph, "Variables" modal window shows two variables "m" and "b" with values set to 1.*
9. Adjust the value, minimum, maximum, and step for each variable <br>
*Expected: y=mx+b graph adjusts to the new values for m and b, step size changes the increments of the slider for each value*
10. Share the graph via OneNote, Outlook/Mail, Twitter, and Feedback Hub <br>
*Expected: Modifiable message that contains an image of the graph customized for the chosen application opens*
11. Verify Key Graph Features tab shows the correct information for the following functions: <br>
*(Note: IP = Inflection Points, VA = Vertical Asymptotes, HA = Horizontal Asymptotes, OA = Oblique Asymptotes)* <br>
a. **y=x** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing* <br>
b. **y=1/x** <br>
*Expected: Domain: ⁅𝑥≠0⁆; Range: ⁅y∈\{0}⁆; X/Y Intercepts: ø/ø; Max: none; Min: none; IP: none; VA: x=0; HA: y=0; OA: none; Parity: Odd; Monotonicity: (0, ∞) Decreasing, (-∞, 0) Increasing* <br>
c. **y=x^2** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈{0, ∞)⁆; X/Y Intercepts: (0)/(0); Max: none; Min: (0,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0, ∞) Increasing, (-∞, 0) Decreasing* <br>
d. **y=x^3** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: (0,0); VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing* <br>
e. **y=e^x** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈(0, ∞)⁆; X/Y Intercepts: ø/(1); Max: none; Min: none; IP: none; VA: none; HA: y=0; OA: none; Parity: none; Monotonicity: (-∞, ∞) Increasing* <br>
f. **y=ln(x)** <br>
*Expected: Domain: ⁅𝑥>0⁆; Range: ⁅y∈⁆; X/Y Intercepts: (1)/ø; Max: none; Min: none; IP: none; VA: x=0; HA: none; OA: none; Parity: none; Monotonicity: (0, ∞) Increasing* <br>
g. **y=sin(x)** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[1,1]⁆; X/Y Intercepts: (⁅𝜋n1,n1∈⁆)/(0); Max: ⁅(2𝜋n1+𝜋/2,1),n1∈⁆; Min: ⁅(2𝜋n1+3𝜋/2,1),n1∈⁆; IP: ⁅(𝜋n1,0),n1∈⁆; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(2𝜋n1+𝜋/2,2𝜋n1+3𝜋/2),n1∈⁆ Decreasing; ⁅(2𝜋n1+3𝜋/2,2𝜋n1+5𝜋/2),n1∈⁆ Increasing; Period: 2𝜋* <br>
h. **y=cos(x)** <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[1,1]⁆; X/Y Intercepts: (⁅𝜋n1+𝜋/2,n1∈⁆)/(1); Max: ⁅(2𝜋n1,1),n1∈⁆; Min: ⁅(2𝜋n1+𝜋,-1),n1∈⁆; IP: ⁅(𝜋n1+𝜋/2,0),n1∈⁆; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: ⁅(2𝜋n1+𝜋,2𝜋n1+2𝜋),n1∈⁆ Increasing, ⁅(2𝜋n1,2𝜋n1+𝜋),n1∈⁆ Decreasing; Period: 2𝜋* <br>
i. **y=tan(x)** <br>
*Expected: Domain: ⁅x≠𝜋n1+𝜋/2,∀n1∈⁆; Range: ⁅𝑦∈ℝ⁆; X/Y Intercepts: (x=𝜋n1, n1 ∈ℤ)/(0); Max: none; Min: none; IP: x=𝜋n1, n1 ∈ℤ; VA: x=𝜋n1+𝜋/2, n1∈; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(𝜋n1+𝜋/2,𝜋n1+3𝜋/2),n1∈⁆ Increasing; Period: 𝜋* <br>
j. **y=sqrt(25-x^2)** <br>
*Expected: Domain: ⁅x∈[-5,5]⁆; Range: ⁅𝑦∈[0,5]⁆; X/Y Intercepts: (5),(-5)/(5); Max: (0,5); Min: (-5,0) and (5,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0,5) Decreasing, (-5,0) Increasing* <br>
k. **y=(-3x^2+2)/(x-1)** <br>
*Expected: Domain: ⁅x≠1⁆; Range: ⁅𝑦∈(-∞, -2√3 - 6}U{2√3 -6,∞⁆; X/Y Intercepts: (-√6/3),(√6/3)/(-2); Max: ⁅(√3/3+1,-2√36)⁆; Min: ⁅(√3/3+1,2√36)⁆; IP: none; VA: x=1; HA: none; OA: y=-3x-3; Parity: none; Monotonicity: (√3/3+1,∞) Decreasing, (1,√3/3+1,) Increasing(-√3/3+1,1), Increasing, (-∞,-√3/3+1) Decreasing* <br>
l. **y=sin(sin(x))** ("too complex" error test) <br>
*Expected: Domain: ⁅𝑥∈ℝ⁆; Range: Unable to calculate range for this function; X/Y Intercepts: none; Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: odd; Monotonicity: Unable to determine the monotonicity of the function* <br>
*These features are too complex for Calculator to calculate: Range, X Intercept, Period, Minima, Maxima, Inflection Points, Monotonicity*
m. **y=mx+b** <br>
*Expected: Analysis is not supported for this function*
**Date Calculation Test: Verify dates can be calculated.**
Steps:
1. Launch the "Calculator" app.
2. Navigate to "Date Calculation" Calculator.
1. Launch the "Calculator" app
2. Navigate to "Date Calculation" Calculator
3. With "Difference between dates" Selected
Change the various date input fields
*Expected: From and To reflect dates input respectively.*
@ -332,80 +383,88 @@ Steps:
1. Launch the "Calculator" app.
For All Applicable Modes verify the following (note: only 11-15 and 20 work in Always-on-Top mode):
2. Press **Alt +1** to Enter "Standard" mode
2. Press **Alt +1** to enter "Standard" mode
*Expected: Move to "Standard" screen.*
3. Press **Alt +2** to Enter "Scientific" mode
3. Press **Alt +2** to enter "Scientific" mode
*Expected: Move to "Scientific" screen.*
4. Press **Alt +3** to Enter "Programmer" mode
4. Press **Alt +3** to enter "Programmer" mode
*Expected: Move to "Programming" screen.*
5. Press **Alt +4** to Enter "Date Calculation" mode
5. Press **Alt +4** to enter "Date Calculation" mode
*Expected: Move to "Date Calculation" screen.*
6. Press **Ctrl +M** to Store in Memory
7. Press **Ctrl +P** to Add to Active Memory
8. Press **Ctrl +Q** to Subtract form Active Memory
9. Press **Ctrl +R** to Recall from Memory
10. Press **Ctrl +L** to Clear from Memory
11. Press **Delete** to Clear Current Input 'CE'
12. Press **Esc** to Full Clear Input 'C'
13. Press **F9** to Toggle '±'
14. Press **R** to Select '1/x'
15. Press **@** to Select '√'
16. Press **Ctrl + H** to Toggle History Panel
6 Press **Alt +5** to enter "Graphing" mode
*Expected: Move to "Graphing" screen.*
7. Press **Ctrl +M** to Store in Memory
8. Press **Ctrl +P** to Add to Active Memory
9. Press **Ctrl +Q** to Subtract form Active Memory
10. Press **Ctrl +R** to Recall from Memory
11. Press **Ctrl +L** to Clear from Memory
12. Press **Delete** to Clear Current Input 'CE'
13. Press **Esc** to Full Clear Input 'C'
14. Press **F9** to Toggle '±'
15. Press **R** to Select '1/x'
16. Press **@** to Select '√'
17. Press **Ctrl + H** to Toggle History Panel
*Expected: Function when in small scale window.*
17. Press **Up arrow** to Move up History Panel
18. Press **Up arrow** to Move up History Panel
*Expected: Function when in small scale window.*
18. Press **Down arrow** to Move Down History Panel
19. Press **Down arrow** to Move Down History Panel
*Expected: Function when in small scale window.*
19. Press **Ctrl + Shift + D** to Clear History Panel
20. Press **Ctrl + Shift + D** to Clear History Panel
*Expected: Function when in small scale window.*
20. Press **Spacebar** to Repeat Last Input
21. Press **Spacebar** to Repeat Last Input
Verify the following in Scientific Mode
21. Press **F3** to Select 'DEG'
22. Press **F4** to Select 'RAD'
23. Press **F5** to Select 'GRAD'
24. Press **Ctrl +G** to Select '10ˣ'
25. Press **Ctrl +Y** to Select 'y√x'
26. Press **Shift +O** to Select 'sin-1'
27. Press **Shift + S** to Select 'cos-1'
28. Press **Shift +T** to Select 'tan-1'
29. Press **Ctrl +O** to Select 'Cosh'
30. Press **Ctrl +S** to Select 'Sinh'
31. Press **Ctrl +T** to Select 'Tanh'
32. Press **D** to Select 'Mod'
33. Press **L** to Select 'log'
34. Press **M** to Select 'dms'
35. Press **N** to Select 'ln'
36. Press **Ctrl +N** to Select 'ex'
37. Press **O** to Select 'Cos'
38. Press **P** to Select 'π'
39. Press **Q** to Select 'x²'
40. Press **S** to Select 'Sin'
41. Press **T** to Select 'Tan'
42. Press **V** to Toggle 'F-E'
43. Press **X** to Select 'Exp'
44. Press **Y** or **^** to Select 'xʸ'
45. Press **#** to Select 'x³'
46. Press **!** to Select 'n!'
22. Press **F3** to Select 'DEG'
23. Press **F4** to Select 'RAD'
24. Press **F5** to Select 'GRAD'
25. Press **Ctrl +G** to Select '10ˣ'
26. Press **Ctrl +Y** to Select 'y√x'
27. Press **Shift +O** to Select 'sin-1'
28. Press **Shift + S** to Select 'cos-1'
29. Press **Shift +T** to Select 'tan-1'
30. Press **Ctrl +O** to Select 'Cosh'
31. Press **Ctrl +S** to Select 'Sinh'
32. Press **Ctrl +T** to Select 'Tanh'
33. Press **D** to Select 'Mod'
34. Press **L** to Select 'log'
35. Press **M** to Select 'dms'
36. Press **N** to Select 'ln'
37. Press **Ctrl +N** to Select 'ex'
38. Press **O** to Select 'Cos'
39. Press **P** to Select 'π'
40. Press **Q** to Select 'x²'
41. Press **S** to Select 'Sin'
42. Press **T** to Select 'Tan'
43. Press **V** to Toggle 'F-E'
44. Press **X** to Select 'Exp'
45. Press **Y** or **^** to Select 'xʸ'
46. Press **#** to Select 'x³'
47. Press **!** to Select 'n!'
Verify the following in Programmer Mode
47. Press **F2** to Select 'DWORD'
48. Press **F3** to Select 'WORD'
49. Press **F4** to Select 'BYTE'
50. Press **F5** to Select 'HEX'
51. Press **F6** to Select 'DEC'
52. Press **F7** to Select 'OCT'
53. Press **F8** to Select 'BIN'
54. Press **F12** to Select 'QWORD'
55. Press **A-F** to Input in HEX
56. Press **J** to Select 'RoL'
57. Press **K** to Select 'RoR'
58. Press **<** to Select 'Lsh'
59. Press **>** to Select 'Rsh'
60. Press **%** to Select 'Mod'
61. Press **|** to Select 'Or'
62. Press **~** to Select 'Not'
63. Press **&** to Select 'And'
48. Press **F2** to Select 'DWORD'
49. Press **F3** to Select 'WORD'
50. Press **F4** to Select 'BYTE'
51. Press **F5** to Select 'HEX'
52. Press **F6** to Select 'DEC'
53. Press **F7** to Select 'OCT'
54. Press **F8** to Select 'BIN'
55. Press **F12** to Select 'QWORD'
56. Press **A-F** to Input in HEX
57. Press **J** to Select 'RoL'
58. Press **K** to Select 'RoR'
59. Press **<** to Select 'Lsh'
60. Press **>** to Select 'Rsh'
61. Press **%** to Select 'Mod'
62. Press **|** to Select 'Or'
63. Press **~** to Select 'Not'
64. Press **&** to Select 'And'
Verify the following in Graphing Mode
65. Press **x** to Select 'x'
66. Press **y** to Select 'y'
67. Press **Ctrl +[Numpad+]** to Select 'Zoom In'
68. Press **Ctrl +[Numpad-]** to Select 'Zoom Out'
## Localization Tests

View file

@ -4,14 +4,11 @@ Windows Calculator is under active development by Microsoft.
## Focus
In 2019, the Windows Calculator team is focused on:
* Refining our open source development process on GitHub
* Iterating upon the existing app design based on the latest [Fluent Design guidelines](https://developer.microsoft.com/en-us/windows/apps/design)
* Improving testing and diagnostics within the project
* Investigating new features with a focus on addressing top user feedback, including:
* Adding graphing mode
* Adding the ability for users to pin Calculator on top of other windows
* Providing additional customization options
In 2020, the Windows Calculator team is focused on:
* Iterating upon the existing app design based on the latest guidelines for [Fluent Design](https://developer.microsoft.com/en-us/windows/apps/design), [Windows 10X dual-screen devices](https://docs.microsoft.com/en-us/dual-screen/windows/), and [WinUI 3.0](https://github.com/microsoft/microsoft-ui-xaml).
* Unblocking community contributions by identifying and addressing bottlenecks affecting developers, including migrating portions of the codebase to C# ([#893](https://github.com/microsoft/calculator/issues/893)) and adding a settings page ([#596](https://github.com/microsoft/calculator/issues/596)).
* Addressing top user pain points, including clearing up confusion around how Standard Calculator behaves ([#138](https://github.com/microsoft/calculator/issues/138)) and fixing hidden characters in copied results ([#504](https://github.com/microsoft/calculator/issues/504)).
* Investigating unit converter improvements ([#379](https://github.com/microsoft/calculator/issues/379), [#589](https://github.com/microsoft/calculator/issues/589) and [#594](https://github.com/microsoft/calculator/issues/594))
* [Your feature idea here] - please review our [new feature development process](https://github.com/Microsoft/calculator/blob/master/docs/NewFeatureProcess.md) to get started!
We welcome contributions of all kinds from the community, but especially those that support the efforts above. Please see our [contributing guidelines](https://github.com/Microsoft/calculator/blob/master/CONTRIBUTING.md) for more information on how to get involved.

View file

@ -1,11 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(calculator CXX)
set(CMAKE_CXX_STANDARD 17)
if(CMAKE_CXX_COMPILER MATCHES ".*clang")
# Clang disagress with libstdc++ about constexpr-ness of wstring_view
# See https://github.com/Microsoft/calculator/pull/321
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
add_subdirectory(CalcManager)

View file

@ -1,14 +0,0 @@
target_sources(CalcManager PRIVATE
calc.cpp
CalcInput.cpp
CalcUtils.cpp
History.cpp
Number.cpp
Rational.cpp
RationalMath.cpp
scicomm.cpp
scidisp.cpp
scifunc.cpp
scioper.cpp
sciset.cpp
)

View file

@ -210,7 +210,7 @@ bool CHistoryCollector::FOpndAddedToHistory()
// This is does the postfix to prefix translation of the input and adds the text to the history. Eg. doing 2 + 4 (sqrt),
// this routine will ensure the last sqrt call unary operator, actually goes back in history and wraps 4 in sqrt(4)
//
void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype)
void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, AngleType angletype)
{
int iCommandEnd;
// When successfully applying a unary op, there should be an opnd already
@ -230,15 +230,15 @@ void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE a
else
{
CalculationManager::Command angleOpCode;
if (angletype == ANGLE_DEG)
if (angletype == AngleType::Degrees)
{
angleOpCode = CalculationManager::Command::CommandDEG;
}
else if (angletype == ANGLE_RAD)
else if (angletype == AngleType::Radians)
{
angleOpCode = CalculationManager::Command::CommandRAD;
}
else // (angletype == ANGLE_GRAD)
else // (angletype == AngleType::Gradians)
{
angleOpCode = CalculationManager::Command::CommandGRAD;
}

View file

@ -460,7 +460,7 @@ namespace CalcEngine
return !(lhs < rhs);
}
wstring Rational::ToString(uint32_t radix, NUMOBJ_FMT fmt, int32_t precision) const
wstring Rational::ToString(uint32_t radix, NumberFormat fmt, int32_t precision) const
{
PRAT rat = this->ToPRAT();
wstring result{};

View file

@ -147,7 +147,7 @@ Rational RationalMath::Abs(Rational const& rat)
return Rational{ Number{ 1, rat.P().Exp(), rat.P().Mantissa() }, Number{ 1, rat.Q().Exp(), rat.Q().Mantissa() } };
}
Rational RationalMath::Sin(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::Sin(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();
@ -167,7 +167,7 @@ Rational RationalMath::Sin(Rational const& rat, ANGLE_TYPE angletype)
return result;
}
Rational RationalMath::Cos(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::Cos(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();
@ -187,7 +187,7 @@ Rational RationalMath::Cos(Rational const& rat, ANGLE_TYPE angletype)
return result;
}
Rational RationalMath::Tan(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::Tan(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();
@ -207,7 +207,7 @@ Rational RationalMath::Tan(Rational const& rat, ANGLE_TYPE angletype)
return result;
}
Rational RationalMath::ASin(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::ASin(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();
@ -227,7 +227,7 @@ Rational RationalMath::ASin(Rational const& rat, ANGLE_TYPE angletype)
return result;
}
Rational RationalMath::ACos(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::ACos(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();
@ -247,7 +247,7 @@ Rational RationalMath::ACos(Rational const& rat, ANGLE_TYPE angletype)
return result;
}
Rational RationalMath::ATan(Rational const& rat, ANGLE_TYPE angletype)
Rational RationalMath::ATan(Rational const& rat, AngleType angletype)
{
PRAT prat = rat.ToPRAT();

View file

@ -73,7 +73,7 @@ CCalcEngine::CCalcEngine(
, m_bRecord(false)
, m_bSetCalcState(false)
, m_input(DEFAULT_DEC_SEPARATOR)
, m_nFE(FMT_FLOAT)
, m_nFE(NumberFormat::Float)
, m_memoryValue{ make_unique<Rational>() }
, m_holdVal{}
, m_currentVal{}
@ -94,8 +94,8 @@ CCalcEngine::CCalcEngine(
, m_nPrecOp()
, m_precedenceOpCount(0)
, m_nLastCom(0)
, m_angletype(ANGLE_DEG)
, m_numwidth(QWORD_WIDTH)
, m_angletype(AngleType::Degrees)
, m_numwidth(NUM_WIDTH::QWORD_WIDTH)
, m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR)
, m_groupSeparator(DEFAULT_GRP_SEPARATOR)
{
@ -105,7 +105,7 @@ CCalcEngine::CCalcEngine(
m_maxTrigonometricNum = RationalMath::Pow(10, 100);
SetRadixTypeAndNumWidth(DEC_RADIX, m_numwidth);
SetRadixTypeAndNumWidth(RadixType::Decimal, m_numwidth);
SettingsChanged();
DisplayNum();
}
@ -128,10 +128,20 @@ void CCalcEngine::InitChopNumbers()
auto maxVal = m_chopNumbers[i] / 2;
maxVal = RationalMath::Integer(maxVal);
m_maxDecimalValueStrings[i] = maxVal.ToString(10, FMT_FLOAT, m_precision);
m_maxDecimalValueStrings[i] = maxVal.ToString(10, NumberFormat::Float, m_precision);
}
}
CalcEngine::Rational CCalcEngine::GetChopNumber() const
{
return m_chopNumbers[static_cast<int>(m_numwidth)];
}
std::wstring CCalcEngine::GetMaxDecimalValueString() const
{
return m_maxDecimalValueStrings[static_cast<int>(m_numwidth)];
}
// Gets the number in memory for UI to keep it persisted and set it again to a different instance
// of CCalcEngine. Otherwise it will get destructed with the CalcEngine
unique_ptr<Rational> CCalcEngine::PersistedMemObject()

View file

@ -35,21 +35,18 @@ namespace
IDC_ADD,2, IDC_SUB,2,
IDC_RSHF,3, IDC_LSHF,3, IDC_RSHFL,3,
IDC_MOD,3, IDC_DIV,3, IDC_MUL,3,
IDC_PWR,4, IDC_ROOT,4, IDC_LOGBASEX,4 };
unsigned int iPrec;
IDC_PWR,4, IDC_ROOT,4, IDC_LOGBASEY,4 };
iPrec = 0;
while ((iPrec < size(rgbPrec)) && (nopCode != rgbPrec[iPrec]))
for (unsigned int iPrec = 0; iPrec < size(rgbPrec); iPrec += 2)
{
iPrec += 2;
}
if (iPrec >= size(rgbPrec))
if (nopCode == rgbPrec[iPrec])
{
iPrec = 0;
}
return rgbPrec[iPrec + 1];
}
}
return 0;
}
}
// HandleErrorCommand
//
@ -104,8 +101,6 @@ void CCalcEngine::ProcessCommand(OpCode wParam)
void CCalcEngine::ProcessCommandWorker(OpCode wParam)
{
int nx, ni;
// Save the last command. Some commands are not saved in this manor, these
// commands are:
// Inv, Deg, Rad, Grad, Stat, FE, MClear, Back, and Exp. The excluded
@ -163,15 +158,12 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
DisplayNum(); // Causes 3.000 to shrink to 3. on first op.
}
}
else
{
if (IsDigitOpCode(wParam) || wParam == IDC_PNT)
else if (IsDigitOpCode(wParam) || wParam == IDC_PNT)
{
m_bRecord = true;
m_input.Clear();
CheckAndAddLastBinOpToHistory();
}
}
// Interpret digit keys.
if (IsDigitOpCode(wParam))
@ -185,7 +177,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
return;
}
if (!m_input.TryAddDigit(iValue, m_radix, m_fIntegerMode, m_maxDecimalValueStrings[m_numwidth], m_dwWordBitWidth, m_cIntDigitsSav))
if (!m_input.TryAddDigit(iValue, m_radix, m_fIntegerMode, GetMaxDecimalValueString(), m_dwWordBitWidth, m_cIntDigitsSav))
{
HandleErrorCommand(wParam);
HandleMaxDigitsReached();
@ -203,7 +195,6 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
// Change the operation if last input was operation.
if (IsBinOpCode(m_nLastCom))
{
int nPrev;
bool fPrecInvToHigher = false; // Is Precedence Inversion from lower to higher precedence happening ??
m_nOpCode = (int)wParam;
@ -214,9 +205,9 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
// Here * is m_nPrevOpCode, m_currentVal is 2 (by 1*2), m_nLastCom is +, m_nOpCode is ^
if (m_fPrecedence && 0 != m_nPrevOpCode)
{
nPrev = NPrecedenceOfOp(m_nPrevOpCode);
nx = NPrecedenceOfOp(m_nLastCom);
ni = NPrecedenceOfOp(m_nOpCode);
int nPrev = NPrecedenceOfOp(m_nPrevOpCode);
int nx = NPrecedenceOfOp(m_nLastCom);
int ni = NPrecedenceOfOp(m_nOpCode);
if (nx <= nPrev && ni > nPrev) // condition for Precedence Inversion
{
fPrecInvToHigher = true;
@ -243,8 +234,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
{
DoPrecedenceCheckAgain:
nx = NPrecedenceOfOp((int)wParam);
ni = NPrecedenceOfOp(m_nOpCode);
int nx = NPrecedenceOfOp((int)wParam);
int ni = NPrecedenceOfOp(m_nOpCode);
if ((nx > ni) && m_fPrecedence)
{
@ -492,8 +483,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
m_lastVal = m_precedenceVals[m_precedenceOpCount];
// Precedence Inversion check
ni = NPrecedenceOfOp(m_nPrevOpCode);
nx = NPrecedenceOfOp(m_nOpCode);
int ni = NPrecedenceOfOp(m_nPrevOpCode);
int nx = NPrecedenceOfOp(m_nOpCode);
if (ni <= nx)
{
m_HistoryCollector.EnclosePrecInversionBrackets();
@ -518,20 +509,15 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
case IDC_OPENP:
case IDC_CLOSEP:
nx = 0;
if (wParam == IDC_OPENP)
{
nx = 1;
}
// -IF- the Paren holding array is full and we try to add a paren
// -OR- the paren holding array is empty and we try to remove a
// paren
// -OR- the precedence holding array is full
if ((m_openParenCount >= MAXPRECDEPTH && nx) || (!m_openParenCount && !nx)
if ((m_openParenCount >= MAXPRECDEPTH && (wParam == IDC_OPENP)) || (!m_openParenCount && (wParam != IDC_OPENP))
|| ((m_precedenceOpCount >= MAXPRECDEPTH && m_nPrecOp[m_precedenceOpCount - 1] != 0)))
{
if (!m_openParenCount && !nx)
if (!m_openParenCount && (wParam != IDC_OPENP))
{
m_pCalcDisplay->OnNoRightParenAdded();
}
@ -540,7 +526,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
break;
}
if (nx)
if (wParam == IDC_OPENP)
{
CheckAndAddLastBinOpToHistory();
m_HistoryCollector.AddOpenBraceToHistory();
@ -588,8 +574,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
for (m_nOpCode = m_nPrecOp[--m_precedenceOpCount]; m_nOpCode; m_nOpCode = m_nPrecOp[--m_precedenceOpCount])
{
// Precedence Inversion check
ni = NPrecedenceOfOp(m_nPrevOpCode);
nx = NPrecedenceOfOp(m_nOpCode);
int ni = NPrecedenceOfOp(m_nPrevOpCode);
int nx = NPrecedenceOfOp(m_nOpCode);
if (ni <= nx)
{
m_HistoryCollector.EnclosePrecInversionBrackets();
@ -633,7 +619,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
case IDM_OCT:
case IDM_BIN:
{
SetRadixTypeAndNumWidth((RADIX_TYPE)(wParam - IDM_HEX), (NUM_WIDTH)-1);
SetRadixTypeAndNumWidth((RadixType)(wParam - IDM_HEX), (NUM_WIDTH)-1);
m_HistoryCollector.UpdateHistoryExpression(m_radix, m_precision);
break;
}
@ -649,20 +635,20 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
}
// Compat. mode BaseX: Qword, Dword, Word, Byte
SetRadixTypeAndNumWidth((RADIX_TYPE)-1, (NUM_WIDTH)(wParam - IDM_QWORD));
SetRadixTypeAndNumWidth((RadixType)-1, (NUM_WIDTH)(wParam - IDM_QWORD));
break;
case IDM_DEG:
case IDM_RAD:
case IDM_GRAD:
m_angletype = static_cast<ANGLE_TYPE>(wParam - IDM_DEG);
m_angletype = static_cast<AngleType>(wParam - IDM_DEG);
break;
case IDC_SIGN:
{
if (m_bRecord)
{
if (m_input.TryToggleSign(m_fIntegerMode, m_maxDecimalValueStrings[m_numwidth]))
if (m_input.TryToggleSign(m_fIntegerMode, GetMaxDecimalValueString()))
{
DisplayNum();
}
@ -780,7 +766,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam)
break;
case IDC_FE:
// Toggle exponential notation display.
m_nFE = NUMOBJ_FMT(!(int)m_nFE);
m_nFE = NumberFormat(!(int)m_nFE);
DisplayNum();
break;
@ -962,7 +948,7 @@ static const std::unordered_map<int, FunctionNameElement> operatorStringTable =
{ IDC_SIGN, { SIDS_NEGATE } },
{ IDC_DEGREES, { SIDS_DEGREES } },
{ IDC_POW2, { SIDS_TWOPOWX } },
{ IDC_LOGBASEX, { SIDS_LOGBASEX } },
{ IDC_LOGBASEY, { SIDS_LOGBASEY } },
{ IDC_ABS, { SIDS_ABS } },
{ IDC_CEIL, { SIDS_CEIL } },
{ IDC_FLOOR, { SIDS_FLOOR } },
@ -975,7 +961,7 @@ static const std::unordered_map<int, FunctionNameElement> operatorStringTable =
{ IDC_MOD, { SIDS_MOD, L"", L"", L"", L"", L"", SIDS_PROGRAMMER_MOD } },
};
wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype)
wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype)
{
// Try to lookup the ID in the UFNE table
wstring ids = L"";
@ -983,7 +969,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE
if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end())
{
const FunctionNameElement& element = pair->second;
if (!element.hasAngleStrings || ANGLE_DEG == angletype)
if (!element.hasAngleStrings || AngleType::Degrees == angletype)
{
if (fInv)
{
@ -995,7 +981,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE
ids = element.degreeString;
}
}
else if (ANGLE_RAD == angletype)
else if (AngleType::Radians == angletype)
{
if (fInv)
{
@ -1006,7 +992,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE
ids = element.radString;
}
}
else if (ANGLE_GRAD == angletype)
else if (AngleType::Gradians == angletype)
{
if (fInv)
{
@ -1059,7 +1045,7 @@ bool CCalcEngine::IsCurrentTooBigForTrig()
return m_currentVal >= m_maxTrigonometricNum;
}
int CCalcEngine::GetCurrentRadix()
uint32_t CCalcEngine::GetCurrentRadix()
{
return m_radix;
}
@ -1108,7 +1094,7 @@ wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix)
if ((radix == 10) && fMsb)
{
// If high bit is set, then get the decimal number in negative 2's complement form.
tempRat = -((tempRat ^ m_chopNumbers[m_numwidth]) + 1);
tempRat = -((tempRat ^ GetChopNumber()) + 1);
}
result = tempRat.ToString(radix, m_nFE, m_precision);

View file

@ -67,10 +67,10 @@ CalcEngine::Rational CCalcEngine::TruncateNumForIntMath(CalcEngine::Rational con
{
// if negative make positive by doing a twos complement
result = -(result)-1;
result ^= m_chopNumbers[m_numwidth];
result ^= GetChopNumber();
}
result &= m_chopNumbers[m_numwidth];
result &= GetChopNumber();
return result;
}
@ -85,7 +85,7 @@ void CCalcEngine::DisplayNum(void)
// called.
//
if (m_bRecord || gldPrevious.value != m_currentVal || gldPrevious.precision != m_precision || gldPrevious.radix != m_radix || gldPrevious.nFE != (int)m_nFE
|| gldPrevious.bUseSep != true || gldPrevious.numwidth != m_numwidth || gldPrevious.fIntMath != m_fIntegerMode || gldPrevious.bRecord != m_bRecord)
|| !gldPrevious.bUseSep || gldPrevious.numwidth != m_numwidth || gldPrevious.fIntMath != m_fIntegerMode || gldPrevious.bRecord != m_bRecord)
{
gldPrevious.precision = m_precision;
gldPrevious.radix = m_radix;

View file

@ -42,7 +42,7 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r
}
else
{
result = rat ^ m_chopNumbers[m_numwidth];
result = rat ^ GetChopNumber();
}
break;

View file

@ -29,11 +29,11 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
break;
case IDC_NAND:
result = (result & rhs) ^ m_chopNumbers[m_numwidth];
result = (result & rhs) ^ GetChopNumber();
break;
case IDC_NOR:
result = (result | rhs) ^ m_chopNumbers[m_numwidth];
result = (result | rhs) ^ GetChopNumber();
break;
case IDC_RSHF:
@ -53,10 +53,10 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
{
result = Integer(result);
auto tempRat = m_chopNumbers[m_numwidth] >> holdVal;
auto tempRat = GetChopNumber() >> holdVal;
tempRat = Integer(tempRat);
result |= tempRat ^ m_chopNumbers[m_numwidth];
result |= tempRat ^ GetChopNumber();
}
break;
}
@ -105,7 +105,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
if (fMsb)
{
result = (rhs ^ m_chopNumbers[m_numwidth]) + 1;
result = (rhs ^ GetChopNumber()) + 1;
iNumeratorSign = -1;
}
@ -115,7 +115,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
if (fMsb)
{
temp = (temp ^ m_chopNumbers[m_numwidth]) + 1;
temp = (temp ^ GetChopNumber()) + 1;
iDenominatorSign = -1;
}
@ -158,8 +158,8 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
result = Root(rhs, result);
break;
case IDC_LOGBASEX:
result = (Log(result) / Log(rhs));
case IDC_LOGBASEY:
result = (Log(rhs) / Log(result));
break;
}
}

View file

@ -9,7 +9,7 @@ using namespace std;
// To be called when either the radix or num width changes. You can use -1 in either of these values to mean
// dont change that.
void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth)
void CCalcEngine::SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth)
{
// When in integer mode, the number is represented in 2's complement form. When a bit width is changing, we can
// change the number representation back to sign, abs num form in ratpak. Soon when display sees this, it will
@ -24,19 +24,19 @@ void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwid
if (fMsb)
{
// If high bit is set, then get the decimal number in -ve 2'scompl form.
auto tempResult = m_currentVal ^ m_chopNumbers[m_numwidth];
auto tempResult = m_currentVal ^ GetChopNumber();
m_currentVal = -(tempResult + 1);
}
}
if (radixtype >= HEX_RADIX && radixtype <= BIN_RADIX)
if (radixtype >= RadixType::Hex && radixtype <= RadixType::Binary)
{
m_radix = NRadixFromRadixType(radixtype);
// radixtype is not even saved
}
if (numwidth >= QWORD_WIDTH && numwidth <= BYTE_WIDTH)
if (numwidth >= NUM_WIDTH::QWORD_WIDTH && numwidth <= NUM_WIDTH::BYTE_WIDTH)
{
m_numwidth = numwidth;
m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(numwidth);
@ -50,29 +50,36 @@ void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwid
DisplayNum();
}
int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH /*numwidth*/)
int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth)
{
static constexpr int nBitMax[] = { 64, 32, 16, 8 };
int32_t wmax = nBitMax[0];
if (m_numwidth >= 0 && (size_t)m_numwidth < size(nBitMax))
switch (numwidth)
{
wmax = nBitMax[m_numwidth];
case NUM_WIDTH::DWORD_WIDTH:
return 32;
case NUM_WIDTH::WORD_WIDTH:
return 16;
case NUM_WIDTH::BYTE_WIDTH:
return 8;
case NUM_WIDTH::QWORD_WIDTH:
default:
return 64;
}
return wmax;
}
uint32_t CCalcEngine::NRadixFromRadixType(RADIX_TYPE radixtype)
uint32_t CCalcEngine::NRadixFromRadixType(RadixType radixtype)
{
static constexpr uint32_t rgnRadish[4] = { 16, 10, 8, 2 }; /* Number bases in the same order as radixtype */
uint32_t radix = 10;
// convert special bases into symbolic values
if (radixtype >= 0 && (size_t)radixtype < size(rgnRadish))
switch (radixtype)
{
radix = rgnRadish[radixtype];
case RadixType::Hex:
return 16;
case RadixType::Octal:
return 8;
case RadixType::Binary:
return 2;
case RadixType::Decimal:
default:
return 10;
}
return radix;
}
// Toggles a given bit into the number representation. returns true if it changed it actually.
@ -141,7 +148,7 @@ void CCalcEngine::UpdateMaxIntDigits()
// if in integer mode you still have to honor the max digits you can enter based on bit width
if (m_fIntegerMode)
{
m_cIntDigitsSav = static_cast<int>(m_maxDecimalValueStrings[m_numwidth].length()) - 1;
m_cIntDigitsSav = static_cast<int>(GetMaxDecimalValueString().length()) - 1;
// This is the max digits you can enter a decimal in fixed width mode aka integer mode -1. The last digit
// has to be checked separately
}

View file

@ -1,11 +0,0 @@
add_library(CalcManager
CalculatorHistory.cpp
CalculatorManager.cpp
ExpressionCommand.cpp
pch.cpp
UnitConverter.cpp
)
target_include_directories(CalcManager PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(Ratpack)
add_subdirectory(CEngine)

View file

@ -132,29 +132,9 @@
</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'">
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>

View file

@ -41,21 +41,13 @@ unsigned int CalculatorHistory::AddToHistory(
_In_ shared_ptr<vector<shared_ptr<IExpressionCommand>>> const& commands,
wstring_view result)
{
unsigned int addedIndex;
shared_ptr<HISTORYITEM> spHistoryItem = make_shared<HISTORYITEM>();
spHistoryItem->historyItemVector.spTokens = tokens;
spHistoryItem->historyItemVector.spCommands = commands;
// to be changed when pszexp is back
wstring generatedExpression = GetGeneratedExpression(*tokens);
// Prefixing and suffixing the special Unicode markers to ensure that the expression
// in the history doesn't get broken for RTL languages
spHistoryItem->historyItemVector.expression = L'\u202d' + generatedExpression + L'\u202c';
spHistoryItem->historyItemVector.expression = GetGeneratedExpression(*tokens);
spHistoryItem->historyItemVector.result = wstring(result);
addedIndex = AddItem(spHistoryItem);
return addedIndex;
return AddItem(spHistoryItem);
}
unsigned int CalculatorHistory::AddItem(_In_ shared_ptr<HISTORYITEM> const& spHistoryItem)
@ -66,21 +58,20 @@ unsigned int CalculatorHistory::AddItem(_In_ shared_ptr<HISTORYITEM> const& spHi
}
m_historyItems.push_back(spHistoryItem);
unsigned int lastIndex = static_cast<unsigned>(m_historyItems.size() - 1);
return lastIndex;
return static_cast<unsigned>(m_historyItems.size() - 1);
}
bool CalculatorHistory::RemoveItem(unsigned int uIdx)
{
if (uIdx > m_historyItems.size() - 1)
if (uIdx < m_historyItems.size())
{
return false;
}
m_historyItems.erase(m_historyItems.begin() + uIdx);
return true;
}
return false;
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorHistory::GetHistory()
{
return m_historyItems;

View file

@ -7,12 +7,6 @@
namespace CalculationManager
{
enum CALCULATOR_MODE
{
CM_STD = 0,
CM_SCI,
};
struct HISTORYITEMVECTOR
{
std::shared_ptr<std::vector<std::pair<std::wstring, int>>> spTokens;

View file

@ -10,16 +10,11 @@ using namespace std;
using namespace CalcEngine;
static constexpr size_t MAX_HISTORY_ITEMS = 20;
static constexpr size_t SERIALIZED_NUMBER_MINSIZE = 3;
#ifndef _MSC_VER
#define __pragma(x)
#endif
// Converts Memory Command enum value to unsigned char,
// while ignoring Warning C4309: 'conversion' : truncation of constant value
#define MEMORY_COMMAND_TO_UNSIGNED_CHAR(c) __pragma(warning(push)) __pragma(warning(disable : 4309)) static_cast<unsigned char>(c) __pragma(warning(pop))
namespace CalculationManager
{
CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider)
@ -30,9 +25,9 @@ namespace CalculationManager
, m_persistedPrimaryValue()
, m_isExponentialFormat(false)
, m_currentDegreeMode(Command::CommandNULL)
, m_savedDegreeMode(Command::CommandDEG)
, m_pStdHistory(new CalculatorHistory(MAX_HISTORY_ITEMS))
, m_pSciHistory(new CalculatorHistory(MAX_HISTORY_ITEMS))
, m_pHistory(nullptr)
{
CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider);
}
@ -131,7 +126,6 @@ namespace CalculationManager
/// </summary>
void CalculatorManager::Reset(bool clearMemory /* = true*/)
{
m_savedCommands.clear();
SetStandardMode();
if (m_scientificCalculatorEngine)
@ -238,13 +232,6 @@ namespace CalculationManager
m_currentCalculatorEngine->ProcessCommand(static_cast<OpCode>(command));
}
m_savedCommands.clear(); // Clear the previous command history
if (command != Command::CommandEQU && command != Command::CommandCLEAR)
{
m_savedCommands.push_back(MapCommandForSerialize(command));
}
m_savedDegreeMode = m_currentDegreeMode;
InputChanged();
return;
}
@ -254,11 +241,6 @@ namespace CalculationManager
m_currentDegreeMode = command;
}
if (command != Command::CommandFE)
{
m_savedCommands.push_back(MapCommandForSerialize(command)); // Save the commands in the m_savedCommands
}
switch (command)
{
case Command::CommandASIN:
@ -324,37 +306,6 @@ namespace CalculationManager
InputChanged();
}
/// <summary>
/// Convert Command to unsigned char.
/// Since some Commands are higher than 255, they are saved after subtracting 255
/// The smallest Command is CommandSIGN = 80, thus, subtracted value does not overlap with other values.
/// </summary>
/// <param name="command">Enum Command</command>
unsigned char CalculatorManager::MapCommandForSerialize(Command command)
{
unsigned int commandToSave = static_cast<unsigned int>(command);
if (commandToSave > UCHAR_MAX)
{
commandToSave -= UCHAR_MAX;
}
return static_cast<unsigned char>(commandToSave);
}
/// <summary>
/// Convert Command to unsigned int
/// The command that is smaller than 80, CommandSIGN, can be converted back to original value by adding 255.
/// </summary>
/// <param name="command">unsigned char value represent the saved command</command>
unsigned int CalculatorManager::MapCommandForDeSerialize(unsigned char command)
{
unsigned int commandToLoad = command;
if (command < static_cast<unsigned int>(Command::CommandSIGN))
{
commandToLoad += UCHAR_MAX;
}
return commandToLoad;
}
/// <summary>
/// Load the persisted value that is saved in memory of CalcEngine
/// </summary>
@ -371,8 +322,6 @@ namespace CalculationManager
/// </summary>
void CalculatorManager::MemorizeNumber()
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber));
if (m_currentCalculatorEngine->FInErrorState())
{
return;
@ -400,8 +349,6 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory);
if (m_currentCalculatorEngine->FInErrorState())
{
return;
@ -420,8 +367,6 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory);
if (m_currentCalculatorEngine->FInErrorState())
{
return;
@ -448,7 +393,6 @@ namespace CalculationManager
{
if (indexOfMemory < m_memorizedNumbers.size())
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberClear, indexOfMemory);
m_memorizedNumbers.erase(m_memorizedNumbers.begin() + indexOfMemory);
}
}
@ -461,8 +405,6 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory);
if (m_currentCalculatorEngine->FInErrorState())
{
return;
@ -494,7 +436,6 @@ namespace CalculationManager
/// </summary>
void CalculatorManager::MemorizedNumberClearAll()
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll));
m_memorizedNumbers.clear();
m_currentCalculatorEngine->ProcessCommand(IDC_MCLEAR);
@ -536,24 +477,14 @@ namespace CalculationManager
}
}
void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory)
{
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(command));
if (indexOfMemory > UCHAR_MAX)
{
throw invalid_argument("Unexpected value. IndexOfMemory is bigger than the biggest unsigned char");
}
m_savedCommands.push_back(static_cast<unsigned char>(indexOfMemory));
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems()
{
return m_pHistory->GetHistory();
}
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems(_In_ CALCULATOR_MODE mode)
vector<shared_ptr<HISTORYITEM>> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode)
{
return (mode == CM_STD) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory();
return (mode == CalculatorMode::Standard) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory();
}
shared_ptr<HISTORYITEM> const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx)
@ -576,20 +507,20 @@ namespace CalculationManager
m_pHistory->ClearHistory();
}
void CalculatorManager::SetRadix(RADIX_TYPE iRadixType)
void CalculatorManager::SetRadix(RadixType iRadixType)
{
switch (iRadixType)
{
case RADIX_TYPE::HEX_RADIX:
case RadixType::Hex:
m_currentCalculatorEngine->ProcessCommand(IDC_HEX);
break;
case RADIX_TYPE::DEC_RADIX:
case RadixType::Decimal:
m_currentCalculatorEngine->ProcessCommand(IDC_DEC);
break;
case RADIX_TYPE::OCT_RADIX:
case RadixType::Octal:
m_currentCalculatorEngine->ProcessCommand(IDC_OCT);
break;
case RADIX_TYPE::BIN_RADIX:
case RadixType::Binary:
m_currentCalculatorEngine->ProcessCommand(IDC_BIN);
break;
default:
@ -603,7 +534,7 @@ namespace CalculationManager
vector<wstring> resultVector;
for (auto const& memoryItem : m_memorizedNumbers)
{
int radix = m_currentCalculatorEngine->GetCurrentRadix();
auto radix = m_currentCalculatorEngine->GetCurrentRadix();
wstring stringValue = m_currentCalculatorEngine->GetStringForDisplay(memoryItem, radix);
if (!stringValue.empty())
@ -623,30 +554,6 @@ namespace CalculationManager
return m_currentDegreeMode;
}
void CalculatorManager::SetHistory(_In_ CALCULATOR_MODE eMode, _In_ vector<shared_ptr<HISTORYITEM>> const& history)
{
CalculatorHistory* pHistory = nullptr;
switch (eMode)
{
case CM_STD:
pHistory = m_pStdHistory.get();
break;
case CM_SCI:
pHistory = m_pSciHistory.get();
break;
}
if (pHistory)
{
pHistory->ClearHistory();
for (auto const& historyItem : history)
{
pHistory->AddItem(historyItem);
}
}
}
wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix)
{
return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L"";
@ -669,7 +576,7 @@ namespace CalculationManager
bool CalculatorManager::IsEngineRecording()
{
return m_currentCalculatorEngine->FInRecordingState() ? true : false;
return m_currentCalculatorEngine->FInRecordingState();
}
bool CalculatorManager::IsInputEmpty()

View file

@ -15,9 +15,8 @@ namespace CalculationManager
enum class CalculatorMode
{
StandardMode,
ScientificMode,
ProgrammerMode,
Standard = 0,
Scientific,
};
enum class CalculatorPrecision
@ -45,6 +44,7 @@ namespace CalculationManager
class CalculatorManager final : public ICalcDisplay
{
private:
static const unsigned int m_maximumMemorySize = 100;
ICalcDisplay* const m_displayCallback;
CCalcEngine* m_currentCalculatorEngine;
std::unique_ptr<CCalcEngine> m_scientificCalculatorEngine;
@ -55,21 +55,8 @@ namespace CalculationManager
std::vector<CalcEngine::Rational> m_memorizedNumbers;
CalcEngine::Rational m_persistedPrimaryValue;
bool m_isExponentialFormat;
static const unsigned int m_maximumMemorySize = 100;
// For persistence
std::vector<unsigned char> m_savedCommands;
std::vector<long> m_savedPrimaryValue;
std::vector<long> m_currentSerializedMemory;
Command m_currentDegreeMode;
Command m_savedDegreeMode;
unsigned char MapCommandForSerialize(Command command);
unsigned int MapCommandForDeSerialize(unsigned char command);
void SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory);
void MemorizedNumberSelect(_In_ unsigned int);
void MemorizedNumberChanged(_In_ unsigned int);
@ -113,11 +100,7 @@ namespace CalculationManager
bool IsEngineRecording();
bool IsInputEmpty();
const std::vector<unsigned char>& GetSavedCommands() const
{
return m_savedCommands;
}
void SetRadix(RADIX_TYPE iRadixType);
void SetRadix(RadixType iRadixType);
void SetMemorizedNumbersString();
std::wstring GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix);
void SetPrecision(int32_t precision);
@ -125,7 +108,7 @@ namespace CalculationManager
wchar_t DecimalSeparator();
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems();
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculationManager::CALCULATOR_MODE mode);
std::vector<std::shared_ptr<HISTORYITEM>> const& GetHistoryItems(_In_ CalculatorMode mode);
std::shared_ptr<HISTORYITEM> const& GetHistoryItem(_In_ unsigned int uIdx);
bool RemoveHistoryItem(_In_ unsigned int uIdx);
void ClearHistory();
@ -134,7 +117,6 @@ namespace CalculationManager
return m_pHistory->MaxHistorySize();
}
CalculationManager::Command GetCurrentDegreeMode();
void SetHistory(_In_ CALCULATOR_MODE eMode, _In_ std::vector<std::shared_ptr<HISTORYITEM>> const& history);
void SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode);
};
}

View file

@ -0,0 +1,156 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#pragma once
#include <string>
#include <vector>
#include "winerror_cross_platform.h"
#include "Ratpack/CalcErr.h"
#include <stdexcept> // for std::out_of_range
#include "sal_cross_platform.h" // for SAL
template <typename TType>
class CalculatorVector
{
public:
ResultCode GetAt(_In_opt_ unsigned int index, _Out_ TType* item)
{
try
{
*item = m_vector.at(index);
}
catch (const std::out_of_range& /*ex*/)
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode GetSize(_Out_ unsigned int* size)
{
*size = static_cast<unsigned>(m_vector.size());
return S_OK;
}
ResultCode SetAt(_In_ unsigned int index, _In_opt_ TType item)
{
try
{
m_vector[index] = item;
}
catch (const std::out_of_range& /*ex*/)
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode RemoveAt(_In_ unsigned int index)
{
if (index < m_vector.size())
{
m_vector.erase(m_vector.begin() + index);
}
else
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode InsertAt(_In_ unsigned int index, _In_ TType item)
{
try
{
auto iter = m_vector.begin() + index;
m_vector.insert(iter, item);
}
catch (const std::bad_alloc& /*ex*/)
{
return E_OUTOFMEMORY;
}
return S_OK;
}
ResultCode Truncate(_In_ unsigned int index)
{
if (index < m_vector.size())
{
auto startIter = m_vector.begin() + index;
m_vector.erase(startIter, m_vector.end());
}
else
{
return E_BOUNDS;
}
return S_OK;
}
ResultCode Append(_In_opt_ TType item)
{
try
{
m_vector.push_back(item);
}
catch (const std::bad_alloc& /*ex*/)
{
return E_OUTOFMEMORY;
}
return S_OK;
}
ResultCode RemoveAtEnd()
{
m_vector.erase(--(m_vector.end()));
return S_OK;
}
ResultCode Clear()
{
m_vector.clear();
return S_OK;
}
ResultCode GetString(_Out_ std::wstring* expression)
{
unsigned int nTokens = 0;
ResultCode hr = this->GetSize(&nTokens);
if (SUCCEEDED(hr))
{
std::pair<std::wstring, int> currentPair;
for (unsigned int i = 0; i < nTokens; i++)
{
hr = this->GetAt(i, &currentPair);
if (SUCCEEDED(hr))
{
expression->append(currentPair.first);
if (i != (nTokens - 1))
{
expression->append(L" ");
}
}
}
std::wstring expressionSuffix{};
hr = GetExpressionSuffix(&expressionSuffix);
if (SUCCEEDED(hr))
{
expression->append(expressionSuffix);
}
}
return hr;
}
ResultCode GetExpressionSuffix(_Out_ std::wstring* suffix)
{
*suffix = L" =";
return S_OK;
}
private:
std::vector<TType> m_vector;
};

View file

@ -69,10 +69,6 @@ namespace CalculationManager
CommandNULL = 0,
// No new command should not be added before CommandSign, 80
// If it is needed, the following two functions need to be revised too.
// CalculatorManager::MapCommandForSerialize(Command command);
// CalculatorManager::MapCommandForDeSerialize(unsigned char command);
CommandSIGN = 80,
CommandCLEAR = 81,
CommandCENTR = 82,
@ -172,7 +168,7 @@ namespace CalculationManager
CommandCeil = 415,
CommandROLC = 416,
CommandRORC = 417,
CommandLogBaseX = 500,
CommandLogBaseY = 500,
CommandNand = 501,
CommandNor = 502,

View file

@ -277,7 +277,7 @@ wstring COpndCommand::GetString(uint32_t radix, int32_t precision)
{
if (m_fInitialized)
{
return m_value.ToString(radix, eNUMOBJ_FMT::FMT_FLOAT, precision);
return m_value.ToString(radix, NumberFormat::Float, precision);
}
return wstring{};

View file

@ -166,7 +166,7 @@
#define IDC_LASTCONTROL IDC_CEIL
#define IDC_BINARYEXTENDEDFIRST 500
#define IDC_LOGBASEX 500 // logx(y)
#define IDC_LOGBASEY 500 // logy(x)
#define IDC_NAND 501 // Nand
#define IDC_NOR 502 // Nor

View file

@ -31,14 +31,13 @@
// The real exports follows later
// This is expected to be in same order as IDM_QWORD, IDM_DWORD etc.
enum eNUM_WIDTH
enum class NUM_WIDTH
{
QWORD_WIDTH, // Number width of 64 bits mode (default)
DWORD_WIDTH, // Number width of 32 bits mode
WORD_WIDTH, // Number width of 16 bits mode
BYTE_WIDTH // Number width of 16 bits mode
};
typedef enum eNUM_WIDTH NUM_WIDTH;
static constexpr size_t NUM_WIDTH_LENGTH = 4;
namespace CalculationManager
@ -78,7 +77,7 @@ public:
}
void SettingsChanged();
bool IsCurrentTooBigForTrig();
int GetCurrentRadix();
uint32_t GetCurrentRadix();
std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix);
void ChangePrecision(int32_t precision)
{
@ -106,7 +105,7 @@ public:
{
return GetString(IdStrFromCmdId(nOpCode));
}
static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype);
static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype);
static std::wstring_view OpCodeToBinaryString(int nOpCode, bool isIntegerMode);
private:
@ -117,11 +116,11 @@ private:
int m_nOpCode; /* ID value of operation. */
int m_nPrevOpCode; // opcode which computed the number in m_currentVal. 0 if it is already bracketed or plain number or
// if it hasn't yet been computed
bool m_bChangeOp; /* Flag for changing operation. */
bool m_bChangeOp; // Flag for changing operation
bool m_bRecord; // Global mode: recording or displaying
bool m_bSetCalcState; // Flag for setting the engine result state
CalcEngine::CalcInput m_input; // Global calc input object for decimal strings
eNUMOBJ_FMT m_nFE; /* Scientific notation conversion flag. */
NumberFormat m_nFE; // Scientific notation conversion flag
CalcEngine::Rational m_maxTrigonometricNum;
std::unique_ptr<CalcEngine::Rational> m_memoryValue; // Current memory value.
@ -148,7 +147,7 @@ private:
std::array<int, MAXPRECDEPTH> m_nPrecOp; /* Holding array for precedence operations. */
size_t m_precedenceOpCount; /* Current number of precedence ops in holding. */
int m_nLastCom; // Last command entered.
ANGLE_TYPE m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad
AngleType m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad
NUM_WIDTH m_numwidth; // one of qword, dword, word or byte mode.
int32_t m_dwWordBitWidth; // # of bits in currently selected word size
@ -179,15 +178,17 @@ private:
CalcEngine::Rational TruncateNumForIntMath(CalcEngine::Rational const& rat);
CalcEngine::Rational SciCalcFunctions(CalcEngine::Rational const& rat, uint32_t op);
CalcEngine::Rational DoOperation(int operation, CalcEngine::Rational const& lhs, CalcEngine::Rational const& rhs);
void SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth);
void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth);
int32_t DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth);
uint32_t NRadixFromRadixType(RADIX_TYPE radixtype);
uint32_t NRadixFromRadixType(RadixType radixtype);
double GenerateRandomNumber();
bool TryToggleBit(CalcEngine::Rational& rat, uint32_t wbitno);
void CheckAndAddLastBinOpToHistory(bool addToHistory = true);
void InitChopNumbers();
CalcEngine::Rational GetChopNumber() const;
std::wstring GetMaxDecimalValueString() const;
static void LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider);
static int IdStrFromCmdId(int id)

View file

@ -196,7 +196,7 @@ inline constexpr auto SIDS_ACSCH = L"InverseCsch";
inline constexpr auto SIDS_COTH = L"Coth";
inline constexpr auto SIDS_ACOTH = L"InverseCoth";
inline constexpr auto SIDS_TWOPOWX = L"TwoPowX";
inline constexpr auto SIDS_LOGBASEX = L"LogBaseX";
inline constexpr auto SIDS_LOGBASEY = L"LogBaseY";
inline constexpr auto SIDS_ABS = L"Abs";
inline constexpr auto SIDS_FLOOR = L"Floor";
inline constexpr auto SIDS_CEIL = L"Ceil";
@ -352,7 +352,7 @@ inline constexpr std::array<std::wstring_view, 152> g_sids =
SIDS_COTH,
SIDS_ACOTH,
SIDS_TWOPOWX,
SIDS_LOGBASEX,
SIDS_LOGBASEY,
SIDS_ABS,
SIDS_FLOOR,
SIDS_CEIL,

View file

@ -23,7 +23,7 @@ public:
void RemoveLastOpndFromHistory();
void AddBinOpToHistory(int nOpCode, bool isIntgerMode, bool fNoRepetition = true);
void ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher, bool isIntgerMode);
void AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype);
void AddUnaryOpToHistory(int nOpCode, bool fInv, AngleType angletype);
void AddOpenBraceToHistory();
void AddCloseBraceToHistory();
void PushLastOpndStart(int ichOpndStart = -1);

View file

@ -4,11 +4,10 @@
#pragma once
// This is expected to be in same order as IDM_HEX, IDM_DEC, IDM_OCT, IDM_BIN
enum eRADIX_TYPE
enum class RadixType
{
HEX_RADIX,
DEC_RADIX,
OCT_RADIX,
BIN_RADIX
Hex,
Decimal,
Octal,
Binary
};
typedef enum eRADIX_TYPE RADIX_TYPE;

View file

@ -64,7 +64,7 @@ namespace CalcEngine
friend bool operator<=(Rational const& lhs, Rational const& rhs);
friend bool operator>=(Rational const& lhs, Rational const& rhs);
std::wstring ToString(uint32_t radix, NUMOBJ_FMT format, int32_t precision) const;
std::wstring ToString(uint32_t radix, NumberFormat format, int32_t precision) const;
uint64_t ToUInt64_t() const;
private:

View file

@ -22,12 +22,12 @@ namespace CalcEngine::RationalMath
Rational Invert(Rational const& rat);
Rational Abs(Rational const& rat);
Rational Sin(Rational const& rat, ANGLE_TYPE angletype);
Rational Cos(Rational const& rat, ANGLE_TYPE angletype);
Rational Tan(Rational const& rat, ANGLE_TYPE angletype);
Rational ASin(Rational const& rat, ANGLE_TYPE angletype);
Rational ACos(Rational const& rat, ANGLE_TYPE angletype);
Rational ATan(Rational const& rat, ANGLE_TYPE angletype);
Rational Sin(Rational const& rat, AngleType angletype);
Rational Cos(Rational const& rat, AngleType angletype);
Rational Tan(Rational const& rat, AngleType angletype);
Rational ASin(Rational const& rat, AngleType angletype);
Rational ACos(Rational const& rat, AngleType angletype);
Rational ATan(Rational const& rat, AngleType angletype);
Rational Sinh(Rational const& rat);
Rational Cosh(Rational const& rat);

View file

@ -50,15 +50,15 @@ namespace CalcManager::NumberFormattingUtils
/// <param name="value">the number</param>
unsigned int GetNumberDigitsWholeNumberPart(double value)
{
return value == 0 ? 1 : (1 + (int)log10(abs(value)));
return value == 0 ? 1u : static_cast<unsigned int>(1 + max(0.0, log10(abs(value))));
}
/// <summary>
/// Rounds the given double to the given number of significant digits
/// </summary>
/// <param name="num">input double</param>
/// <param name="numSignificant">int number of significant digits to round to</param>
wstring RoundSignificantDigits(double num, int numSignificant)
/// <param name="numSignificant">unsigned int number of significant digits to round to</param>
wstring RoundSignificantDigits(double num, unsigned int numSignificant)
{
wstringstream out(wstringstream::out);
out << fixed;

View file

@ -10,6 +10,6 @@ namespace CalcManager::NumberFormattingUtils
void TrimTrailingZeros(_Inout_ std::wstring& input);
unsigned int GetNumberDigits(std::wstring value);
unsigned int GetNumberDigitsWholeNumberPart(double value);
std::wstring RoundSignificantDigits(double value, int numberSignificantDigits);
std::wstring RoundSignificantDigits(double value, unsigned int numberSignificantDigits);
std::wstring ToScientificNumber(double number);
}

View file

@ -1,14 +0,0 @@
target_sources(CalcManager PRIVATE
basex.cpp
conv.cpp
exp.cpp
fact.cpp
itrans.cpp
itransh.cpp
logic.cpp
num.cpp
rat.cpp
support.cpp
trans.cpp
transh.cpp
)

View file

@ -303,17 +303,13 @@ PRAT numtorat(_In_ PNUMBER pin, uint32_t radix)
PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision)
{
uint32_t bitmask;
uint32_t cdigits;
MANTTYPE* ptr;
PNUMBER sum = i32tonum(0, radix);
PNUMBER powofnRadix = i32tonum(BASEX, radix);
// A large penalty is paid for conversion of digits no one will see anyway.
// limit the digits to the minimum of the existing precision or the
// requested precision.
cdigits = precision + 1;
uint32_t cdigits = precision + 1;
if (cdigits > (uint32_t)a->cdigit)
{
cdigits = (uint32_t)a->cdigit;
@ -323,10 +319,10 @@ PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision)
numpowi32(&powofnRadix, a->exp + (a->cdigit - cdigits), radix, precision);
// Loop over all the relative digits from MSD to LSD
for (ptr = &(a->mant[a->cdigit - 1]); cdigits > 0; ptr--, cdigits--)
for (MANTTYPE* ptr = &(a->mant[a->cdigit - 1]); cdigits > 0; ptr--, cdigits--)
{
// Loop over all the bits from MSB to LSB
for (bitmask = BASEX / 2; bitmask > 0; bitmask /= 2)
for (uint32_t bitmask = BASEX / 2; bitmask > 0; bitmask /= 2)
{
addnum(&sum, sum, radix);
if (*ptr & bitmask)
@ -368,9 +364,7 @@ PNUMBER numtonRadixx(_In_ PNUMBER a, uint32_t radix)
ptrdigit += a->cdigit - 1;
PNUMBER thisdigit = nullptr; // thisdigit holds the current digit of a
// being summed into result.
int32_t idigit; // idigit is the iterate of digits in a.
for (idigit = 0; idigit < a->cdigit; idigit++)
for (int32_t idigit = 0; idigit < a->cdigit; idigit++)
{
mulnumx(&pnumret, num_radix);
// WARNING:
@ -628,11 +622,10 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis
MANTTYPE* pmant = pnumret->mant + numberString.length() - 1;
uint8_t state = START; // state is the state of the input state machine.
wchar_t curChar;
for (const auto& c : numberString)
{
// If the character is the decimal separator, use L'.' for the purposes of the state machine.
curChar = (c == g_decimalSeparator ? L'.' : c);
wchar_t curChar = (c == g_decimalSeparator ? L'.' : c);
// Switch states based on the character we encountered
switch (curChar)
@ -1032,13 +1025,10 @@ int32_t numtoi32(_In_ PNUMBER pnum, uint32_t radix)
bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting)
{
MANTTYPE* pmant;
int32_t cdigits;
bool fstrip = false;
// point pmant to the LeastCalculatedDigit
pmant = pnum->mant;
cdigits = pnum->cdigit;
MANTTYPE* pmant = pnum->mant;
int32_t cdigits = pnum->cdigit;
// point pmant to the LSD
if (cdigits > starting)
{
@ -1073,27 +1063,27 @@ bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting)
// FUNCTION: NumberToString
//
// ARGUMENTS: number representation
// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or
// FMT_ENGINEERING
// fmt, one of NumberFormat::Float, NumberFormat::Scientific or
// NumberFormat::Engineering
// integer radix and int32_t precision value
//
// RETURN: String representation of number.
//
// DESCRIPTION: Converts a number to it's string
// DESCRIPTION: Converts a number to its string
// representation.
//
//-----------------------------------------------------------------------------
wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision)
wstring NumberToString(_Inout_ PNUMBER& pnum, NumberFormat format, uint32_t radix, int32_t precision)
{
stripzeroesnum(pnum, precision + 2);
int32_t length = pnum->cdigit;
int32_t exponent = pnum->exp + length; // Actual number of digits to the left of decimal
int32_t oldFormat = format;
if (exponent > precision && format == FMT_FLOAT)
NumberFormat oldFormat = format;
if (exponent > precision && format == NumberFormat::Float)
{
// Force scientific mode to prevent user from assuming 33rd digit is exact.
format = FMT_SCIENTIFIC;
format = NumberFormat::Scientific;
}
// Make length small enough to fit in pret.
@ -1113,7 +1103,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
divnum(&round, num_two, radix, precision);
// Make round number exponent one below the LSD for the number.
if (exponent > 0 || format == FMT_FLOAT)
if (exponent > 0 || format == NumberFormat::Float)
{
round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision;
}
@ -1126,7 +1116,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
round->sign = pnum->sign;
}
if (format == FMT_FLOAT)
if (format == NumberFormat::Float)
{
// Figure out if the exponent will fill more space than the non-exponent field.
if ((length - exponent > precision) || (exponent > precision + 3))
@ -1140,7 +1130,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
{
// Case where too many zeros are to the right or left of the
// decimal pt. And we are forced to switch to scientific form.
format = FMT_SCIENTIFIC;
format = NumberFormat::Scientific;
}
}
else if (length + abs(exponent) < precision && round)
@ -1173,13 +1163,13 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
int32_t eout = exponent - 1; // Displayed exponent.
MANTTYPE* pmant = pnum->mant + pnum->cdigit - 1;
// Case where too many digits are to the left of the decimal or
// FMT_SCIENTIFIC or FMT_ENGINEERING was specified.
if ((format == FMT_SCIENTIFIC) || (format == FMT_ENGINEERING))
// NumberFormat::Scientific or NumberFormat::Engineering was specified.
if ((format == NumberFormat::Scientific) || (format == NumberFormat::Engineering))
{
useSciForm = true;
if (eout != 0)
{
if (format == FMT_ENGINEERING)
if (format == NumberFormat::Engineering)
{
exponent = (eout % 3);
eout -= exponent;
@ -1280,7 +1270,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
// ARGUMENTS:
// PRAT *representation of a number.
// i32 representation of base to dump to screen.
// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or FMT_ENGINEERING
// fmt, one of NumberFormat::Float, NumberFormat::Scientific, or NumberFormat::Engineering
// precision uint32_t
//
// RETURN: string
@ -1293,7 +1283,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_
// why a pointer to the rational is passed in.
//
//-----------------------------------------------------------------------------
wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision)
wstring RatToString(_Inout_ PRAT& prat, NumberFormat format, uint32_t radix, int32_t precision)
{
PNUMBER p = RatToNumber(prat, radix, precision);

View file

@ -63,7 +63,6 @@ void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
{
PRAT pwr = nullptr;
PRAT pint = nullptr;
int32_t intpwr;
if (rat_gt(*px, rat_max_exp, precision) || rat_lt(*px, rat_min_exp, precision))
{
@ -76,7 +75,7 @@ void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
intrat(&pint, radix, precision);
intpwr = rattoi32(pint, radix, precision);
const int32_t intpwr = rattoi32(pint, radix, precision);
ratpowi32(&pwr, intpwr, precision);
subrat(px, pint, precision);
@ -153,7 +152,6 @@ void _lograt(PRAT* px, int32_t precision)
void lograt(_Inout_ PRAT* px, int32_t precision)
{
bool fneglog;
PRAT pwr = nullptr; // pwr is the large scaling factor.
PRAT offset = nullptr; // offset is the incremental scaling factor.
@ -164,12 +162,10 @@ void lograt(_Inout_ PRAT* px, int32_t precision)
}
// Get number > 1, for scaling
fneglog = rat_lt(*px, rat_one, precision);
bool fneglog = rat_lt(*px, rat_one, precision);
if (fneglog)
{
// WARNING: This is equivalent to doing *px = 1 / *px
PNUMBER pnumtemp = nullptr;
pnumtemp = (*px)->pp;
PNUMBER pnumtemp = (*px)->pp;
(*px)->pp = (*px)->pq;
(*px)->pq = pnumtemp;
}
@ -178,10 +174,7 @@ void lograt(_Inout_ PRAT* px, int32_t precision)
// log(x*2^(BASEXPWR*k)) = BASEXPWR*k*log(2)+log(x)
if (LOGRAT2(*px) > 1)
{
// Take advantage of px's base BASEX to scale quickly down to
// a reasonable range.
int32_t intpwr;
intpwr = LOGRAT2(*px) - 1;
const int32_t intpwr = LOGRAT2(*px) - 1;
(*px)->pq->exp += intpwr;
pwr = i32torat(intpwr * BASEXPWR);
mulrat(&pwr, ln_two, precision);
@ -448,10 +441,9 @@ void powratcomp(_Inout_ PRAT* px, _In_ PRAT y, uint32_t radix, int32_t precision
{
// If power is an integer let ratpowi32 deal with it.
PRAT iy = nullptr;
int32_t inty;
DUPRAT(iy, y);
subrat(&iy, podd, precision);
inty = rattoi32(iy, radix, precision);
int32_t inty = rattoi32(iy, radix, precision);
PRAT plnx = nullptr;
DUPRAT(plnx, *px);

View file

@ -67,13 +67,9 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision)
PRAT sum = nullptr;
PRAT err = nullptr;
PRAT mpy = nullptr;
PRAT ratprec = nullptr;
PRAT ratRadix = nullptr;
int32_t oldprec;
// Set up constants and initial conditions
oldprec = precision;
ratprec = i32torat(oldprec);
PRAT ratprec = i32torat(precision);
// Find the best 'A' for convergence to the required precision.
a = i32torat(radix);
@ -102,7 +98,7 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision)
exprat(&tmp, radix, precision);
mulrat(&term, tmp, precision);
lograt(&term, precision);
ratRadix = i32torat(radix);
const auto ratRadix = i32torat(radix);
DUPRAT(tmp, ratRadix);
lograt(&tmp, precision);
subrat(&term, tmp, precision);
@ -173,7 +169,6 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision)
mulrat(&sum, mpy, precision);
// And cleanup
precision = oldprec;
destroyrat(ratprec);
destroyrat(err);
destroyrat(term);

View file

@ -17,17 +17,17 @@
//-----------------------------------------------------------------------------
#include "ratpak.h"
void ascalerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, int32_t precision)
void ascalerat(_Inout_ PRAT* pa, AngleType angletype, int32_t precision)
{
switch (angletype)
{
case ANGLE_RAD:
case AngleType::Radians:
break;
case ANGLE_DEG:
case AngleType::Degrees:
divrat(pa, two_pi, precision);
mulrat(pa, rat_360, precision);
break;
case ANGLE_GRAD:
case AngleType::Gradians:
divrat(pa, two_pi, precision);
mulrat(pa, rat_400, precision);
break;
@ -76,7 +76,7 @@ void _asinrat(PRAT* px, int32_t precision)
DESTROYTAYLOR();
}
void asinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void asinanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
asinrat(pa, radix, precision);
@ -164,7 +164,7 @@ void asinrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
//
//-----------------------------------------------------------------------------
void acosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void acosanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
acosrat(pa, radix, precision);
@ -249,7 +249,7 @@ void acosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
//
//-----------------------------------------------------------------------------
void atananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void atananglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
atanrat(pa, radix, precision);

View file

@ -21,7 +21,6 @@ void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision)
{
PRAT pwr = nullptr;
int32_t intb;
intrat(pa, radix, precision);
if (!zernum((*pa)->pp))
@ -32,7 +31,7 @@ void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision)
// Don't attempt lsh of anything big
throw(CALC_E_DOMAIN);
}
intb = rattoi32(b, radix, precision);
const int32_t intb = rattoi32(b, radix, precision);
DUPRAT(pwr, rat_two);
ratpowi32(&pwr, intb, precision);
mulrat(pa, pwr, precision);
@ -44,7 +43,6 @@ void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision)
{
PRAT pwr = nullptr;
int32_t intb;
intrat(pa, radix, precision);
if (!zernum((*pa)->pp))
@ -55,7 +53,7 @@ void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision)
// Don't attempt rsh of anything big and negative.
throw(CALC_E_DOMAIN);
}
intb = rattoi32(b, radix, precision);
const int32_t intb = rattoi32(b, radix, precision);
DUPRAT(pwr, rat_two);
ratpowi32(&pwr, intb, precision);
divrat(pa, pwr, precision);

View file

@ -67,8 +67,7 @@ void _addnum(PNUMBER* pa, PNUMBER b, uint32_t radix)
MANTTYPE* pcha; // pcha is a pointer to the mantissa of a.
MANTTYPE* pchb; // pchb is a pointer to the mantissa of b.
MANTTYPE* pchc; // pchc is a pointer to the mantissa of c.
int32_t cdigits; // cdigits is the max count of the digits results
// used as a counter.
int32_t cdigits; // cdigits is the max count of the digits results used as a counter.
int32_t mexp; // mexp is the exponent of the result.
MANTTYPE da; // da is a single 'digit' after possible padding.
MANTTYPE db; // db is a single 'digit' after possible padding.
@ -558,38 +557,26 @@ bool equnum(_In_ PNUMBER a, _In_ PNUMBER b)
bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b)
{
int32_t diff;
MANTTYPE* pa;
MANTTYPE* pb;
int32_t cdigits;
int32_t ccdigits;
MANTTYPE da;
MANTTYPE db;
diff = (a->cdigit + a->exp) - (b->cdigit + b->exp);
int32_t diff = (a->cdigit + a->exp) - (b->cdigit + b->exp);
if (diff < 0)
{
// The exponent of a is less than b
return true;
}
else
{
if (diff > 0)
{
return false;
}
else
{
pa = a->mant;
pb = b->mant;
MANTTYPE* pa = a->mant;
MANTTYPE* pb = b->mant;
pa += a->cdigit - 1;
pb += b->cdigit - 1;
cdigits = max(a->cdigit, b->cdigit);
ccdigits = cdigits;
int32_t cdigits = max(a->cdigit, b->cdigit);
int32_t ccdigits = cdigits;
for (; cdigits > 0; cdigits--)
{
da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0);
db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0);
MANTTYPE da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0);
MANTTYPE db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0);
diff = da - db;
if (diff)
{
@ -599,8 +586,6 @@ bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b)
// In this case, they are equal.
return false;
}
}
}
//----------------------------------------------------------------------------
//

View file

@ -31,25 +31,20 @@ static constexpr uint32_t BASEX = 0x80000000; // Internal radix used in calculat
typedef uint32_t MANTTYPE;
typedef uint64_t TWO_MANTTYPE;
enum eNUMOBJ_FMT
enum class NumberFormat
{
FMT_FLOAT, // returns floating point, or exponential if number is too big
FMT_SCIENTIFIC, // always returns scientific notation
FMT_ENGINEERING // always returns engineering notation such that exponent is a multiple of 3
Float, // returns floating point, or exponential if number is too big
Scientific, // always returns scientific notation
Engineering // always returns engineering notation such that exponent is a multiple of 3
};
enum eANGLE_TYPE
enum class AngleType
{
ANGLE_DEG, // Calculate trig using 360 degrees per revolution
ANGLE_RAD, // Calculate trig using 2 pi radians per revolution
ANGLE_GRAD // Calculate trig using 400 gradients per revolution
Degrees, // Calculate trig using 360 degrees per revolution
Radians, // Calculate trig using 2 pi radians per revolution
Gradians // Calculate trig using 400 gradians per revolution
};
typedef enum eNUMOBJ_FMT NUMOBJ_FMT;
typedef enum eANGLE_TYPE ANGLE_TYPE;
//-----------------------------------------------------------------------------
//
// NUMBER type is a representation of a generic sized generic radix number
@ -341,10 +336,10 @@ extern bool equnum(_In_ PNUMBER a, _In_ PNUMBER b); // returns true of a == b
extern bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b); // returns true of a < b
extern bool zernum(_In_ PNUMBER a); // returns true of a == 0
extern bool zerrat(_In_ PRAT a); // returns true if a == 0/q
extern std::wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision);
extern std::wstring NumberToString(_Inout_ PNUMBER& pnum, NumberFormat format, uint32_t radix, int32_t precision);
// returns a text representation of a PRAT
extern std::wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision);
extern std::wstring RatToString(_Inout_ PRAT& prat, NumberFormat format, uint32_t radix, int32_t precision);
// converts a PRAT into a PNUMBER
extern PNUMBER RatToNumber(_In_ PRAT prat, uint32_t radix, int32_t precision);
// flattens a PRAT by converting it to a PNUMBER and back to a PRAT
@ -376,7 +371,7 @@ extern PRAT _createrat(void);
// returns a new rat structure with the acos of x->p/x->q taking into account
// angle type
extern void acosanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void acosanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the acosh of x->p/x->q
extern void acoshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
@ -386,7 +381,7 @@ extern void acosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
// returns a new rat structure with the asin of x->p/x->q taking into account
// angle type
extern void asinanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void asinanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
extern void asinhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
// returns a new rat structure with the asinh of x->p/x->q
@ -396,7 +391,7 @@ extern void asinrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
// returns a new rat structure with the atan of x->p/x->q taking into account
// angle type
extern void atananglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void atananglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the atanh of x->p/x->q
extern void atanhrat(_Inout_ PRAT* px, int32_t precision);
@ -412,7 +407,7 @@ extern void cosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
// returns a new rat structure with the cos of x->p/x->q taking into account
// angle type
extern void cosanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void cosanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
// returns a new rat structure with the exp of x->p/x->q this should not be called explicitly.
extern void _exprat(_Inout_ PRAT* px, int32_t precision);
@ -435,14 +430,14 @@ extern void sinrat(_Inout_ PRAT* px);
// returns a new rat structure with the sin of x->p/x->q taking into account
// angle type
extern void sinanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void sinanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
extern void tanhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
extern void tanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision);
// returns a new rat structure with the tan of x->p/x->q taking into account
// angle type
extern void tananglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision);
extern void tananglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision);
extern void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER* const src);

View file

@ -596,15 +596,13 @@ void _dumprawrat(_In_ const wchar_t* varname, _In_ PRAT rat, wostream& out)
void _dumprawnum(_In_ const wchar_t* varname, _In_ PNUMBER num, wostream& out)
{
int i;
out << L"NUMBER " << varname << L" = {\n";
out << L"\t" << num->sign << L",\n";
out << L"\t" << num->cdigit << L",\n";
out << L"\t" << num->exp << L",\n";
out << L"\t{ ";
for (i = 0; i < num->cdigit; i++)
for (int i = 0; i < num->cdigit; i++)
{
out << L" " << num->mant[i] << L",";
}
@ -681,10 +679,9 @@ void trimit(_Inout_ PRAT* px, int32_t precision)
{
if (!g_ftrueinfinite)
{
int32_t trim;
PNUMBER pp = (*px)->pp;
PNUMBER pq = (*px)->pq;
trim = g_ratio * (min((pp->cdigit + pp->exp), (pq->cdigit + pq->exp)) - 1) - precision;
int32_t trim = g_ratio * (min((pp->cdigit + pp->exp), (pq->cdigit + pq->exp)) - 1) - precision;
if (trim > g_ratio)
{
trim /= g_ratio;

View file

@ -16,17 +16,17 @@
#include "ratpak.h"
void scalerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void scalerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
switch (angletype)
{
case ANGLE_RAD:
case AngleType::Radians:
scale2pi(pa, radix, precision);
break;
case ANGLE_DEG:
case AngleType::Degrees:
scale(pa, rat_360, radix, precision);
break;
case ANGLE_GRAD:
case AngleType::Gradians:
scale(pa, rat_400, radix, precision);
break;
}
@ -98,13 +98,13 @@ void sinrat(PRAT* px, uint32_t radix, int32_t precision)
_sinrat(px, precision);
}
void sinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void sinanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
scalerat(pa, angletype, radix, precision);
switch (angletype)
{
case ANGLE_DEG:
case AngleType::Degrees:
if (rat_gt(*pa, rat_180, precision))
{
subrat(pa, rat_360, precision);
@ -112,7 +112,7 @@ void sinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t
divrat(pa, rat_180, precision);
mulrat(pa, pi, precision);
break;
case ANGLE_GRAD:
case AngleType::Gradians:
if (rat_gt(*pa, rat_200, precision))
{
subrat(pa, rat_400, precision);
@ -193,13 +193,13 @@ void cosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
_cosrat(px, radix, precision);
}
void cosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void cosanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
scalerat(pa, angletype, radix, precision);
switch (angletype)
{
case ANGLE_DEG:
case AngleType::Degrees:
if (rat_gt(*pa, rat_180, precision))
{
PRAT ptmp = nullptr;
@ -211,7 +211,7 @@ void cosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t
divrat(pa, rat_180, precision);
mulrat(pa, pi, precision);
break;
case ANGLE_GRAD:
case AngleType::Gradians:
if (rat_gt(*pa, rat_200, precision))
{
PRAT ptmp = nullptr;
@ -263,13 +263,13 @@ void tanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision)
_tanrat(px, radix, precision);
}
void tananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision)
void tananglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision)
{
scalerat(pa, angletype, radix, precision);
switch (angletype)
{
case ANGLE_DEG:
case AngleType::Degrees:
if (rat_gt(*pa, rat_180, precision))
{
subrat(pa, rat_180, precision);
@ -277,7 +277,7 @@ void tananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t
divrat(pa, rat_180, precision);
mulrat(pa, pi, precision);
break;
case ANGLE_GRAD:
case AngleType::Gradians:
if (rat_gt(*pa, rat_200, precision))
{
subrat(pa, rat_200, precision);

View file

@ -13,19 +13,19 @@ using namespace std;
using namespace UnitConversionManager;
using namespace CalcManager::NumberFormattingUtils;
static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3;
static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6;
static constexpr uint32_t EXPECTEDSTATEDATATOKENCOUNT = 5;
static constexpr uint32_t EXPECTEDMAPCOMPONENTTOKENCOUNT = 2;
static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3U;
static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6U;
static constexpr uint32_t EXPECTEDSTATEDATATOKENCOUNT = 5U;
static constexpr uint32_t EXPECTEDMAPCOMPONENTTOKENCOUNT = 2U;
static constexpr int32_t MAXIMUMDIGITSALLOWED = 15;
static constexpr int32_t OPTIMALDIGITSALLOWED = 7;
static constexpr uint32_t MAXIMUMDIGITSALLOWED = 15U;
static constexpr uint32_t OPTIMALDIGITSALLOWED = 7U;
static constexpr wchar_t LEFTESCAPECHAR = L'{';
static constexpr wchar_t RIGHTESCAPECHAR = L'}';
static const double OPTIMALDECIMALALLOWED = pow(10, -1 * (OPTIMALDIGITSALLOWED - 1));
static const double MINIMUMDECIMALALLOWED = pow(10, -1 * (MAXIMUMDIGITSALLOWED - 1));
static const double OPTIMALDECIMALALLOWED = 1e-6; // pow(10, -1 * (OPTIMALDIGITSALLOWED - 1));
static const double MINIMUMDECIMALALLOWED = 1e-14; // pow(10, -1 * (MAXIMUMDIGITSALLOWED - 1));
unordered_map<wchar_t, wstring> quoteConversions;
unordered_map<wstring, wchar_t> unquoteConversions;
@ -109,7 +109,7 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i
{
if (m_currentCategory.id != input.id)
{
for (auto& unit : m_categoryToUnits[m_currentCategory])
for (auto& unit : m_categoryToUnits[m_currentCategory.id])
{
unit.isConversionSource = (unit.id == m_fromType.id);
unit.isConversionTarget = (unit.id == m_toType.id);
@ -121,7 +121,7 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i
}
}
newUnitList = m_categoryToUnits[input];
newUnitList = m_categoryToUnits[input.id];
}
InitializeSelectedUnits();
@ -149,6 +149,11 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType
return;
}
if (m_fromType != fromType)
{
m_switchedActive = true;
}
m_fromType = fromType;
m_toType = toType;
Calculate();
@ -191,6 +196,11 @@ void UnitConverter::SwitchActive(const wstring& newValue)
}
}
bool UnitConversionManager::UnitConverter::IsSwitchedActive() const
{
return m_switchedActive;
}
wstring UnitConverter::CategoryToString(const Category& c, wstring_view delimiter)
{
return Quote(std::to_wstring(c.id))
@ -209,7 +219,7 @@ vector<wstring> UnitConverter::StringToVector(wstring_view w, wstring_view delim
while (delimiterIndex != wstring_view::npos)
{
serializedTokens.emplace_back(w.substr(startIndex, delimiterIndex - startIndex));
startIndex = delimiterIndex + (int)delimiter.size();
startIndex = delimiterIndex + static_cast<int>(delimiter.size());
delimiterIndex = w.find(delimiter, startIndex);
}
if (addRemainder)
@ -244,9 +254,9 @@ Unit UnitConverter::StringToUnit(wstring_view w)
serializedUnit.name = Unquote(tokenList[1]);
serializedUnit.accessibleName = serializedUnit.name;
serializedUnit.abbreviation = Unquote(tokenList[2]);
serializedUnit.isConversionSource = (tokenList[3].compare(L"1") == 0);
serializedUnit.isConversionTarget = (tokenList[4].compare(L"1") == 0);
serializedUnit.isWhimsical = (tokenList[5].compare(L"1") == 0);
serializedUnit.isConversionSource = (tokenList[3] == L"1");
serializedUnit.isConversionTarget = (tokenList[4] == L"1");
serializedUnit.isWhimsical = (tokenList[5] == L"1");
return serializedUnit;
}
@ -256,7 +266,7 @@ Category UnitConverter::StringToCategory(wstring_view w)
assert(tokenList.size() == EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT);
Category serializedCategory;
serializedCategory.id = wcstol(Unquote(tokenList[0]).c_str(), nullptr, 10);
serializedCategory.supportsNegative = (tokenList[1].compare(L"1") == 0);
serializedCategory.supportsNegative = (tokenList[1] == L"1");
serializedCategory.name = Unquote(tokenList[2]);
return serializedCategory;
}
@ -283,7 +293,7 @@ void UnitConverter::RestoreUserPreferences(wstring_view userPreferences)
m_currentCategory = StringToCategory(outerTokens[2]);
// Only restore from the saved units if they are valid in the current available units.
auto itr = m_categoryToUnits.find(m_currentCategory);
auto itr = m_categoryToUnits.find(m_currentCategory.id);
if (itr != m_categoryToUnits.end())
{
const auto& curUnits = itr->second;
@ -559,7 +569,7 @@ future<pair<bool, wstring>> UnitConverter::RefreshCurrencyRatios()
}
shared_future<bool> sharedLoadResult = loadDataResult.share();
return async([this, currencyDataLoader, sharedLoadResult]() {
return async([currencyDataLoader, sharedLoadResult]() {
sharedLoadResult.wait();
bool didLoad = sharedLoadResult.get();
wstring timestamp;
@ -618,10 +628,10 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
newEntry.magnitude = log10(convertedValue);
newEntry.value = convertedValue;
newEntry.type = cur.first;
if (newEntry.type.isWhimsical == false)
intermediateVector.push_back(newEntry);
else
if (newEntry.type.isWhimsical)
intermediateWhimsicalVector.push_back(newEntry);
else
intermediateVector.push_back(newEntry);
}
}
@ -643,15 +653,15 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
wstring roundedString;
if (abs(entry.value) < 100)
{
roundedString = RoundSignificantDigits(entry.value, 2);
roundedString = RoundSignificantDigits(entry.value, 2U);
}
else if (abs(entry.value) < 1000)
{
roundedString = RoundSignificantDigits(entry.value, 1);
roundedString = RoundSignificantDigits(entry.value, 1U);
}
else
{
roundedString = RoundSignificantDigits(entry.value, 0);
roundedString = RoundSignificantDigits(entry.value, 0U);
}
if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative)
{
@ -681,15 +691,15 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
wstring roundedString;
if (abs(entry.value) < 100)
{
roundedString = RoundSignificantDigits(entry.value, 2);
roundedString = RoundSignificantDigits(entry.value, 2U);
}
else if (abs(entry.value) < 1000)
{
roundedString = RoundSignificantDigits(entry.value, 1);
roundedString = RoundSignificantDigits(entry.value, 1U);
}
else
{
roundedString = RoundSignificantDigits(entry.value, 0);
roundedString = RoundSignificantDigits(entry.value, 0U);
}
// How to work out which is the best whimsical value to add to the vector?
@ -713,10 +723,8 @@ vector<tuple<wstring, Unit>> UnitConverter::CalculateSuggested()
/// </summary>
void UnitConverter::ResetCategoriesAndRatios()
{
m_categories = m_dataLoader->LoadOrderedCategories();
m_switchedActive = false;
m_categories = m_dataLoader->GetOrderedCategories();
if (m_categories.empty())
{
return;
@ -738,8 +746,8 @@ void UnitConverter::ResetCategoriesAndRatios()
continue;
}
vector<Unit> units = activeDataLoader->LoadOrderedUnits(category);
m_categoryToUnits[category] = units;
vector<Unit> units = activeDataLoader->GetOrderedUnits(category);
m_categoryToUnits[category.id] = units;
// Just because the units are empty, doesn't mean the user can't select this category,
// we just want to make sure we don't let an unready category be the default.
@ -789,7 +797,7 @@ void UnitConverter::InitializeSelectedUnits()
return;
}
auto itr = m_categoryToUnits.find(m_currentCategory);
auto itr = m_categoryToUnits.find(m_currentCategory.id);
if (itr == m_categoryToUnits.end())
{
return;
@ -800,8 +808,8 @@ void UnitConverter::InitializeSelectedUnits()
{
// Units may already have been initialized through UnitConverter::RestoreUserPreferences().
// Check if they have been, and if so, do not override restored units.
bool isFromUnitValid = m_fromType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_fromType) != curUnits.end();
bool isToUnitValid = m_toType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_toType) != curUnits.end();
const bool isFromUnitValid = m_fromType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_fromType) != curUnits.end();
const bool isToUnitValid = m_toType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_toType) != curUnits.end();
if (isFromUnitValid && isToUnitValid)
{
@ -877,9 +885,9 @@ void UnitConverter::Calculate()
else
{
double currentValue = stod(m_currentDisplay);
double returnValue = Convert(currentValue, conversionTable[m_toType]);
const double returnValue = Convert(currentValue, conversionTable[m_toType]);
auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory);
const auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory);
if (isCurrencyConverter)
{
// We don't need to trim the value when it's a currency.
@ -888,15 +896,15 @@ void UnitConverter::Calculate()
}
else
{
int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue);
const unsigned int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue);
if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED))
{
m_returnDisplay = ToScientificNumber(returnValue);
}
else
{
int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay);
int precision = 0;
const unsigned int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay);
unsigned int precision;
if (abs(returnValue) < OPTIMALDECIMALALLOWED)
{
precision = MAXIMUMDIGITSALLOWED;
@ -905,7 +913,8 @@ void UnitConverter::Calculate()
{
// Fewer digits are needed following the decimal if the number is large,
// we calculate the number of decimals necessary based on the number of digits in the integer part.
precision = max(0, max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits)) - numPreDecimal);
auto numberDigits = max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits));
precision = numberDigits > numPreDecimal ? numberDigits - numPreDecimal : 0;
}
m_returnDisplay = RoundSignificantDigits(returnValue, precision);

View file

@ -18,11 +18,11 @@ namespace UnitConversionManager
Unit()
{
}
Unit(int id, std::wstring name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical)
Unit(int id, std::wstring_view name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical)
: id(id)
, name(name)
, accessibleName(name)
, abbreviation(abbreviation)
, abbreviation(std::move(abbreviation))
, isConversionSource(isConversionSource)
, isConversionTarget(isConversionTarget)
, isWhimsical(isWhimsical)
@ -31,23 +31,26 @@ namespace UnitConversionManager
Unit(
int id,
std::wstring currencyName,
std::wstring countryName,
std::wstring_view currencyName,
std::wstring_view countryName,
std::wstring abbreviation,
bool isRtlLanguage,
bool isConversionSource,
bool isConversionTarget)
: id(id)
, abbreviation(abbreviation)
, abbreviation(std::move(abbreviation))
, isConversionSource(isConversionSource)
, isConversionTarget(isConversionTarget)
, isWhimsical(false)
{
std::wstring nameValue1 = isRtlLanguage ? currencyName : countryName;
std::wstring nameValue2 = isRtlLanguage ? countryName : currencyName;
auto nameValue1 = isRtlLanguage ? currencyName : countryName;
auto nameValue2 = isRtlLanguage ? countryName : currencyName;
name = nameValue1 + L" - " + nameValue2;
accessibleName = nameValue1 + L" " + nameValue2;
name = nameValue1;
name.append(L" - ").append(nameValue2);
accessibleName = nameValue1;
accessibleName.append(1, L' ').append(nameValue2);
}
int id;
@ -84,7 +87,7 @@ namespace UnitConversionManager
Category(int id, std::wstring name, bool supportsNegative)
: id(id)
, name(name)
, name(std::move(name))
, supportsNegative(supportsNegative)
{
}
@ -113,15 +116,6 @@ namespace UnitConversionManager
}
};
class CategoryHash
{
public:
size_t operator()(const Category& x) const
{
return x.id;
}
};
struct SuggestedValueIntermediate
{
double magnitude;
@ -168,7 +162,7 @@ namespace UnitConversionManager
std::unordered_map<UnitConversionManager::Unit, UnitConversionManager::ConversionData, UnitConversionManager::UnitHash>,
UnitConversionManager::UnitHash>
UnitToUnitToConversionDataMap;
typedef std::unordered_map<UnitConversionManager::Category, std::vector<UnitConversionManager::Unit>, UnitConversionManager::CategoryHash>
typedef std::unordered_map<int, std::vector<UnitConversionManager::Unit>>
CategoryToUnitVectorMap;
class IViewModelCurrencyCallback
@ -187,8 +181,8 @@ namespace UnitConversionManager
public:
virtual ~IConverterDataLoader(){};
virtual void LoadData() = 0; // prepare data if necessary before calling other functions
virtual std::vector<Category> LoadOrderedCategories() = 0;
virtual std::vector<Unit> LoadOrderedUnits(const Category& c) = 0;
virtual std::vector<Category> GetOrderedCategories() = 0;
virtual std::vector<Unit> GetOrderedUnits(const Category& c) = 0;
virtual std::unordered_map<Unit, ConversionData, UnitHash> LoadOrderedRatios(const Unit& u) = 0;
virtual bool SupportsCategory(const Category& target) = 0;
};
@ -229,6 +223,7 @@ namespace UnitConversionManager
virtual Category GetCurrentCategory() = 0;
virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0;
virtual void SwitchActive(const std::wstring& newValue) = 0;
virtual bool IsSwitchedActive() const = 0;
virtual std::wstring SaveUserPreferences() = 0;
virtual void RestoreUserPreferences(_In_ std::wstring_view userPreferences) = 0;
virtual void SendCommand(Command command) = 0;
@ -252,6 +247,7 @@ namespace UnitConversionManager
Category GetCurrentCategory() override;
void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override;
void SwitchActive(const std::wstring& newValue) override;
bool IsSwitchedActive() const override;
std::wstring SaveUserPreferences() override;
void RestoreUserPreferences(std::wstring_view userPreference) override;
void SendCommand(Command command) override;

View file

@ -21,6 +21,6 @@
#include <vector>
#include <winerror.h>
#include <iostream>
#include <math.h>
#include <cmath>
#include <random>
#include <iomanip>

View file

@ -43,6 +43,7 @@ namespace
ApplicationViewModel::ApplicationViewModel()
: m_CalculatorViewModel(nullptr)
, m_DateCalcViewModel(nullptr)
, m_GraphingCalcViewModel(nullptr)
, m_ConverterViewModel(nullptr)
, m_PreviousMode(ViewMode::None)
, m_mode(ViewMode::None)
@ -74,7 +75,7 @@ void ApplicationViewModel::Categories::set(IObservableVector<NavCategoryGroup ^>
void ApplicationViewModel::Initialize(ViewMode mode)
{
if (!NavCategory::IsValidViewMode(mode))
if (!NavCategory::IsValidViewMode(mode) || !NavCategory::IsViewModeEnabled(mode))
{
mode = ViewMode::Standard;
}
@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged()
}
m_CalculatorViewModel->SetCalculatorType(m_mode);
}
else if (NavCategory::IsGraphingCalculatorViewMode(m_mode))
{
if (!m_GraphingCalcViewModel)
{
m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel();
}
}
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
{
if (!m_DateCalcViewModel)
@ -182,7 +190,7 @@ void ApplicationViewModel::OnCopyCommand(Object ^ parameter)
{
DateCalcViewModel->OnCopyCommand(parameter);
}
else
else if (NavCategory::IsCalculatorViewMode(m_mode))
{
CalculatorViewModel->OnCopyCommand(parameter);
}
@ -223,7 +231,6 @@ task<void> ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float heig
localSettings->Values->Insert(HeightLocalSettings, height);
bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::Default);
CalculatorViewModel->AreHistoryShortcutsEnabled = success;
CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = success;
CalculatorViewModel->IsAlwaysOnTop = !success;
IsAlwaysOnTop = !success;
@ -252,7 +259,6 @@ task<void> ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float heig
}
bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::CompactOverlay, compactOptions);
CalculatorViewModel->AreHistoryShortcutsEnabled = !success;
CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = !success;
CalculatorViewModel->IsAlwaysOnTop = success;
IsAlwaysOnTop = success;

View file

@ -5,6 +5,7 @@
#include "StandardCalculatorViewModel.h"
#include "DateCalculatorViewModel.h"
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
#include "UnitConverterViewModel.h"
namespace CalculatorApp
@ -21,6 +22,7 @@ namespace CalculatorApp
OBSERVABLE_OBJECT();
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel);
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel ^, DateCalcViewModel);
OBSERVABLE_PROPERTY_RW(GraphingCalculatorViewModel ^, GraphingCalcViewModel);
OBSERVABLE_PROPERTY_RW(UnitConverterViewModel ^, ConverterViewModel);
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
OBSERVABLE_PROPERTY_R(bool, IsAlwaysOnTop);

View file

@ -122,29 +122,9 @@
</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'">
<PropertyGroup>
<GenerateManifest>false</GenerateManifest>
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -313,16 +293,13 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ApplicationViewModel.h" />
<ClInclude Include="Common\AlwaysSelectedCollectionView.h" />
<ClInclude Include="Common\AppResourceProvider.h" />
<ClInclude Include="Common\Automation\NarratorAnnouncement.h" />
<ClInclude Include="Common\Automation\NarratorNotifier.h" />
<ClInclude Include="Common\BindableBase.h" />
<ClInclude Include="Common\BitLength.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" />
@ -330,7 +307,6 @@
<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" />
@ -338,16 +314,19 @@
<ClInclude Include="Common\NavCategory.h" />
<ClInclude Include="Common\NetworkManager.h" />
<ClInclude Include="Common\NumberBase.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="GraphingCalculatorEnums.h" />
<ClInclude Include="GraphingCalculator\EquationViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h" />
<ClInclude Include="GraphingCalculator\VariableViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h" />
<ClInclude Include="HistoryItemViewModel.h" />
<ClInclude Include="HistoryViewModel.h" />
<ClInclude Include="MemoryItemViewModel.h" />
@ -361,16 +340,13 @@
<ClCompile Include="Common\AppResourceProvider.cpp" />
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp" />
<ClCompile Include="Common\Automation\NarratorNotifier.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" />
@ -380,6 +356,9 @@
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
<ClCompile Include="DateCalculatorViewModel.cpp" />
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp" />
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp" />
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp" />
<ClCompile Include="HistoryItemViewModel.cpp" />
<ClCompile Include="HistoryViewModel.cpp" />
<ClCompile Include="MemoryItemViewModel.cpp" />
@ -400,6 +379,12 @@
<ProjectReference Include="..\CalcManager\CalcManager.vcxproj">
<Project>{311e866d-8b93-4609-a691-265941fee101}</Project>
</ProjectReference>
<ProjectReference Include="..\GraphControl\GraphControl.vcxproj">
<Project>{e727a92b-f149-492c-8117-c039a298719b}</Project>
</ProjectReference>
<ProjectReference Include="..\TraceLogging\TraceLogging.vcxproj">
<Project>{fc81ff41-02cd-4cd9-9bc5-45a1e39ac6ed}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup Condition="!Exists('DataLoaders\DataLoaderConstants.h')">
<ClCompile>
@ -420,16 +405,7 @@
</Choose>
<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.2.190830001\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\packages\Microsoft.UI.Xaml.2.2.190830001\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.2.190830001\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.UI.Xaml.2.2.190830001\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<ImportGroup Label="ExtensionTargets" />
</Project>

View file

@ -10,6 +10,9 @@
<Filter Include="DataLoaders">
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
</Filter>
<Filter Include="GraphingCalculator">
<UniqueIdentifier>{cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
@ -23,18 +26,12 @@
<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>
@ -50,9 +47,6 @@
<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>
@ -80,9 +74,18 @@
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
<Filter>DataLoaders</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp">
<Filter>Common\Automation</Filter>
</ClCompile>
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp">
<Filter>GraphingCalculator</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@ -94,15 +97,9 @@
<ClInclude Include="MemoryItemViewModel.h" />
<ClInclude Include="StandardCalculatorViewModel.h" />
<ClInclude Include="UnitConverterViewModel.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>
@ -112,18 +109,12 @@
<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>
@ -136,9 +127,6 @@
<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>
@ -163,9 +151,6 @@
<ClInclude Include="Common\Utils.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Common\ValidatingConverters.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Common\Automation\NarratorNotifier.h">
<Filter>Common\Automation</Filter>
</ClInclude>
@ -184,9 +169,6 @@
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h">
<Filter>DataLoaders</Filter>
</ClInclude>
<ClInclude Include="Common\TraceActivity.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="DataLoaders\DataLoaderMockConstants.h">
<Filter>DataLoaders</Filter>
</ClInclude>
@ -199,12 +181,29 @@
<ClInclude Include="Common\NumberBase.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\EquationViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculatorEnums.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\VariableViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="Common\DelegateCommand.h">
<Filter>Common</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="DataLoaders\DefaultFromToCurrency.json">
<Filter>DataLoaders</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)DensityStyles\Compact.xaml" />

View file

@ -0,0 +1,27 @@
// 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,42 @@
// 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,33 @@
// 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 notifications 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

@ -18,11 +18,18 @@ namespace CalculatorApp::Common::Automation
StringReference MemoryItemChanged(L"MemorySlotChanged");
StringReference MemoryItemAdded(L"MemorySlotAdded");
StringReference HistoryCleared(L"HistoryCleared");
StringReference HistorySlotCleared(L"HistorySlotCleared");
StringReference CategoryNameChanged(L"CategoryNameChanged");
StringReference UpdateCurrencyRates(L"UpdateCurrencyRates");
StringReference DisplayCopied(L"DisplayCopied");
StringReference OpenParenthesisCountChanged(L"OpenParenthesisCountChanged");
StringReference NoParenthesisAdded(L"NoParenthesisAdded");
StringReference GraphModeChanged(L"GraphModeChanged");
StringReference GraphViewChanged(L"GraphViewChanged");
StringReference FunctionRemoved(L"FunctionRemoved");
StringReference GraphViewBestFitChanged(L"GraphViewBestFitChanged");
StringReference AlwaysOnTop(L"AlwaysOnTop");
StringReference BitShiftRadioButtonContent(L"BitShiftRadioButtonContent");
}
}
@ -99,6 +106,15 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(Str
announcement, CalculatorActivityIds::HistoryCleared, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::MostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetHistorySlotClearedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::HistorySlotCleared,
AutomationNotificationKind::ItemRemoved,
AutomationNotificationProcessing::ImportantMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
@ -140,3 +156,51 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnounc
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::ImportantMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphModeChangedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::GraphModeChanged,
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::ImportantMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphViewChangedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::GraphViewChanged,
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::CurrentThenMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetFunctionRemovedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement, CalculatorActivityIds::FunctionRemoved, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::MostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphViewBestFitChangedAnnouncement(Platform::String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::GraphViewBestFitChanged,
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::MostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetAlwaysOnTopChangedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement, CalculatorActivityIds::AlwaysOnTop, AutomationNotificationKind::ActionCompleted, AutomationNotificationProcessing::ImportantMostRecent);
}
NarratorAnnouncement ^ CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(String ^ announcement)
{
return ref new NarratorAnnouncement(
announcement,
CalculatorActivityIds::BitShiftRadioButtonContent,
AutomationNotificationKind::ActionCompleted,
AutomationNotificationProcessing::ImportantMostRecent);
}

View file

@ -57,6 +57,7 @@ public
static NarratorAnnouncement ^ GetMemoryItemAddedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetHistoryClearedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetHistorySlotClearedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetCategoryNameChangedAnnouncement(Platform::String ^ announcement);
@ -66,5 +67,16 @@ public
static NarratorAnnouncement ^ GetOpenParenthesisCountChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetNoRightParenthesisAddedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetGraphModeChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetGraphViewChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetGraphViewBestFitChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetFunctionRemovedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetAlwaysOnTopChangedAnnouncement(Platform::String ^ announcement);
static NarratorAnnouncement ^ GetBitShiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement);
};
}

View file

@ -0,0 +1,61 @@
// 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 fall back to LiveRegion
// if not. The availability 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,33 @@
// 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,97 @@
// 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

@ -1,34 +0,0 @@
// 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

@ -1,38 +0,0 @@
// 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

@ -109,7 +109,7 @@ public
InvCoth = (int) CM::Command::CommandACOTH,
CubeRoot = (int) CM::Command::CommandCUBEROOT,
TwoPowerX = (int) CM::Command::CommandPOW2,
LogBaseX = (int) CM::Command::CommandLogBaseX,
LogBaseY = (int) CM::Command::CommandLogBaseY,
Nand = (int) CM::Command::CommandNand,
Nor = (int) CM::Command::CommandNor,
Abs = (int) CM::Command::CommandAbs,
@ -194,6 +194,15 @@ public
MemoryRecall = (int)CM::Command::CommandRECALL,
MemoryClear = (int)CM::Command::CommandMCLEAR,
BitflipButton = 1000,
FullKeypadButton = 1001
FullKeypadButton = 1001,
// Buttons used in graphing calculator
LessThan,
LessThanOrEqualTo,
GreaterThan,
GreaterThanOrEqualTo,
X,
Y,
Submit
};
}

View file

@ -1,41 +0,0 @@
// 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

@ -1,24 +0,0 @@
// 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

@ -310,6 +310,9 @@ bool CopyPasteManager::ExpressionRegExMatch(
if (operandMatched)
{
// Remember the sign of the operand
bool isNegativeValue = operand->Data()[0] == L'-';
// 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.
auto operandValue = SanitizeOperand(operand);
@ -332,7 +335,11 @@ bool CopyPasteManager::ExpressionRegExMatch(
break;
}
if (operandAsULL->Value > maxOperandLengthAndValue.maxValue)
// Calculate how much we exceed the maxValue.
// In case we exceed it for 1 only, and working with negative number - that's a corner case for max signed values (e.g. -32768)
bool isOverflow = operandAsULL->Value > maxOperandLengthAndValue.maxValue;
bool isMaxNegativeValue = operandAsULL->Value - 1 == maxOperandLengthAndValue.maxValue;
if (isOverflow && !(isNegativeValue && isMaxNegativeValue))
{
expMatched = false;
break;
@ -594,9 +601,11 @@ ULONG32 CopyPasteManager::ProgrammerOperandLength(Platform::String ^ operand, Nu
// Indian rupee(₹) - 8377
// pound(£) - 163
// euro(€) - 8364
// non-breaking whitespace - 160
Platform::String ^ CopyPasteManager::RemoveUnwantedCharsFromString(Platform::String ^ input)
{
constexpr wchar_t unWantedChars[] = { L' ', L',', L'"', 165, 164, 8373, 36, 8353, 8361, 8362, 8358, 8377, 163, 8364, 8234, 8235, 8236, 8237 };
constexpr wchar_t unWantedChars[] = { L' ', L',', L'"', 165, 164, 8373, 36, 8353, 8361, 8362, 8358, 8377, 163, 8364, 8234, 8235, 8236, 8237, 160 };
input = CalculatorApp::Common::LocalizationSettings::GetInstance().RemoveGroupSeparators(input);
return ref new String(Utils::RemoveUnwantedCharsFromString(input->Data(), unWantedChars).c_str());
}

View file

@ -1,850 +0,0 @@
// 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 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 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();
TimeSpan lightUpTime{};
lightUpTime.Duration = 500000L; // Half second (in 100-ns units)
timer->Interval = 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 below we will not mark the event as handled
// because this is a supplemental 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 currControlKeyPressed = s_ControlKeyPressed.find(viewId);
if (currControlKeyPressed != 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 currShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
if (currShiftKeyPressed != 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 (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, false));
}
}
else if (key == VirtualKey::Control)
{
// Writer lock for the static maps
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
auto currControlKeyPressed = s_ControlKeyPressed.find(viewId);
if (currControlKeyPressed != 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));
auto nvi = dynamic_cast<MUXC::NavigationViewItem ^>(menuItems->GetAt(NavCategory::GetFlatIndex(toMode)));
if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode))
{
vm->Mode = toMode;
navView->SelectedItem = nvi;
}
}
}
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

@ -545,7 +545,7 @@ unordered_map<wstring, wstring> LocalizationService::GetTokenToReadableNameMap()
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"),
make_pair<wstring, wstring>(L"LogBaseX", L"Logx"),
make_pair<wstring, wstring>(L"LogBaseY", L"Logy"),
// Y Root scientific function
make_pair<wstring, wstring>(L"16", L"YRoot")

View file

@ -81,6 +81,18 @@ namespace CalculatorApp
{
return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data(), param3->Data(), param4->Data());
}
static Platform::String
^ GetLocalizedString(
Platform::String ^ pMessage,
Platform::String ^ param1,
Platform::String ^ param2,
Platform::String ^ param3,
Platform::String ^ param4,
Platform::String ^ param5)
{
return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data(), param3->Data(), param4->Data(), param5->Data());
}
};
}
}

View file

@ -5,13 +5,17 @@
#include "NavCategory.h"
#include "AppResourceProvider.h"
#include "Common/LocalizationStringUtil.h"
#include <initializer_list>
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace Concurrency;
using namespace Platform;
using namespace Platform::Collections;
using namespace std;
using namespace Windows::Foundation::Collections;
using namespace Windows::Management::Policies;
using namespace Windows::System;
namespace UCM = UnitConversionManager;
@ -22,12 +26,6 @@ static constexpr bool SUPPORTS_ALL = true;
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;
@ -46,17 +44,55 @@ static constexpr int DATA_ID = 13;
static constexpr int PRESSURE_ID = 14;
static constexpr int ANGLE_ID = 15;
static constexpr int CURRENCY_ID = 16;
static constexpr int GRAPHING_ID = 17;
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
wchar_t* towchar_t(int number)
{
auto wstr = to_wstring(number);
return _wcsdup(wstr.c_str());
}
bool IsGraphingModeAvailable()
{
static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath");
return supportGraph;
}
Box<bool> ^ _isGraphingModeEnabledCached = nullptr;
bool IsGraphingModeEnabled()
{
if (!IsGraphingModeAvailable())
{
return false;
}
if (_isGraphingModeEnabledCached != nullptr)
{
return _isGraphingModeEnabledCached->Value;
}
User ^ firstUser;
create_task(User::FindAllAsync(UserType::LocalUser)).then([&firstUser](IVectorView<User ^> ^ users) {
firstUser = users->GetAt(0); }).wait();
auto namedPolicyData = NamedPolicy::GetPolicyFromPathForUser(firstUser, L"Education", L"AllowGraphingCalculator");
_isGraphingModeEnabledCached = namedPolicyData->GetBoolean() == true;
return _isGraphingModeEnabledCached->Value;
}
// 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,
static const list<NavCategoryInitializer> s_categoryManifest = [] {
auto res = list<NavCategoryInitializer>{ NavCategoryInitializer{ ViewMode::Standard,
STANDARD_ID,
L"Standard",
L"StandardMode",
L"\uE8EF",
CategoryGroupType::Calculator,
MyVirtualKey::Number1,
SUPPORTS_ALL },
L"1",
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Scientific,
SCIENTIFIC_ID,
L"Scientific",
@ -64,23 +100,49 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF196",
CategoryGroupType::Calculator,
MyVirtualKey::Number2,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Programmer,
L"2",
SUPPORTS_ALL,
true } };
int currentIndex = 3;
bool supportGraphingCalculator = IsGraphingModeAvailable();
if (supportGraphingCalculator)
{
const bool isEnabled = IsGraphingModeEnabled();
res.push_back(NavCategoryInitializer{ ViewMode::Graphing,
GRAPHING_ID,
L"Graphing",
L"GraphingCalculatorMode",
L"\uF770",
CategoryGroupType::Calculator,
MyVirtualKey::Number3,
L"3",
SUPPORTS_ALL,
isEnabled });
++currentIndex;
}
res.insert(
res.end(),
{ NavCategoryInitializer{ ViewMode::Programmer,
PROGRAMMER_ID,
L"Programmer",
L"ProgrammerMode",
L"\uECCE",
CategoryGroupType::Calculator,
MyVirtualKey::Number3,
SUPPORTS_ALL },
supportGraphingCalculator ? MyVirtualKey::Number4 : MyVirtualKey::Number3,
towchar_t(currentIndex++),
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Date,
DATE_ID,
L"Date",
L"DateCalculationMode",
L"\uE787",
CategoryGroupType::Calculator,
MyVirtualKey::Number4,
SUPPORTS_ALL },
supportGraphingCalculator ? MyVirtualKey::Number5 : MyVirtualKey::Number4,
towchar_t(currentIndex++),
SUPPORTS_ALL,
true },
NavCategoryInitializer{ ViewMode::Currency,
CURRENCY_ID,
L"Currency",
@ -88,7 +150,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEB0D",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Volume,
VOLUME_ID,
L"Volume",
@ -96,7 +160,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF1AA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Length,
LENGTH_ID,
L"Length",
@ -104,7 +170,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uECC6",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Weight,
WEIGHT_ID,
L"Weight and Mass",
@ -112,7 +180,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF4C1",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Temperature,
TEMPERATURE_ID,
L"Temperature",
@ -120,7 +190,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE7A3",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
nullptr,
SUPPORTS_NEGATIVE,
true },
NavCategoryInitializer{ ViewMode::Energy,
ENERGY_ID,
L"Energy",
@ -128,7 +200,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uECAD",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Area,
AREA_ID,
L"Area",
@ -136,7 +210,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE809",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Speed,
SPEED_ID,
L"Speed",
@ -144,7 +220,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEADA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Time,
TIME_ID,
L"Time",
@ -152,7 +230,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE917",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Power,
POWER_ID,
L"Power",
@ -160,7 +240,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uE945",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
nullptr,
SUPPORTS_NEGATIVE,
true },
NavCategoryInitializer{ ViewMode::Data,
DATA_ID,
L"Data",
@ -168,7 +250,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF20F",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Pressure,
PRESSURE_ID,
L"Pressure",
@ -176,7 +260,9 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uEC4A",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
nullptr,
POSITIVE_ONLY,
true },
NavCategoryInitializer{ ViewMode::Angle,
ANGLE_ID,
L"Angle",
@ -184,7 +270,11 @@ static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
L"\uF515",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE } };
nullptr,
SUPPORTS_NEGATIVE,
true } });
return res;
}();
// This function should only be used when storing the mode to app data.
int NavCategory::Serialize(ViewMode mode)
@ -211,6 +301,14 @@ ViewMode NavCategory::Deserialize(Platform::Object ^ obj)
if (iter != s_categoryManifest.end())
{
if (iter->viewMode == ViewMode::Graphing)
{
// check if the user is allowed to use this feature
if (!IsGraphingModeEnabled())
{
return ViewMode::None;
}
}
return iter->viewMode;
}
}
@ -226,11 +324,23 @@ bool NavCategory::IsValidViewMode(ViewMode mode)
return iter != s_categoryManifest.end();
}
bool NavCategory::IsViewModeEnabled(ViewMode mode)
{
auto iter =
find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode && initializer.isEnabled; });
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);
// Historically, Calculator modes are Standard, Scientific, and Programmer.
return !IsDateCalculatorViewMode(mode) && !IsGraphingCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
}
bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode)
{
return mode == ViewMode::Graphing;
}
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)
@ -389,10 +499,12 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti
categoryName,
categoryAutomationName,
StringReference(categoryInitializer.glyph),
resProvider->GetResourceString(nameResourceKey + "AccessKey"),
categoryInitializer.accessKey != nullptr ? ref new String(categoryInitializer.accessKey)
: resProvider->GetResourceString(nameResourceKey + "AccessKey"),
groupMode,
categoryInitializer.viewMode,
categoryInitializer.supportsNegative));
categoryInitializer.supportsNegative,
categoryInitializer.isEnabled));
}
}
}
@ -407,29 +519,12 @@ IObservableVector<NavCategoryGroup ^> ^ NavCategoryGroup::CreateMenuOptions()
NavCategoryGroup ^ NavCategoryGroup::CreateCalculatorCategory()
{
return ref new NavCategoryGroup(s_categoryGroupManifest.at(0));
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" });
}
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;
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" });
}

View file

@ -44,7 +44,8 @@ namespace CalculatorApp
Data = 13,
Pressure = 14,
Angle = 15,
Currency = 16
Currency = 16,
Graphing = 17
};
public
@ -66,7 +67,9 @@ namespace CalculatorApp
wchar_t const* glyph,
CategoryGroupType group,
MyVirtualKey vKey,
bool categorySupportsNegative)
wchar_t const* aKey,
bool categorySupportsNegative,
bool enabled)
: viewMode(mode)
, serializationId(id)
, friendlyName(name)
@ -74,7 +77,9 @@ namespace CalculatorApp
, glyph(glyph)
, groupType(group)
, virtualKey(vKey)
, accessKey(aKey)
, supportsNegative(categorySupportsNegative)
, isEnabled(enabled)
{
}
@ -85,7 +90,9 @@ namespace CalculatorApp
const wchar_t* const glyph;
const CategoryGroupType groupType;
const MyVirtualKey virtualKey;
const wchar_t* const accessKey;
const bool supportsNegative;
const bool isEnabled;
};
private
@ -109,45 +116,17 @@ namespace CalculatorApp
{
public:
OBSERVABLE_OBJECT();
PROPERTY_R(Platform::String ^, Name);
PROPERTY_R(Platform::String ^, AutomationName);
PROPERTY_R(Platform::String ^, Glyph);
PROPERTY_R(ViewMode, Mode);
PROPERTY_R(Platform::String ^, AccessKey);
PROPERTY_R(bool, SupportsNegative);
PROPERTY_R(bool, IsEnabled);
property Platform::String
^ Name { Platform::String ^ get() { return m_name; } }
^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } }
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);
@ -155,7 +134,9 @@ namespace CalculatorApp
static ViewMode GetViewModeForFriendlyName(Platform::String ^ name);
static bool IsValidViewMode(ViewMode mode);
static bool IsViewModeEnabled(ViewMode mode);
static bool IsCalculatorViewMode(ViewMode mode);
static bool IsGraphingCalculatorViewMode(ViewMode mode);
static bool IsDateCalculatorViewMode(ViewMode mode);
static bool IsConverterViewMode(ViewMode mode);
@ -178,16 +159,17 @@ namespace CalculatorApp
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)
bool supportsNegative,
bool isEnabled)
: m_Name(name)
, m_AutomationName(automationName)
, m_Glyph(glyph)
, m_AccessKey(accessKey)
, m_modeString(mode)
, m_Mode(viewMode)
, m_SupportsNegative(supportsNegative)
, m_IsEnabled(isEnabled)
{
m_position = NavCategory::GetPosition(m_viewMode);
}
static std::vector<MyVirtualKey> GetCategoryAcceleratorKeys();
@ -195,14 +177,7 @@ namespace CalculatorApp
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;
Platform::String ^ m_modeString;
};
[Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
@ -216,15 +191,11 @@ namespace CalculatorApp
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

@ -1,56 +0,0 @@
// 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

@ -8,15 +8,15 @@
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace TraceLogging;
using namespace Concurrency;
using namespace std;
using namespace Platform;
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;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Diagnostics;
using namespace Windows::Globalization;
using namespace Windows::Globalization::DateTimeFormatting;
using namespace Windows::System::UserProfile;
namespace CalculatorApp
{
@ -35,36 +35,23 @@ namespace CalculatorApp
constexpr auto EVENT_NAME_VISUAL_STATE_CHANGED = L"VisualStateChanged";
constexpr auto EVENT_NAME_CONVERTER_INPUT_RECEIVED = L"ConverterInputReceived";
constexpr auto EVENT_NAME_INPUT_PASTED = L"InputPasted";
constexpr auto EVENT_NAME_SHOW_HIDE_BUTTON_CLICKED = L"ShowHideButtonClicked";
constexpr auto EVENT_NAME_GRAPH_BUTTON_CLICKED = L"GraphButtonClicked";
constexpr auto EVENT_NAME_GRAPH_LINE_STYLE_CHANGED = L"GraphLineStyleChanged";
constexpr auto EVENT_NAME_VARIABLE_CHANGED = L"VariableChanged";
constexpr auto EVENT_NAME_VARIABLE_SETTING_CHANGED = L"VariableSettingChanged";
constexpr auto EVENT_NAME_GRAPH_SETTINGS_CHANGED = L"GraphSettingsChanged";
constexpr auto EVENT_NAME_GRAPH_THEME = L"GraphTheme";
constexpr auto EVENT_NAME_EXCEPTION = L"Exception";
constexpr auto PDT_PRIVACY_DATA_TAG = L"PartA_PrivTags";
constexpr auto PDT_PRODUCT_AND_SERVICE_USAGE = 0x0000'0000'0200'0000u;
#ifdef SEND_DIAGNOSTICS
// 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_LEVEL_1 = 0x0000800000000000; // Bit 47
constexpr int64_t MICROSOFT_KEYWORD_LEVEL_2 = 0x0000400000000000; // Bit 46
constexpr int64_t MICROSOFT_KEYWORD_LEVEL_3 = 0x0000200000000000; // Bit 45
#else
// define all Keyword options as 0 when we do not want to upload app diagnostics
constexpr int64_t MICROSOFT_KEYWORD_LEVEL_1 = 0;
constexpr int64_t MICROSOFT_KEYWORD_LEVEL_2 = 0;
constexpr int64_t MICROSOFT_KEYWORD_LEVEL_3 = 0;
#endif
constexpr auto CALC_MODE = L"CalcMode";
constexpr auto GRAPHING_MODE = L"Graphing";
#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 }),
GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 })
, // Unique providerID {0905CA09-610E-401E-B650-2F212980B9E0}
m_appLaunchActivity{ nullptr }
{
CoCreateGuid(&sessionGuid);
}
TraceLogger ^ TraceLogger::GetInstance()
@ -73,33 +60,6 @@ namespace CalculatorApp
return s_selfInstance;
}
bool TraceLogger::GetTraceLoggingProviderEnabled()
{
return g_calculatorProvider.Enabled();
}
#pragma region Tracing methods
void TraceLogger::LogLevel1Event(wstring_view eventName, LoggingFields fields)
{
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_1));
}
void TraceLogger::LogLevel2Event(wstring_view eventName, LoggingFields fields)
{
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_2));
}
void TraceLogger::LogLevel3Event(wstring_view eventName, LoggingFields fields)
{
g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_3));
}
unique_ptr<TraceActivity> TraceLogger::CreateTraceActivity(wstring_view eventName, LoggingFields fields)
{
return make_unique<TraceActivity>(g_calculatorProvider, eventName, fields);
}
#pragma endregion
// return true if windowId is logged once else return false
bool TraceLogger::IsWindowIdInLog(int windowId)
{
@ -116,18 +76,12 @@ namespace CalculatorApp
void TraceLogger::LogVisualStateChanged(ViewMode mode, String ^ state, bool isAlwaysOnTop)
{
if (!GetTraceLoggingProviderEnabled())
{
return;
}
auto fields = ref new LoggingFields();
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddString(L"VisualState", state->Data());
fields.AddBoolean(L"IsAlwaysOnTop", isAlwaysOnTop);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_VISUAL_STATE_CHANGED, fields);
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddString(StringReference(L"VisualState"), state);
fields->AddBoolean(StringReference(L"IsAlwaysOnTop"), isAlwaysOnTop);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VISUAL_STATE_CHANGED), fields);
}
void TraceLogger::LogWindowCreated(ViewMode mode, int windowId)
@ -138,122 +92,69 @@ namespace CalculatorApp
windowIdLog.push_back(windowId);
}
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddUInt64(L"NumOfOpenWindows", currentWindowCount);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_WINDOW_ON_CREATED, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddUInt64(StringReference(L"NumOfOpenWindows"), currentWindowCount);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_WINDOW_ON_CREATED), fields);
}
void TraceLogger::LogModeChange(ViewMode mode)
{
if (!GetTraceLoggingProviderEnabled())
return;
if (NavCategory::IsValidViewMode(mode))
{
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_MODE_CHANGED, fields);
auto fields = ref new LoggingFields();
;
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MODE_CHANGED), fields);
}
}
void TraceLogger::LogHistoryItemLoad(ViewMode mode, int historyListSize, int loadedIndex)
{
if (!GetTraceLoggingProviderEnabled())
{
return;
}
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddInt32(L"HistoryListSize", historyListSize);
fields.AddInt32(L"HistoryItemIndex", loadedIndex);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_HISTORY_ITEM_LOAD, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddInt32(StringReference(L"HistoryListSize"), historyListSize);
fields->AddInt32(StringReference(L"HistoryItemIndex"), loadedIndex);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_HISTORY_ITEM_LOAD), fields);
}
void TraceLogger::LogMemoryItemLoad(ViewMode mode, int memoryListSize, int loadedIndex)
{
if (!GetTraceLoggingProviderEnabled())
{
return;
}
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddInt32(L"MemoryListSize", memoryListSize);
fields.AddInt32(L"MemoryItemIndex", loadedIndex);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_MEMORY_ITEM_LOAD, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddInt32(StringReference(L"MemoryListSize"), memoryListSize);
fields->AddInt32(StringReference(L"MemoryItemIndex"), loadedIndex);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MEMORY_ITEM_LOAD), fields);
}
void TraceLogger::LogError(ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString)
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddString(L"FunctionName", functionName->Data());
fields.AddString(L"Message", errorString->Data());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_EXCEPTION, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddString(StringReference(L"FunctionName"), functionName);
fields->AddString(StringReference(L"Message"), errorString);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields);
}
void TraceLogger::LogStandardException(ViewMode mode, wstring_view functionName, const exception& e)
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddString(L"FunctionName", functionName);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddString(StringReference(L"FunctionName"), StringReference(functionName.data()));
wstringstream exceptionMessage;
exceptionMessage << e.what();
fields.AddString(L"Message", exceptionMessage.str());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_EXCEPTION, fields);
}
void TraceLogger::LogWinRTException(ViewMode mode, wstring_view functionName, hresult_error const& e)
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddString(L"FunctionName", functionName);
fields.AddString(L"Message", e.message());
fields.AddInt32(L"HRESULT", e.code());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_EXCEPTION, fields);
fields->AddString(StringReference(L"Message"), StringReference(exceptionMessage.str().data()));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields);
}
void TraceLogger::LogPlatformException(ViewMode mode, wstring_view functionName, Platform::Exception ^ e)
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddString(L"FunctionName", functionName);
fields.AddString(L"Message", e->Message->Data());
fields.AddInt32(L"HRESULT", e->HResult);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_EXCEPTION, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
fields->AddString(StringReference(L"FunctionName"), StringReference(functionName.data()));
fields->AddString(StringReference(L"Message"), e->Message);
fields->AddInt32(StringReference(L"HRESULT"), e->HResult);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields);
}
void TraceLogger::UpdateButtonUsage(NumbersAndOperatorsEnum button, ViewMode mode)
@ -305,9 +206,6 @@ namespace CalculatorApp
void TraceLogger::LogButtonUsage()
{
if (!GetTraceLoggingProviderEnabled())
return;
// Writer lock for the buttonLog resource
reader_writer_lock::scoped_lock lock(s_traceLoggerLock);
@ -330,11 +228,9 @@ namespace CalculatorApp
}
}
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"ButtonUsage", buttonUsageString->Data());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_BUTTON_USAGE, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(L"ButtonUsage"), buttonUsageString);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_BUTTON_USAGE), fields);
buttonLog.clear();
}
@ -342,46 +238,89 @@ namespace CalculatorApp
void TraceLogger::LogDateCalculationModeUsed(bool AddSubtractMode)
{
const wchar_t* calculationType = AddSubtractMode ? L"AddSubtractMode" : L"DateDifferenceMode";
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(ViewMode::Date)->Data());
fields.AddString(L"CalculationType", calculationType);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_DATE_CALCULATION_MODE_USED, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(ViewMode::Date));
fields->AddString(StringReference(L"CalculationType"), StringReference(calculationType));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_DATE_CALCULATION_MODE_USED), fields);
}
void TraceLogger::LogConverterInputReceived(ViewMode mode)
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_CONVERTER_INPUT_RECEIVED, fields);
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_CONVERTER_INPUT_RECEIVED), fields);
}
void TraceLogger::LogNavBarOpened()
{
if (!GetTraceLoggingProviderEnabled())
return;
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_NAV_BAR_OPENED, fields);
auto fields = ref new LoggingFields();
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_NAV_BAR_OPENED), fields);
}
void TraceLogger::LogInputPasted(ViewMode mode)
{
if (!GetTraceLoggingProviderEnabled())
return;
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_INPUT_PASTED), fields);
}
LoggingFields fields{};
fields.AddGuid(L"SessionGuid", sessionGuid);
fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data());
fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE);
LogLevel2Event(EVENT_NAME_INPUT_PASTED, fields);
void TraceLogger::LogShowHideButtonClicked(bool isHideButton)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddBoolean(StringReference(L"IsHideButton"), isHideButton);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_SHOW_HIDE_BUTTON_CLICKED), fields);
}
void TraceLogger::LogGraphButtonClicked(GraphButton buttonName, GraphButtonValue buttonValue)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddInt16(StringReference(L"ButtonName"), static_cast<int16>(buttonName));
fields->AddInt16(StringReference(L"ButtonValue"), static_cast<int16>(buttonValue));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_BUTTON_CLICKED), fields);
}
void TraceLogger::LogGraphLineStyleChanged(LineStyleType style)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddInt16(StringReference(L"StyleType"), static_cast<int16>(style));
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_LINE_STYLE_CHANGED), fields);
}
void TraceLogger::LogVariableChanged(String ^ inputChangedType, String ^ variableName)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddString(StringReference(L"InputChangedType"), inputChangedType);
fields->AddString(StringReference(L"VariableName"), variableName);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VARIABLE_CHANGED), fields);
}
void TraceLogger::LogVariableSettingsChanged(String ^ setting)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddString(StringReference(L"SettingChanged"), setting);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VARIABLE_SETTING_CHANGED), fields);
}
void TraceLogger::LogGraphSettingsChanged(GraphSettingsType settingType, String ^ settingValue)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddInt16(L"SettingType", static_cast<int16>(settingType));
fields->AddString(L"SettingValue", settingValue);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_SETTINGS_CHANGED), fields);
}
void TraceLogger::LogGraphTheme(String ^ graphTheme)
{
auto fields = ref new LoggingFields();
fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE));
fields->AddString(L"GraphTheme", graphTheme);
TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_THEME), fields);
}
}

View file

@ -3,13 +3,9 @@
#pragma once
#include "CalcManager/Command.h"
#include "TraceActivity.h"
#include "NavCategory.h"
#include "CalculatorButtonUser.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
@ -28,12 +24,44 @@ namespace CalculatorApp
}
};
public
ref class TraceLogger sealed
public enum class GraphSettingsType
{
Grid,
TrigUnits,
Theme
};
public enum class GraphButton
{
StylePicker,
RemoveFunction,
ActiveTracingChecked,
ActiveTracingUnchecked,
GraphSettings,
Share,
ZoomIn,
ZoomOut,
GraphView
};
public enum class GraphButtonValue
{
None,
AutomaticBestFit,
ManualAdjustment
};
public enum class LineStyleType
{
Color,
Pattern
};
public ref class TraceLogger sealed
{
public:
static TraceLogger ^ GetInstance();
bool GetTraceLoggingProviderEnabled();
void LogModeChange(CalculatorApp::Common::ViewMode mode);
void LogHistoryItemLoad(CalculatorApp::Common::ViewMode mode, int historyListSize, int loadedIndex);
void LogMemoryItemLoad(CalculatorApp::Common::ViewMode mode, int memoryListSize, int loadedIndex);
@ -48,9 +76,15 @@ public
void LogConverterInputReceived(CalculatorApp::Common::ViewMode mode);
void LogNavBarOpened();
void LogError(CalculatorApp::Common::ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString);
void LogShowHideButtonClicked(bool isHideButton);
void LogGraphButtonClicked(GraphButton buttonName, GraphButtonValue buttonValue);
void LogGraphLineStyleChanged(LineStyleType style);
void LogVariableChanged(Platform::String ^ inputChangedType, Platform::String ^ variableName);
void LogVariableSettingsChanged(Platform::String ^ setting);
void LogGraphSettingsChanged(GraphSettingsType settingsType, Platform::String ^ settingValue);
void LogGraphTheme(Platform::String ^ graphTheme);
internal:
void LogStandardException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ const std::exception& e);
void LogWinRTException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ winrt::hresult_error const& e);
void LogPlatformException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ Platform::Exception ^ e);
void LogInputPasted(CalculatorApp::Common::ViewMode mode);
@ -58,24 +92,8 @@ public
// Create an instance of TraceLogger
TraceLogger();
// As mentioned in Microsoft's Privacy Statement(https://privacy.microsoft.com/en-US/privacystatement#maindiagnosticsmodule),
// sampling is involved in Microsoft's diagnostic data collection process.
// These keywords provide additional input into how frequently an event might be sampled.
// The lower the level of the keyword, the higher the possibility that the corresponding event may be sampled.
void LogLevel1Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields);
void LogLevel2Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields);
void LogLevel3Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields);
std::unique_ptr<TraceActivity> CreateTraceActivity(std::wstring_view activityName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields);
winrt::Windows::Foundation::Diagnostics::LoggingChannel g_calculatorProvider;
std::vector<ButtonLog> buttonLog;
std::vector<int> windowIdLog;
GUID sessionGuid;
uint64 currentWindowCount = 0;
winrt::Windows::Foundation::Diagnostics::LoggingActivity m_appLaunchActivity;
};
}

View file

@ -14,14 +14,17 @@
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace concurrency;
using namespace Graphing::Renderer;
using namespace Platform;
using namespace std;
using namespace Utils;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Storage::Streams;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::Foundation;
using namespace Windows::Storage;
@ -34,17 +37,6 @@ void Utils::IFTPlatformException(HRESULT hr)
}
}
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)
{
constexpr wchar_t unWantedChars[] = { L' ', L',', 8234, 8235, 8236, 8237 };
@ -80,67 +72,6 @@ bool Utils::IsLastCharacterTarget(_In_ wstring const& input, _In_ wchar_t target
return !input.empty() && input.back() == target;
}
void Utils::SerializeCommandsAndTokens(
_In_ shared_ptr<vector<pair<wstring, int>>> const& tokens,
_In_ shared_ptr<vector<shared_ptr<IExpressionCommand>>> const& commands,
DataWriter ^ writer)
{
// Save the size of the commands vector
writer->WriteUInt32(static_cast<unsigned int>(commands->size()));
SerializeCommandVisitor cmdVisitor(writer);
for (const auto& exprCmd : *commands)
{
CalculationManager::CommandType commandType = exprCmd->GetCommandType();
writer->WriteInt32(static_cast<int>(commandType));
exprCmd->Accept(cmdVisitor);
}
writer->WriteUInt32(static_cast<unsigned int>(tokens->size()));
for (const auto& eachToken : *tokens)
{
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<vector<shared_ptr<IExpressionCommand>>> Utils::DeserializeCommands(DataReader ^ reader)
{
auto commandVector = make_shared<vector<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->push_back(exprCmd);
}
return commandVector;
}
const shared_ptr<vector<pair<wstring, int>>> Utils::DeserializeTokens(DataReader ^ reader)
{
auto tokenVector = make_shared<vector<pair<wstring, int>>>();
auto tokensSize = reader->ReadUInt32();
for (unsigned int i = 0; i < tokensSize; ++i)
{
auto stringDataLen = reader->ReadUInt32();
auto stringData = reader->ReadString(stringDataLen);
auto intData = reader->ReadInt32();
tokenVector->emplace_back(stringData->Data(), intData);
}
return tokenVector;
}
DateTime Utils::GetUniversalSystemTime()
{
SYSTEMTIME sysTime = {};
@ -196,3 +127,133 @@ task<String ^> Utils::ReadFileFromFolder(IStorageFolder ^ folder, String ^ fileN
String ^ contents = co_await FileIO::ReadTextAsync(file);
co_return contents;
}
bool Utils::AreColorsEqual(const Color& color1, const Color& color2)
{
return ((color1.A == color2.A)
&& (color1.R == color2.R)
&& (color1.G == color2.G)
&& (color1.B == color2.B));
}
String^ Utils::Trim(String^ value)
{
if (!value)
{
return nullptr;
}
wstring trimmed = value->Data();
Trim(trimmed);
return ref new String(trimmed.c_str());
}
void Utils::Trim(wstring& value)
{
TrimFront(value);
TrimBack(value);
}
void Utils::TrimFront(wstring& value)
{
value.erase(value.begin(), find_if(value.cbegin(), value.cend(), [](int ch){
return !isspace(ch);
}));
}
void Utils::TrimBack(wstring& value)
{
value.erase(find_if(value.crbegin(), value.crend(), [](int ch) {
return !isspace(ch);
}).base(), value.end());
}
String^ Utils::EscapeHtmlSpecialCharacters(String^ originalString, shared_ptr<vector<wchar_t>> specialCharacters)
{
// Construct a default special characters if not provided.
if (specialCharacters == nullptr)
{
specialCharacters = make_shared<vector<wchar_t>>();
specialCharacters->push_back(L'&');
specialCharacters->push_back(L'\"');
specialCharacters->push_back(L'\'');
specialCharacters->push_back(L'<');
specialCharacters->push_back(L'>');
}
bool replaceCharacters = false;
const wchar_t* pCh;
String^ replacementString = nullptr;
// First step is scanning the string for special characters.
// If there isn't any special character, we simply return the original string
for (pCh = originalString->Data(); *pCh; pCh++)
{
if (std::find(specialCharacters->begin(), specialCharacters->end(), *pCh) != specialCharacters->end())
{
replaceCharacters = true;
break;
}
}
if (replaceCharacters)
{
// If we indeed find a special character, we step back one character (the special
// character), and we create a new string where we replace those characters one by one
pCh--;
wstringstream buffer;
buffer << wstring(originalString->Data(), pCh);
for (; *pCh; pCh++)
{
switch (*pCh)
{
case L'&':
buffer << L"&amp;";
break;
case L'\"':
buffer << L"&quot;";
break;
case L'\'':
buffer << L"&apos;";
break;
case L'<':
buffer << L"&lt;";
break;
case L'>':
buffer << L"&gt;";
break;
default:
buffer << *pCh;
}
}
replacementString = ref new String(buffer.str().c_str());
}
return replaceCharacters ? replacementString : originalString;
}
bool operator==(const Color& color1, const Color& color2)
{
return equal_to<Color>()(color1, color2);
}
bool operator!=(const Color& color1, const Color& color2)
{
return !(color1 == color2);
}
// This method calculates the luminance ratio between White and the given background color.
// The luminance is calculate using the RGB values and does not use the A value.
// White or Black is returned
SolidColorBrush ^ Utils::GetContrastColor(Color backgroundColor)
{
auto luminance = 0.2126 * backgroundColor.R + 0.7152 * backgroundColor.G + 0.0722 * backgroundColor.B;
if ((255 + 0.05) / (luminance + 0.05) >= 2.5)
{
return static_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"WhiteBrush"));
}
return static_cast<SolidColorBrush ^>(Application::Current->Resources->Lookup(L"BlackBrush"));
}

Some files were not shown because too many files have changed in this diff Show more