diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..16412e51 --- /dev/null +++ b/.clang-format @@ -0,0 +1,99 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: true +BreakStringLiterals: true +ColumnLimit: 160 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Single +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +#... +# unsupported rules +#BreakInheritanceList: AfterColon +#Language: None +#ObjCBinPackProtocolList: Auto +#PenaltyBreakTemplateDeclaration: 10 +#SpaceBeforeCpp11BracedList: false +#SpaceBeforeCtorInitializerColon: true +#SpaceBeforeInheritanceColon: true +#SpaceBeforeRangeBasedForLoopColon: true + diff --git a/.gitattributes b/.gitattributes index 51617f5f..e0d31f91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,29 +12,6 @@ ############################################################################### *.cs diff=csharp -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -*.sln merge=binary -*.csproj merge=binary -*.vbproj merge=binary -*.vcxproj merge=binary -*.vcproj merge=binary -*.dbproj merge=binary -*.fsproj merge=binary -*.lsproj merge=binary -*.wixproj merge=binary -*.modelproj merge=binary -*.sqlproj merge=binary -*.wwaproj merge=binary - ############################################################################### # behavior for image files # diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 859bebaa..efff3e1d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,36 +6,55 @@ labels: '' assignees: '' --- - +- Search existing issues and make sure this issue is not already filed. +--> **Describe the bug** - + **Steps To Reproduce** - +4. See error +--> **Expected behavior** - + **Screenshots** - + -**Device and Application Information (please complete the following information):** +**Device and Application Information** - OS Build: - Architecture: - Application Version: + - Region: + - Dev Version Installed: - **Additional context** - + + +**Requested Assignment** + +If possible, I would like to fix this. +I'm just reporting this problem. I don't want to fix it. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 3611d4ed..908a643e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,42 +2,53 @@ name: Feature request about: Propose a new feature in the app title: '' -labels: '' +labels: 'Enhancement' assignees: '' --- **Problem Statement** - + **Evidence or User Insights** - + **Proposal** - + **Goals** - + **Non-Goals** - + **Low-Fidelity Concept** - \ No newline at end of file + + +**Requested Assignment** + +If possible, I would like to implement this. +I'm just suggesting this idea. I don't want to implement it. diff --git a/.github/ISSUE_TEMPLATE/localization_suggestion.md b/.github/ISSUE_TEMPLATE/localization_suggestion.md new file mode 100644 index 00000000..dd47f61d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/localization_suggestion.md @@ -0,0 +1,54 @@ +--- +name: Localization Suggestion +about: Report a problem or suggested change to Calculator's localized content. +title: '[Localization] ' +labels: 'Area: World-Readiness' +assignees: '' +--- + + +**Describe the bug** + + +**Steps To Reproduce** + + +**Expected behavior** + + +**Screenshots** + + +**Device and Application Information** + - OS Build: + - Architecture: + - Application Version: + - Region: + - Dev Version Installed: + + + +**Additional context** + diff --git a/.gitignore b/.gitignore index 80d64f53..0167a23c 100644 --- a/.gitignore +++ b/.gitignore @@ -289,5 +289,11 @@ __pycache__/ # Calculator specific Generated Files/ -!/build/config/TRexDefs/** -!src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx \ No newline at end of file +src/GraphControl/GraphingImplOverrides.props +src/CalcViewModel/DataLoaders/DataLoaderConstants.h +!src/Calculator/WindowsDev_TemporaryKey.pfx +!src/CalculatorUnitTests/WindowsDev_TemporaryKey.pfx +!src/x64 +!src/x86 +!src/out +!src/ARM \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9bab5f16..63702de4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,8 @@ The Calculator team encourages community feedback and contributions. Thank you f making Calculator better! There are several ways you can get involved. ## Reporting issues and suggesting new features -If Calculator is not working properly, please file a report in the [Feedback Hub](https://insider.windows.com/en-us/fb/?contextid=130&newFeedback=True). +If Calculator is not working properly, please file a report in the +[Feedback Hub](https://insider.windows.com/en-us/fb/?contextid=130&newFeedback=True). Feedback Hub reports automatically include diagnostic data, such as the version of Calculator you're using. @@ -17,21 +18,38 @@ all community interactions must abide by the [Code of Conduct](CODE_OF_CONDUCT.m ## Finding issues you can help with Looking for something to work on? -[Issues marked *good first issue*](https://github.com/Microsoft/calculator/labels/good%20first%20issue) +Issues marked [``good first issue``](https://github.com/Microsoft/calculator/labels/good%20first%20issue) are a good place to start. -You can also check [the *help wanted* tag](https://github.com/Microsoft/calculator/labels/help%20wanted) -to find other issues to help with. +You can also check the [``help wanted``](https://github.com/Microsoft/calculator/labels/help%20wanted) tag to find +other issues to help with. If you're interested in working on a fix, leave a comment to let everyone know and to help +avoid duplicated effort from others. ## Contributions we accept We welcome your contributions to the Calculator project, especially to fix bugs and to make -improvements which address the top issues reported by Calculator users. +improvements which address the top issues reported by Calculator users. Some general guidelines: -We have a high bar for new features and changes to the user experience. We prefer to evaluate -*prototypes* to ensure that the design meets users' needs before we start discussing implementation -details and reviewing code. We follow a [user-centered process for developing features](docs/NewFeatureProcess.md). -New features need sponsorship from the Calculator team, but we welcome community contributions at -many stages of the process. +* **DO** create one pull request per Issue, and ensure that the Issue is linked in the pull request. +* **DO** follow our [Coding and Style](#style-guidelines) guidelines, and keep code changes as small as possible. +* **DO** include corresponding tests whenever possible. +* **DO** check for additional occurrences of the same problem in other parts of the codebase before submitting your PR. +* **DO** [link the issue](https://github.com/blog/957-introducing-issue-mentions) you are addressing in the + pull request. +* **DO** write a good description for your pull request. More detail is better. Describe *why* the change is being + made and *why* you have chosen a particular solution. Describe any manual testing you performed to validate your change. +* **DO NOT** submit a PR unless it is linked to an Issue marked + [`triage approved`](https://github.com/Microsoft/calculator/issues?q=is%3Aissue+is%3Aopen+label%3A%22Triage%3A+Approved%22). + This enables us to have a discussion on the idea before anyone invests time in an implementation. +* **DO NOT** merge multiple changes into one PR unless they have the same root cause. +* **DO NOT** submit pure formatting/typo changes to code that has not been modified otherwise. + +We follow a [user-centered process for developing features](docs/NewFeatureProcess.md). New features +need sponsorship from the Calculator team, but we welcome community contributions at many stages of +the process. + +> Submitting a pull request for an approved Issue is not a guarantee it will be approved. +> The change must meet our high bar for code quality, architecture, and performance, as well as +> [other requirements](#docs/NewFeatureProcess.md#technical-review). ## Making changes to the code @@ -41,7 +59,8 @@ To learn how to build the code and run tests, follow the instructions in the [RE ### Style guidelines The code in this project uses several different coding styles, depending on the age and history of the code. Please attempt to match the style of surrounding code as much as possible. In new -components, prefer the patterns described in the [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) +components, prefer the patterns described in the +[C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) and the [modern C++/WinRT language projections](https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/). ### Testing @@ -61,18 +80,9 @@ to group your changes into a small number of commits which we can review one at When completing a pull request, we will generally squash your changes into a single commit. Please let us know if your pull request needs to be merged as separate commits. -## Submitting a pull request and participating in code review -Writing a good description for your pull request is crucial to help reviewers and future -maintainers understand your change. More detail is better. -- [Link the issue you're addressing in the pull request](https://github.com/blog/957-introducing-issue-mentions). -- Describe *why* the change is being made and *why* you've chosen a particular solution. -- Describe any manual testing you performed to validate your change. - -Please submit one pull request per issue. Large pull requests which have unrelated changes can be -difficult to review. - +## Review Process After submitting a pull request, members of the calculator team will review your code. We will -assign the request to an appropriate reviewer within two days. Any member of the community may +assign the request to an appropriate reviewer. Any member of the community may participate in the review, but at least one member of the Calculator team will ultimately approve the request. diff --git a/NOTICE.txt b/NOTICE.txt index d115a691..adfcd323 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -37,3 +37,211 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- + +Appium DotNet Driver + +Copyright 2014-2015 Appium Contributors + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index ffba40c0..08d59ee6 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,13 @@ Calculator ships regularly with new features and bug fixes. You can get the late - Standard Calculator functionality which offers basic operations and evaluates commands immediately as they are entered. - Scientific Calculator functionality which offers expanded operations and evaluates commands using order of operations. - Programmer Calculator functionality which offers common mathematical operations for developers including conversion between common bases. +- Date Calculation functionality which offers the difference between two dates, as well as the ability to add/subtract years, months and/or days to/from a given input date. - Calculation history and memory capabilities. - Conversion between many units of measurement. - Currency conversion based on data retrieved from [Bing](https://www.bing.com). +- [Infinite precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) for basic + arithmetic operations (addition, subtraction, multiplication, division) so that calculations + never lose precision. ## Getting started Prerequisites: @@ -34,6 +38,9 @@ Prerequisites: - Open [src\Calculator.sln](/src/Calculator.sln) in Visual Studio to build and run the Calculator app. - For a general description of the Calculator project architecture see [ApplicationArchitecture.md](docs/ApplicationArchitecture.md). +- To run the UI Tests, you need to make sure that + [Windows Application Driver (WinAppDriver)](https://github.com/microsoft/WinAppDriver/releases/latest) + is installed. ## Contributing Want to contribute? The team encourages community feedback and contributions. Please follow our [contributing guidelines](CONTRIBUTING.md). @@ -44,19 +51,24 @@ 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). -## Data / Telemetry +### 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. -Telemetry is disabled in development builds by default, and can be enabled with the `SEND_TELEMETRY` +Diagnostic data is disabled in development builds by default, and can be enabled with the `SEND_DIAGNOSTICS` build flag. +## Currency Converter +Windows Calculator includes a currency converter feature that uses mock data in developer builds. The data that +Microsoft uses for the currency converter feature (e.g., in the retail version of the application) is not licensed +for your use. The mock data will be clearly identifiable as it references planets instead of countries, +and remains static regardless of selected inputs. + ## Reporting Security Issues -Security issues and bugs should be reported privately, via email, to the -Microsoft Security Response Center (MSRC) at <[secure@microsoft.com](mailto:secure@microsoft.com)>. -You should receive a response within 24 hours. If for some reason you do not, please follow up via -email to ensure we received your original message. Further information, including the -[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the -[Security TechCenter](https://technet.microsoft.com/en-us/security/default). +Please refer to [SECURITY.md](./SECURITY.md). ## License Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..1b724719 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,48 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all +source code repositories managed through our GitHub organizations, which include +[Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), +[DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), +[Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets +Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) +of a security vulnerability, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** Instead, please +report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). +If possible, encrypt your message with our PGP key; please download it from the +[Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via +email to ensure we received your original message. Additional information can be found at +[microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better +understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of +[Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + diff --git a/Tools/Scripts/clang-format/clang-format-all.ps1 b/Tools/Scripts/clang-format/clang-format-all.ps1 new file mode 100644 index 00000000..107f3f2c --- /dev/null +++ b/Tools/Scripts/clang-format/clang-format-all.ps1 @@ -0,0 +1,91 @@ +<# + .DESCRIPTION + Helper script to format all header and source files in the repository. + + By default, the script will recursively search under the repo root for + files to format. Users can give explicit parameters indicating how the + search should include and exclude filetypes. + + If users don't want the search functionality, they can instead provide + an explicit list of files to format. + + .PARAMETER RepoRoot + Full path to the root of the repository which is the target of the search. + Will default to the root of the current working directory. + + .PARAMETER Include + Array of filetype extensions to target for formatting. + By default, targets standard extensions for header and source files. + Follows the same rules as the -Include parameter for Get-ChildItem. + + .PARAMETER Exclude + Array of filetype extensions to exclude from formatting. + By default, excludes generated XAML files. + Follows the same rules as the -Exclude paramter for Get-ChildItem. + + .PARAMETER Files + Array of files to format. The script will exit if one of the provided + filepaths does not exist. + + .EXAMPLE + .\clang-format-all.ps1 + + Formats all header and source files under the repository root. + + .EXAMPLE + .\clang-format-all.ps1 -RepoRoot 'S:\repos\calculator' -Include '*.h', '*.cpp' -Exclude '*.g.*' + + Formats all *.h and *.cpp files under 'S:\repos\calculator', excluding files with an extension + like *.g.* + + .EXAMPLE + .\clang-format-all.ps1 -File 'S:\repos\calculator\src\CalcViewModel\UnitConverterViewModel.h', 'S:\repos\calculator\src\CalcViewModel\MemoryItemViewModel.cpp' + + Formats the specified files. +#> +[CmdletBinding( DefaultParameterSetName = 'Search' )] +param( + [Parameter( ParameterSetName = 'Search' )] + [ValidateScript({ Test-Path -PathType Container -Path $_ })] + [string] $RepoRoot = "$( git rev-parse --show-toplevel )", + + [Parameter( ParameterSetName = 'Search' )] + [string[]] $Include = ( '*.h', '*.hh', '*.hpp', '*.c', '*.cc', '*.cpp' ), + + [Parameter( ParameterSetName = 'Search' )] + [string[]] $Exclude = '*.g.*', + + [Parameter( + ParameterSetName = 'Explicit', + Mandatory)] + [ValidateScript({ + $_ | Where-Object { -not (Test-Path -PathType Leaf -Path $_) } | + ForEach-Object { throw "Could not find file: [$_]" } + + return $true + })] + [string[]] $Files +) + +if ($PSCmdlet.ParameterSetName -eq 'Explicit') +{ + # Use the file paths we were given. + $targetFiles = @($Files) +} +else +{ + # Gather the files to be formatted. + $targetFiles = @( + Get-ChildItem -Recurse -Path $RepoRoot -Include $Include -Exclude $Exclude | + Select-Object -ExpandProperty FullName + ) +} + +# Format the files. +$formatParams = @( + '-i' # In-place + '-style=file' # Search for a .clang-format file in the parent directory of the source file. + '-verbose' +) + +clang-format $formatParams $targetFiles \ No newline at end of file diff --git a/Tools/Scripts/clang-format/clang-format-all.sh b/Tools/Scripts/clang-format/clang-format-all.sh new file mode 100644 index 00000000..b54fe6e2 --- /dev/null +++ b/Tools/Scripts/clang-format/clang-format-all.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +function usage { + echo "Usage: $0 DIR..." + exit 1 +} + +# Variable that will hold the name of the clang-format command +FMT="" + +# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent +# that the version number be part of the command. We prefer clang-format if +# that's present, otherwise we work backwards from highest version to lowest +# version. +for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do + if which "$clangfmt" &>/dev/null; then + FMT="$clangfmt" + break + fi +done + +# Check if we found a working clang-format +if [ -z "$FMT" ]; then + echo "failed to find clang-format" + exit 1 +fi + +SRC_PATH="$@" +if [ -z "$SRC_PATH" ]; then + SRC_PATH="../../../src" +fi + +# Check all of the arguments first to make sure they're all directories +for dir in "$SRC_PATH"; do + if [ ! -d "${dir}" ]; then + echo "${dir} is not a directory" + usage + fi +done + +# Run clang-format -i on all of the things +for dir in "$SRC_PATH"; do + pushd "${dir}" &>/dev/null + find . \ + \( -name '*.c' \ + -o -name '*.cc' \ + -o -name '*.cpp' \ + -o -name '*.h' \ + -o -name '*.hh' \ + -o -name '*.hpp' \) \ + -exec "${FMT}" -style=file -i '{}' \; + popd &>/dev/null +done diff --git a/build/config/LocConfigPackageEs.xml b/build/config/LocConfigPackageEs.xml deleted file mode 100644 index d566c7cf..00000000 --- a/build/config/LocConfigPackageEs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/build/config/SignConfig.xml b/build/config/SignConfig.xml deleted file mode 100644 index 7209725e..00000000 --- a/build/config/SignConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/performance.xml b/build/config/TRexDefs/amd64/performance.xml deleted file mode 100644 index e6b6b40e..00000000 --- a/build/config/TRexDefs/amd64/performance.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - RS_APPS_VALIDATE AMD64 Desktop WinPerf Test Run - 134858 - paxeedev - true - true - Performance_AMD64.testlist - - - - VSTS\IsReliabilityRun - True - - - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs4_release.xml b/build/config/TRexDefs/amd64/rs4_release.xml deleted file mode 100644 index 78a52df9..00000000 --- a/build/config/TRexDefs/amd64/rs4_release.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - RS4_RELEASE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs4_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs5_release.xml b/build/config/TRexDefs/amd64/rs5_release.xml deleted file mode 100644 index 7fbccdc2..00000000 --- a/build/config/TRexDefs/amd64/rs5_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS5_RELEASE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs5_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs_apps_validate.xml b/build/config/TRexDefs/amd64/rs_apps_validate.xml deleted file mode 100644 index a90ca288..00000000 --- a/build/config/TRexDefs/amd64/rs_apps_validate.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - RS_APPS_VALIDATE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - - RS_APPS_VALIDATE AMD64 WCOS Test Run - 153648 - paxeedev - true - true - WCOS_AMD64.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/arm/performance.xml b/build/config/TRexDefs/arm/performance.xml deleted file mode 100644 index 5fde2b8a..00000000 --- a/build/config/TRexDefs/arm/performance.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - RS_PRERELEASE ARM WindowsCore WinPerf Test Run - 135258 - paxeedev - true - true - Performance_ARM.testlist - - - - VSTS\IsReliabilityRun - True - - - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/arm/rs_apps_validate.xml b/build/config/TRexDefs/arm/rs_apps_validate.xml deleted file mode 100644 index 277c9761..00000000 --- a/build/config/TRexDefs/arm/rs_apps_validate.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs4_release.xml b/build/config/TRexDefs/x86/rs4_release.xml deleted file mode 100644 index 7f3f2cbf..00000000 --- a/build/config/TRexDefs/x86/rs4_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs4_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs5_release.xml b/build/config/TRexDefs/x86/rs5_release.xml deleted file mode 100644 index 205a9c09..00000000 --- a/build/config/TRexDefs/x86/rs5_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs5_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs_apps_validate.xml b/build/config/TRexDefs/x86/rs_apps_validate.xml deleted file mode 100644 index 130dc8f7..00000000 --- a/build/config/TRexDefs/x86/rs_apps_validate.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - - - RS_PRERELEASE x86 WindowsCoreVM Test Run - 153715 - paxeedev - true - true - WCOS_x86.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TestLists/ARM/Desktop_ARM.testlist b/build/config/TestLists/ARM/Desktop_ARM.testlist deleted file mode 100644 index cf0e5b66..00000000 --- a/build/config/TestLists/ARM/Desktop_ARM.testlist +++ /dev/null @@ -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", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM/Performance_ARM.testlist b/build/config/TestLists/ARM/Performance_ARM.testlist deleted file mode 100644 index 2ec2d933..00000000 --- a/build/config/TestLists/ARM/Performance_ARM.testlist +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "Performance" - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM/WCOS_ARM.testlist b/build/config/TestLists/ARM/WCOS_ARM.testlist deleted file mode 100644 index cf0e5b66..00000000 --- a/build/config/TestLists/ARM/WCOS_ARM.testlist +++ /dev/null @@ -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", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM64/Desktop_ARM64.testlist b/build/config/TestLists/ARM64/Desktop_ARM64.testlist deleted file mode 100644 index a2e620ca..00000000 --- a/build/config/TestLists/ARM64/Desktop_ARM64.testlist +++ /dev/null @@ -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", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM64/WCOS_ARM64.testlist b/build/config/TestLists/ARM64/WCOS_ARM64.testlist deleted file mode 100644 index a2e620ca..00000000 --- a/build/config/TestLists/ARM64/WCOS_ARM64.testlist +++ /dev/null @@ -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", - } - ] -} \ No newline at end of file diff --git a/build/config/rs_apps_utils.json b/build/config/rs_apps_utils.json deleted file mode 100644 index 9c16d817..00000000 --- a/build/config/rs_apps_utils.json +++ /dev/null @@ -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" - } - ] -} \ No newline at end of file diff --git a/build/config/rs_apps_validate.json b/build/config/rs_apps_validate.json deleted file mode 100644 index 3fbe98b5..00000000 --- a/build/config/rs_apps_validate.json +++ /dev/null @@ -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" - } - ] -} \ No newline at end of file diff --git a/build/pipelines/azure-pipelines.ci.yaml b/build/pipelines/azure-pipelines.ci.yaml index 31ce98fb..2d55627a 100644 --- a/build/pipelines/azure-pipelines.ci.yaml +++ b/build/pipelines/azure-pipelines.ci.yaml @@ -6,10 +6,12 @@ trigger: - master -- servicing/* +- release/* +- feature/* pr: - master -- servicing/* +- release/* +- feature/* name: 0.$(Date:yyMM).$(DayOfMonth)$(Rev:rr).0 @@ -33,18 +35,17 @@ jobs: platform: ARM64 condition: not(eq(variables['Build.Reason'], 'PullRequest')) +- template: ./templates/run-ui-tests.yaml + parameters: + platform: x64 + runsettingsFileName: CalculatorUITests.ci.runsettings + - template: ./templates/run-unit-tests.yaml parameters: platform: x64 - reimageServiceConnection: essential-experiences-interactive-reimage - reimageSubscriptionId: a8f5eb47-e59c-44b4-8e42-e70811a047b5 - reimageResourceGroup: EETestPublic - template: ./templates/run-unit-tests.yaml parameters: platform: x86 - reimageServiceConnection: essential-experiences-interactive-reimage - reimageSubscriptionId: a8f5eb47-e59c-44b4-8e42-e70811a047b5 - reimageResourceGroup: EETestPublic - template: ./templates/package-appxbundle.yaml diff --git a/build/pipelines/azure-pipelines.loc.yaml b/build/pipelines/azure-pipelines.loc.yaml index d2bef95e..287188be 100644 --- a/build/pipelines/azure-pipelines.loc.yaml +++ b/build/pipelines/azure-pipelines.loc.yaml @@ -15,30 +15,32 @@ name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) jobs: - job: Localize pool: - name: Package ES Custom Demands Lab A - demands: - - ClientAlias -equals PKGESUTILAPPS - workspace: - clean: outputs + vmImage: windows-2019 + variables: + skipComponentGovernanceDetection: true steps: - - checkout: self - clean: true - - - task: PkgESSetupBuild@10 - displayName: Initialize Package ES + - task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@1 + displayName: Send resources to Touchdown Build inputs: - productName: Calculator - branchVersion: true + teamId: 86 + authId: d3dd8113-65b3-4526-bdca-a00a7d1c37ba + authKey: $(LocServiceKey) + isPreview: false + relativePathRoot: src/Calculator/Resources/en-US/ + resourceFilePath: '*.resw' + outputDirectoryRoot: src/Calculator/Resources/ - - task: PkgESTouchdownLocService@10 - displayName: Package ES Touchdown Loc Service + - script: | + cd $(Build.SourcesDirectory) + git add -A + git diff --cached --exit-code + echo ##vso[task.setvariable variable=hasChanges]%errorlevel% + git diff --cached > $(Build.ArtifactStagingDirectory)\LocalizedStrings.patch + displayName: Check for changes and create patch file + + - task: PublishPipelineArtifact@0 + displayName: Publish patch file as artifact + condition: eq(variables['hasChanges'], '1') inputs: - IsCallToServiceStepSelected: true - IsCheckedInFileSelected: true - CheckinFilesAtOriginFilePath: true - GitLocPath: Loc/Resources - LocConfigFile: build/config/LocConfigPackageEs.xml - AuthenticationMode: OAuth - ClientApplicationID: d3dd8113-65b3-4526-bdca-a00a7d1c37ba - ApplicationKeyID: $(LocServiceKey) - SendToLoc: true + artifactName: Patch + targetPath: $(Build.ArtifactStagingDirectory) \ No newline at end of file diff --git a/build/pipelines/azure-pipelines.release.yaml b/build/pipelines/azure-pipelines.release.yaml index 78b4fd90..09ae291c 100644 --- a/build/pipelines/azure-pipelines.release.yaml +++ b/build/pipelines/azure-pipelines.release.yaml @@ -9,8 +9,8 @@ pr: none variables: versionMajor: 10 - versionMinor: 1902 - versionBuild: $[counter('10.1902.*', 0)] + versionMinor: 2009 + versionBuild: $[counter('10.2009.*', 0)] versionPatch: 0 name: '$(versionMajor).$(versionMinor).$(versionBuild).$(versionPatch)' @@ -30,25 +30,26 @@ jobs: platform: ARM condition: not(eq(variables['Build.Reason'], 'PullRequest')) -- template: ./templates/build-app-internal.yaml +- template: ./templates/run-ui-tests.yaml parameters: - platform: ARM64 - condition: not(eq(variables['Build.Reason'], 'PullRequest')) + platform: x64 + runsettingsFileName: CalculatorUITests.release.runsettings + +- template: ./templates/run-ui-tests.yaml + parameters: + platform: x86 + runsettingsFileName: CalculatorUITests.release.runsettings - template: ./templates/run-unit-tests.yaml parameters: platform: x64 - reimageServiceConnection: macool-sandbox-interactiveDesktopRS5 - reimageSubscriptionId: 012a8008-c00f-45b3-9828-41ebba30141d - reimageResourceGroup: interactiveDesktopRS5 - template: ./templates/run-unit-tests.yaml parameters: platform: x86 - reimageServiceConnection: macool-sandbox-interactiveDesktopRS5 - reimageSubscriptionId: 012a8008-c00f-45b3-9828-41ebba30141d - reimageResourceGroup: interactiveDesktopRS5 - template: ./templates/package-appxbundle.yaml + parameters: + signBundle: true - template: ./templates/prepare-release-internalonly.yaml diff --git a/build/pipelines/templates/build-app-internal.yaml b/build/pipelines/templates/build-app-internal.yaml index 8a75dcec..08d38a1c 100644 --- a/build/pipelines/templates/build-app-internal.yaml +++ b/build/pipelines/templates/build-app-internal.yaml @@ -12,7 +12,7 @@ jobs: displayName: Build ${{ parameters.platform }} condition: ${{ parameters.condition }} pool: - vmImage: vs2017-win2016 + vmImage: windows-2019 variables: BuildConfiguration: Release BuildPlatform: ${{ parameters.platform }} @@ -21,15 +21,15 @@ jobs: steps: - checkout: self clean: true - + - task: UniversalPackages@0 displayName: Download internals package inputs: command: download downloadDirectory: $(Build.SourcesDirectory) - vstsFeed: WindowsApps + vstsFeed: WindowsInboxApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.7 + vstsPackageVersion: 0.0.50 - template: ./build-single-architecture.yaml parameters: @@ -64,4 +64,4 @@ jobs: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: Detect open source components inputs: - sourceScanPath: $(Agent.BuildDirectory) \ No newline at end of file + sourceScanPath: $(Agent.BuildDirectory) diff --git a/build/pipelines/templates/build-app-public.yaml b/build/pipelines/templates/build-app-public.yaml index 78c033ed..c256e74d 100644 --- a/build/pipelines/templates/build-app-public.yaml +++ b/build/pipelines/templates/build-app-public.yaml @@ -10,7 +10,7 @@ jobs: displayName: Build ${{ parameters.platform }} condition: ${{ parameters.condition }} pool: - vmImage: vs2017-win2016 + vmImage: windows-2019 variables: BuildConfiguration: Release BuildPlatform: ${{ parameters.platform }} diff --git a/build/pipelines/templates/build-single-architecture.yaml b/build/pipelines/templates/build-single-architecture.yaml index 92e06f7b..3e826e56 100644 --- a/build/pipelines/templates/build-single-architecture.yaml +++ b/build/pipelines/templates/build-single-architecture.yaml @@ -5,11 +5,10 @@ parameters: extraMsBuildArgs: '' steps: - - 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 # 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 @@ -29,8 +28,8 @@ steps: displayName: 'Build solution src/Calculator.sln' inputs: solution: src/Calculator.sln - vsVersion: 15.0 - msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) ${{ parameters.extraMsBuildArgs }} + vsVersion: 16.0 + 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 diff --git a/build/pipelines/templates/package-appxbundle.yaml b/build/pipelines/templates/package-appxbundle.yaml index 2965c06e..4770af8e 100644 --- a/build/pipelines/templates/package-appxbundle.yaml +++ b/build/pipelines/templates/package-appxbundle.yaml @@ -1,25 +1,28 @@ # 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: vs2017-win2016 + vmImage: windows-2019 workspace: clean: outputs + variables: + skipComponentGovernanceDetection: true steps: - checkout: self clean: true @@ -36,10 +39,13 @@ jobs: filePath: $(Build.SourcesDirectory)\build\scripts\CreateAppxBundleMapping.ps1 arguments: '-InputPath $(Build.ArtifactStagingDirectory)\drop\Release -ProjectName Calculator -OutputFile $(Build.BinariesDirectory)\AppxBundleMapping.txt' - - script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\MakeAppx.exe" bundle /v /bv %BUNDLEVERSION% /f %MAPPINGFILEPATH% /p %OUTPUTPATH%' + - powershell: | + $buildVersion = [version]$Env:BUILDVERSION + $bundleVersion = "2020.$($buildVersion.Minor).$($buildVersion.Build).$($buildVersion.Revision)" + & "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86\MakeAppx.exe" bundle /v /bv $bundleVersion /f $Env:MAPPINGFILEPATH /p $Env:OUTPUTPATH displayName: Make AppxBundle env: - BUNDLEVERSION: $(Build.BuildNumber) + BUILDVERSION: $(Build.BuildNumber) MAPPINGFILEPATH: $(Build.BinariesDirectory)\AppxBundleMapping.txt OUTPUTPATH: $(Build.BinariesDirectory)\Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle @@ -54,4 +60,44 @@ jobs: displayName: Publish AppxBundle artifact inputs: artifactName: appxBundle - pathToPublish: $(Build.ArtifactStagingDirectory)\appxBundle \ No newline at end of file + 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 \ No newline at end of file diff --git a/build/pipelines/templates/prepare-release-internalonly.yaml b/build/pipelines/templates/prepare-release-internalonly.yaml index 6bb3b6cb..be286c65 100644 --- a/build/pipelines/templates/prepare-release-internalonly.yaml +++ b/build/pipelines/templates/prepare-release-internalonly.yaml @@ -1,9 +1,7 @@ # This template contains a job which builds artifacts needed to release the app to the store and to -# Windows using Microsoft-internal systems. It relies Microsoft-internal resources and will not +# 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 @@ -17,6 +15,14 @@ jobs: name: Package ES Lab E workspace: clean: outputs + variables: + skipComponentGovernanceDetection: true + SBMediaRootPath: '$(TEMP)\SBMedia' + SBPackagePath: '$(Build.ArtifactStagingDirectory)\storeBrokerPayload' + SBLogPath: '$(SBPackagePath)\StoreBroker.log' + FlightId: '161f0975-cb5f-475b-8ef6-26383c37621f' + AppId: '9WZDNCRFHVN5' + ProductId: '00009007199266248474' steps: - checkout: self clean: true @@ -26,122 +32,122 @@ jobs: - task: PkgESSetupBuild@10 displayName: Initialize Package ES inputs: - productName: Calculator - disableWorkspace: true + productName: Calculator + disableWorkspace: true 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 - 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 - targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle + sourceFolder: $(Build.ArtifactStagingDirectory)\appxBundleSigned + contents: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle + targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle - task: PkgESVPack@10 displayName: Create and push vpack for app env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: - sourceDirectory: $(Build.ArtifactStagingDirectory)\vpack\appxBundle - description: VPack for the Calculator Application - pushPkgName: calculator.app - version: $(versionMajor).$(versionMinor).$(versionBuild) - owner: paxeeapps + sourceDirectory: $(Build.ArtifactStagingDirectory)\vpack\appxBundle + description: VPack for the Calculator Application + pushPkgName: calculator.app + version: $(versionMajor).$(versionMinor).$(versionBuild) + owner: paxeeapps + provData: false + - task: PublishBuildArtifacts@1 displayName: Publish vpack\app artifact with vpack manifest inputs: - pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME) - artifactName: vpack\app - - # TODO (macool): create and push internal test packages and test config + pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME) + artifactName: vpack\app - task: UniversalPackages@0 displayName: Download internals package inputs: command: download downloadDirectory: $(Build.SourcesDirectory) - vstsFeed: WindowsApps + vstsFeed: WindowsInboxApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.7 + vstsPackageVersion: 0.0.50 - - task: PkgESStoreBrokerPackage@10 - displayName: Create StoreBroker Packages - env: - XES_SERIALPOSTBUILDREADY: True + - powershell: | + # Just modify this line to indicate where your en-us PDP file is. Leave the other lines alone. + $enUSPdpFilePath = "$(Build.SourcesDirectory)\PDP\en-US\PDP.xml" + + # This is going to save the release value from the PDP file to $(SBMediaReleaseVersion) + # which you can then refer to in the UniversalPackages task. + $release = ([xml](Get-Content $enUSPdpFilePath)).ProductDescription.Release.Trim() + Write-Host "##vso[task.setvariable variable=SBMediaReleaseVersion;]$release" + displayName: Determine the PDP Media release version from the en-us PDP file + + - task: UniversalPackages@0 + displayName: Download PDP media (screenshots, trailers) universal package inputs: - addToFlight: false - configPath: tools/Build/StoreBroker/SBCalculatorConfig.json - PDPRootPath: $(Build.SourcesDirectory)\PDP - imagesRootPath: $(Build.SourcesDirectory)\PDPMediaRoot - appxPath: $(Build.ArtifactStagingDirectory)\appxBundleSigned\Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle - useArtifactServiceForMedia: true - outPath: $(Build.ArtifactStagingDirectory)\StoreBrokerPayload - paToken: $(System.AccessToken) - logRootPath: $(Build.ArtifactStagingDirectory)/StoreBrokerLogs + command: download + downloadDirectory: $(SBMediaRootPath)/$(SBMediaReleaseVersion) + vstsFeed: WindowsInboxApps + vstsFeedPackage: calculator-pdp-media + vstsPackageVersion: $(SBMediaReleaseVersion) + + - task: MS-RDX-MRO.windows-store-publish-dev.package-task.store-package@2 + displayName: Create StoreBroker Payload + inputs: + serviceEndpoint: StoreBrokerProxy + sbConfigPath: Tools/Build/StoreBroker/SBCalculatorConfig.json + sourceFolder: $(Build.ArtifactStagingDirectory)/appxBundleSigned + contents: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle + pdpPath: '$(Build.SourcesDirectory)\PDP' + pdpInclude: PDP.xml + pdpMediaPath: '$(SBMediaRootPath)' + outSBPackagePath: '$(SBPackagePath)' + outSBName: SBCalculator - task: PublishBuildArtifacts@1 - displayName: Publish StoreBrokerPayload artifact + displayName: Publish StoreBroker Payload artifact inputs: + pathtoPublish: '$(SBPackagePath)' artifactName: storeBrokerPayload - pathToPublish: $(Build.ArtifactStagingDirectory)/StoreBrokerPayload - - task: PkgESStoreBrokerFlight@10 + - task: MS-RDX-MRO.windows-store-publish-dev.flight-task.store-flight@2 + displayName: 'Flight StoreBroker Payload to team ring' name: StoreBrokerFlight - displayName: Flight package with StoreBroker - env: - XES_SERIALPOSTBUILDREADY: True inputs: - packageToFlight: Custom - appId: 9WZDNCRFHVN5 - flightId: 161f0975-cb5f-475b-8ef6-26383c37621f - submissionDataPath: $(Build.ArtifactStagingDirectory)/StoreBrokerPayload/SBCalculator.json - packagePath: $(Build.ArtifactStagingDirectory)/StoreBrokerPayload/SBCalculator.zip - updatePackageAction: AddPackages - logRootPath: $(Build.ArtifactStagingDirectory)/StoreBrokerLogs - - - task: PublishBuildArtifacts@1 - displayName: Publish StoreBrokerLogs artifact - inputs: - artifactName: storeBrokerLogs - pathToPublish: $(Build.ArtifactStagingDirectory)/StoreBrokerLogs + serviceEndpoint: StoreBrokerProxy + appId: '$(AppId)' + flightId: '$(FlightId)' + inputMethod: JsonAndZip + jsonPath: '$(SBPackagePath)\SBCalculator.json' + zipPath: '$(SBPackagePath)\SBCalculator.zip' + force: true + skipPolling: true + targetPublishMode: Immediate + logPath: '$(SBLogPath)' - task: PkgESStoreBrokerAeroUpload@10 displayName: Upload to Aero flighting dashboard env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: - productId: 00009007199266248474 - flightId: 161f0975-cb5f-475b-8ef6-26383c37621f - submissionId: $(StoreBrokerFlight.WS_SubmissionId) - submissionDataPath: $(Build.ArtifactStagingDirectory)/StoreBrokerPayload/SBCalculator.json - packagePath: $(Build.ArtifactStagingDirectory)/StoreBrokerPayload/SBCalculator.zip + ProductId: '$(ProductId)' + FlightId: '$(FlightId)' + SubmissionId: '$(StoreBrokerFlight.WS_SubmissionId)' + SubmissionDataPath: '$(SBPackagePath)\SBCalculator.json' + PackagePath: '$(SBPackagePath)\SBCalculator.zip' + AeroEnvironment: Production - task: PkgESLateTasks@10 displayName: Run PackageES LateTasks env: - XES_DISABLEPROV: true \ No newline at end of file + XES_DISABLEPROV: true diff --git a/build/pipelines/templates/run-ui-tests.yaml b/build/pipelines/templates/run-ui-tests.yaml new file mode 100644 index 00000000..347a3a8b --- /dev/null +++ b/build/pipelines/templates/run-ui-tests.yaml @@ -0,0 +1,52 @@ +# This template contains jobs to run UI tests using WinAppDriver. + +parameters: + platform: '' + runsettingsFileName: '' + +jobs: +- job: UITests${{ parameters.platform }} + displayName: UITests ${{ parameters.platform }} + dependsOn: Build${{ parameters.platform }} + condition: succeeded() + pool: + vmImage: windows-2019 + variables: + skipComponentGovernanceDetection: true + steps: + - checkout: none + + - powershell: Set-DisplayResolution -Width 1920 -Height 1080 -Force + displayName: Set resolution to 1920x1080 + continueOnError: true + + - task: DownloadBuildArtifacts@0 + displayName: Download AppxBundle and CalculatorUITests + inputs: + artifactName: drop + itemPattern: | + drop/Release/${{ parameters.platform }}/Calculator/AppPackages/** + drop/Release/${{ parameters.platform }}/publish/** + + - task: PowerShell@2 + displayName: Install certificate + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -CertificatePath $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Calculator_$(Build.BuildNumber)_${{ parameters.platform }}.cer -Force + + - task: PowerShell@2 + displayName: Install app + inputs: + filePath: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/Calculator/AppPackages/Calculator_$(Build.BuildNumber)_Test/Add-AppDevPackage.ps1 + arguments: -Force + + - task: VSTest@2 + displayName: Run CalculatorUITests + inputs: + testAssemblyVer2: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/CalculatorUITests.dll + vsTestVersion: 16.0 + runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/${{ parameters.runsettingsFileName }} + platform: ${{ parameters.platform }} + configuration: Release + ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + testFiltercriteria: Priority=0 diff --git a/build/pipelines/templates/run-unit-tests.yaml b/build/pipelines/templates/run-unit-tests.yaml index 6123609e..56f60969 100644 --- a/build/pipelines/templates/run-unit-tests.yaml +++ b/build/pipelines/templates/run-unit-tests.yaml @@ -1,27 +1,20 @@ -# This template contains jobs to run unit tests on the interactive test agents. +# This template contains jobs to run unit tests. parameters: platform: '' - reimageServiceConnection: '' - reimageSubscriptionId: '' - reimageResourceGroup: '' + runsettingsFileName: '' jobs: - job: UnitTests${{ parameters.platform }} displayName: UnitTests ${{ parameters.platform }} dependsOn: Build${{ parameters.platform }} + condition: succeeded() pool: - name: Essential Experiences Interactive - workspace: - clean: outputs + vmImage: windows-2019 + variables: + skipComponentGovernanceDetection: true steps: - checkout: none - - - powershell: Write-Host "##vso[task.setvariable variable=agentInstanceId;isOutput=true]$($env:AgentName -replace '\D+' -as [int])" - name: LogAgentStep - displayName: Log this agent's instance for later cleanup - env: - AgentName: $(Agent.Name) - task: DownloadBuildArtifacts@0 displayName: Download CalculatorUnitTests @@ -39,19 +32,4 @@ jobs: displayName: Run CalculatorUnitTests inputs: testAssemblyVer2: $(Build.ArtifactStagingDirectory)\drop\Release\${{ parameters.platform }}\CalculatorUnitTests\AppPackages\CalculatorUnitTests_Test\CalculatorUnitTests.appx - otherConsoleOptions: /Platform:${{ parameters.platform }} - -- job: CleanUpUnitTests${{ parameters.platform }} - dependsOn: UnitTests${{ parameters.platform }} - condition: and(always(), ne(dependencies.UnitTests${{ parameters.platform }}.Outputs['LogAgentStep.agentInstanceId'], '')) - pool: server - variables: - agentInstanceId: $[ dependencies.UnitTests${{ parameters.platform }}.outputs['LogAgentStep.agentInstanceId'] ] - steps: - - task: InvokeRESTAPI@1 - displayName: Reimage test machine - inputs: - connectionType: connectedServiceNameARM - azureServiceConnection: ${{ parameters.reimageServiceConnection }} - urlSuffix: subscriptions/${{ parameters.reimageSubscriptionId }}/resourceGroups/${{ parameters.reimageResourceGroup }}/providers/Microsoft.Compute/virtualMachineScaleSets/essential/reimage?api-version=2018-10-01 - body: '{ "instanceIds": ["$(agentInstanceId)"] }' \ No newline at end of file + otherConsoleOptions: /Platform:${{ parameters.platform }} \ No newline at end of file diff --git a/docs/ApplicationArchitecture.md b/docs/ApplicationArchitecture.md index 0d7c2ff3..235af889 100644 --- a/docs/ApplicationArchitecture.md +++ b/docs/ApplicationArchitecture.md @@ -153,7 +153,9 @@ The CalcEngine contains the logic for interpreting and performing operations acc ### RatPack -The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for performing its mathematical operations. The interface to this layer is defined in [ratpak.h][ratpak.h]. +The RatPack (short for Rational Pack) is the core of the Calculator model and contains the logic for +performing its mathematical operations (using [infinite precision][Infinite Precision] arithmetic +instead of regular floating point arithmetic). The interface to this layer is defined in [ratpak.h][ratpak.h]. [References]:#################################################################################################### @@ -199,4 +201,5 @@ The RatPack (short for Rational Pack) is the core of the Calculator model and co [CalcManager folder]: ../src/CalcManager [CalculatorManager.h]: ../src/CalcManager/CalculatorManager.h [CalcEngine.h]: ../src/CalcManager/Header Files/CalcEngine.h +[Infinite Precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic [ratpak.h]: ../src/CalcManager/Ratpack/ratpak.h diff --git a/docs/Images/VSInstallationScreenshot.png b/docs/Images/VSInstallationScreenshot.png index 30152691..d027e1bb 100644 Binary files a/docs/Images/VSInstallationScreenshot.png and b/docs/Images/VSInstallationScreenshot.png differ diff --git a/docs/ManualTests.md b/docs/ManualTests.md index a1d9de58..884f0aef 100644 --- a/docs/ManualTests.md +++ b/docs/ManualTests.md @@ -54,6 +54,59 @@ Steps: 2. Select “miles” as the unit type in the output field *Expected: The output starts with is “3.106856”* +### Always-on-Top + +**Test 1** +Steps: +1. Launch the "Calculator" app and navigate to "Standard" Calculator +*Expected: Always-on-Top button's tooltip says "Keep on top"* +2. Click the Always-on-Top button +*Expected: Always-on-Top button's tooltip now says "Back to full view"* +3. Launch the "Notepad" app and put it in full-screen mode +*Expected: Calculator is still on top of Notepad and in Always-on-Top mode* + +**Test 2** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, input “3”, “+”, “3” (do not press “Enter”) +2. Tab over the Always-on-Top button and press "Enter" on the keyboard +*Expected: The application title, hamburger menu, calculator type title, calculation expression (the secondary line above the main display), history button and memory buttons are no longer visible. The main display shows "3"* +2. Press “Enter” +*Expected: The main display shows "6"* +3. Press "Ctrl-H" on the keyboard +*Expected: Nothing happens (history keyboard shortcuts are disabled)* +4. Press "Ctrl-P" on the keyboard, then tab over the Always-on-Top button and press "Enter" on the keyboard again +5. Open the Memory panel +*Expected: Nothing is stored in memory (memory keyboard shortcuts are disabled in Always-on-Top mode) and "6" is in history* + +**Test 3** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +2. Resize the window horizontally +*Expected: The buttons automatically expand or shrink to fit the available screen size* +3. Resize the window vertically +*Expected: The buttons automatically expand or shrink to fit the available screen size and the percent, square-root, squared and reciprocal buttons disappear when the screen height is small* +4. Click the Always-on-Top button again +*Expected: Calculator is in Standard mode and the original window layout from before Step 1 is restored* +5. Click the Always-on-Top button again +*Expected: Calculator is in Always-on-Top mode and the window size from after Step 3 is restored* +6. Close the "Calculator" app +7. Launch the "Calculator" app again and click the Always-on-Top button +*Expected: The window size from right before closing from Always-on-Top mode (ie. after Step 5) is restored* + +**Test 4** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +2. Input "/", "0", “Enter” on the keyboard +*Expected: "Result is undefined" is displayed in the system default app language* +3. Click the Always-on-Top button again +*Expected: Calculator is in Standard mode and all operator (except for "CE", "C", "Delete" and "=") and memory buttons are disabled + +**Test 5** +Steps: +1. Launch the "Calculator" app and navigate to "Scientific" Calculator +*Expected: The Always-on-Top button is hidden* +2. Navigate to "Standard" Calculator +*Expected: The Always-on-Top button is visible* ## Basic Verification Tests @@ -207,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.* @@ -249,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
+*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
+*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
+*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
+*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
+*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
+*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
+*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
+*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:
+ *(Note: IP = Inflection Points, VA = Vertical Asymptotes, HA = Horizontal Asymptotes, OA = Oblique Asymptotes)*
+ a. **y=x**
+ *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*
+ b. **y=1/x**
+ *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*
+ c. **y=x^2**
+ *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*
+ d. **y=x^3**
+ *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*
+ e. **y=e^x**
+ *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*
+ f. **y=ln(x)**
+ *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*
+ g. **y=sin(x)**
+ *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𝜋*
+ h. **y=cos(x)**
+ *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𝜋*
+ i. **y=tan(x)**
+ *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: 𝜋*
+ j. **y=sqrt(25-x^2)**
+ *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*
+ k. **y=(-3x^2+2)/(x-1)**
+ *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√3−6)⁆; Min: ⁅(−√3/3+1,2√3−6)⁆; 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*
+ l. **y=sin(sin(x))** ("too complex" error test)
+ *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*
+ *These features are too complex for Calculator to calculate: Range, X Intercept, Period, Minima, Maxima, Inflection Points, Monotonicity* + m. **y=mx+b**
+ *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.* @@ -278,78 +382,113 @@ Steps: Steps: 1. Launch the "Calculator" app. - For All Applicable Modes verify the following: -2. Press **Alt +1** to Enter "Standard" mode + 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 *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 + +### Always-on-Top + +**Test 1** +Steps: +1. Change the system default app language to Arabic +2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +*Expected: UI/Menu is localized (for example, the title bar buttons is in right-to-left order)* +3. Input "/", "0", “Enter” on the keyboard +*Expected: Error message is in Arabic* + +## Ease of Access Tests + +### Always-on-Top + +**Test 1** +Steps: +1. Open the "Narrator" app +2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +3. Tab over the Always-on-Top button +*Expected: Narrator reads the localized version of "Back to full view"* +4. Tab over the main results field +*Expected: Narrator reads the localized version of exactly what's displayed (ie. "0")* +5. Tab over the rest of the UI elements +*Expected: Narrator reads the localized version of the UI elements' contents* diff --git a/docs/NewFeatureProcess.md b/docs/NewFeatureProcess.md index 66c9a635..6f514d00 100644 --- a/docs/NewFeatureProcess.md +++ b/docs/NewFeatureProcess.md @@ -1,7 +1,8 @@ # New feature process ## Where do I submit my idea for a new feature? -The easiest way to submit new feature requests is through [Feedback Hub](https://insider.windows.com/en-us/fb/?contextid=130). +The easiest way to submit new feature requests is through +[Feedback Hub](https://insider.windows.com/en-us/fb/?contextid=130). In Feedback Hub, any Windows user (even if they aren't on GitHub) can upvote suggestions. The Calculator team reviews these suggestions regularly, and when we're ready to work on an idea we create [feature pitch issues here on GitHub](https://github.com/Microsoft/calculator/issues?q=is%3Aissue+is%3Aopen+project%3AMicrosoft%2Fcalculator%2F1). @@ -12,73 +13,45 @@ product. The [Feature Tracking board](https://github.com/Microsoft/calculator/pr all the features we're working on and where they're at in the process. ## Do I need to follow this process? Can I just start coding and submit a PR? -You *do not* need to follow this process for bug fixes, performance improvements, or changes to the -development tools. To contribute these changes, [discuss the proposed change in an issue](https://github.com/Microsoft/calculator/issues/new) +You **do not** need to follow this process for bug fixes, performance improvements, or changes to the +development tools. To contribute these changes, +[discuss the proposed change in an issue](https://github.com/Microsoft/calculator/issues/new) and then submit a pull request. -You *do* need to follow this process for any change which "users will notice". This applies +You **do** need to follow this process for any change which "users will notice". This applies especially to new features and major visual changes. ## Step 1: Feature pitch -The feature pitch concisely describes a point of view on the problem the new feature should solve. -It will typically include these sections: +Feature pitches are submitted as issues on GitHub using the +[Feature Request template](https://github.com/Microsoft/calculator/issues/new?assignees=&labels=&template=feature_request.md&title=). +We encourage discussion on open issues, and as discussion progresses we will edit the issue description to refine the +idea until it is ready for review. -* **Problem Statement**: What problem are we trying to solve? Who’s the target audience? Is there a - customer need or pain point we need to remedy? Is there a business goal or metric we are trying - to improve? Do we have a hypothesis we want to prove or disprove? -* **Evidence or User Insights**: Why should we do this? Potential sources of data: Feedback Hub, - other GitHub issues, other anecdotes from listening to customers in person or online, request - from another team, telemetry data, user research, market or competitive research -* **Proposal**: How will the solution/feature help us solve the problem? How will it meet the - target audience’s needs? If there are business goals or metrics, how does this improve them? -* **Goals**: What you want to accomplish with this feature. Typical examples include “User Can - *perform some task*” -* **Non-Goals**: Things we are explicitly not doing or supporting or that are out of scope, - including reasons why. -* **Low-Fidelity Concept**: Show as much of the experience as needed to explain the idea. This - can be as simple as a napkin drawing but can also be a code prototype, a PowerPoint walkthrough, - or a design comp. +We review pitches regularly, and will approve or close issues based on whether they broadly align with the +[Calculator roadmap](https://github.com/Microsoft/calculator/blob/master/docs/Roadmap.md). Approved pitches are moved +into [planning](https://github.com/Microsoft/calculator/projects/1) on the feature tracking board. -The low-fidelity concept should be kept simple at this stage and refined during the pre-production -process. +## Step 2: Planning +For most features, the output of this phase is a specification which describes how the feature will work, supported by +design renderings and code prototypes as needed. The original issue will continue to track the overall progress of the +feature, but we will create and iterate on spec documentation in the +[Calculator Spec repo](https://github.com/Microsoft/calculator-specs). Sometimes we'll learn new things about a feature +proposal during planning, and we'll edit or close the original pitch. -Feature pitches are submitted as issues on GitHub. We encourage discussion on open issues, and as -discussion progresses we will edit the issue description to refine the idea. - -## Step 2: Pre-production -In the pre-production phase, we experiment with a variety of ways to address the goals described in -the feature pitch. The output of this phase is a specification which demonstrates how the feature -will work, supported by design renderings and code prototypes as needed. Sometimes we'll learn new -things about a feature proposal during pre-production, and we'll edit or close the original pitch. - -We welcome community participation in the pre-production process. The GitHub issue will be the -primary place to share progress updates. - -The best ideas often come from trying many ideas during the pre-production phase. To enable rapid +We welcome community participation throughout planning. The best ideas often come from trying many ideas during +the planning phase. To enable rapid experimentation, we encourage developing and sharing rough ideas—maybe even with pencil and paper—before making designs pixel-perfect or making code robust and maintainable. -### Spec review -Once there is a high-fidelity design which addresses the goals described in the original pitch, the -Microsoft product team will review the prototype and ensure all items on this checklist are -addressed: +After the [spec review](https://github.com/Microsoft/calculator-specs#spec-review) is completed, we will move the issue +into [implementation](https://github.com/Microsoft/calculator/projects/1) on the feature tracking board. In _some_ cases, +all of the details of an idea can be captured concisely in original feature pitch. When that happens, we may move ideas +directly into implementation. -- [ ] Is there a high-fidelity design which gives reviewers a clear idea of how the feature will - look and function when implemented? -- [ ] Has the plan been shared with the community (documented on the wiki and updates posted in the - original issue) and have others been given an opportunity to provide suggestions? -- [ ] Are [Fluent design principles](https://docs.microsoft.com/en-us/windows/uwp/design/fluent-design-system/) - followed? If we do something which deviates from the guidelines, do we have a good reason? -- [ ] Does the design include provisions for [all users](https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/designing-inclusive-software) - and [all cultures](https://docs.microsoft.com/en-us/windows/uwp/design/globalizing/guidelines-and-checklist-for-globalizing-your-app)? -- [ ] Is it technically feasible to build this feature? Take a look at the "before committing" - checklist below and identify any issues which are likely to be blockers. - -## Step 3: Production -A feature can be implemented by the original proposer, a Microsoft team member, or by other -community members. Code contributions and testing help are greatly appreciated. Please let us know -in the issue comments if you're actively working on a feature so we can ensure it's assigned to -you. +## Step 3: Implementation +A feature can be implemented by the original submitter, a Microsoft team member, or by other +community members. Code contributions and testing help are greatly appreciated. Please let everyone know if you're +actively working on a feature to help avoid duplicated efforts from others. You might be able to reuse code written during the prototype process, although there will typically be more work required to make the solution robust. Once the code is ready, you can begin @@ -122,7 +95,8 @@ new features, the Microsoft team considers at least these items: - [ ] Run the perf tests to measure any increase in startup time. Move work out of the startup path if possible. - [ ] If the change adds additional logging: - - [ ] All logging should use [TraceLogging](https://docs.microsoft.com/en-us/windows/desktop/tracelogging/trace-logging-about). + - [ ] All logging should use + [TraceLogging](https://docs.microsoft.com/en-us/windows/desktop/tracelogging/trace-logging-about). - [ ] Unnecessary log events should be removed, or configured so that they are collected only when needed to debug issues or measure feature usage. - [ ] If the change reads user data from files or app settings: diff --git a/docs/Roadmap.md b/docs/Roadmap.md index 93b1428e..59a4e547 100644 --- a/docs/Roadmap.md +++ b/docs/Roadmap.md @@ -4,14 +4,12 @@ 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 the ability for users to pin Calculator on top of other windows - * Providing additional customization options - * [Your feature idea here] - please review our [new feature development process](https://github.com/Microsoft/calculator/blob/master/docs/NewFeatureProcess.md) to get started! +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. diff --git a/internal/Calculator.TestPackage/Calculator.TestPackage.csproj b/internal/Calculator.TestPackage/Calculator.TestPackage.csproj deleted file mode 100644 index 2a3a29d2..00000000 --- a/internal/Calculator.TestPackage/Calculator.TestPackage.csproj +++ /dev/null @@ -1,131 +0,0 @@ - - - - - Debug - AnyCPU - {24767C43-CD5A-4DC9-8D6B-429F255524E5} - Library - Properties - Calculator.TestPackage - Calculator.TestPackage - .NETPortable - v5.0 - .NETCore,Version=v5.0 - UAP - 10.0.17763.0 - 10.0.17134.0 - 512 - - true - True - - - true - full - false - bin\Debug\x86 - DEBUG;TRACE - prompt - 4 - x86 - - - pdbonly - true - bin\Release\x86 - TRACE - prompt - 4 - x86 - - - true - full - false - bin\Debug\x64 - DEBUG;TRACE - prompt - 4 - x64 - - - pdbonly - true - bin\Release\x64 - TRACE - prompt - 4 - x64 - - - true - full - false - bin\Debug\arm - DEBUG;TRACE - prompt - 4 - ARM - - - pdbonly - true - bin\Release\arm - TRACE - prompt - 4 - ARM - - - true - full - false - bin\Debug\ARM64\ - DEBUG;TRACE - prompt - 4 - ARM64 - - - pdbonly - true - bin\Release\ARM64\ - TRACE - prompt - 4 - ARM64 - - - - - - - - {9447424a-0e05-4911-beb8-e0354405f39a} - Calculator - - - - 15.0 - - - - - - - - - - - - - @(AppxBundleOutput->'%(RootDir)%(Directory)') - $(UniversalTestCustomMacros)AppxPackageVCLibsDependency=$(AppxPackageTestDir)Dependencies\$(PlatformTarget)\Microsoft.VCLibs.$(PlatformTarget).Debug.14.00.appx; - $(UniversalTestCustomMacros)AppxPackageVCLibsDependency=$(AppxPackageTestDir)Dependencies\$(PlatformTarget)\Microsoft.VCLibs.$(PlatformTarget).14.00.appx; - $(UniversalTestCustomMacros)AppxPackageWinUIDependency=$(AppxPackageTestDir)Dependencies\$(PlatformTarget)\Microsoft.UI.Xaml.2.0.appx; - $(UniversalTestCustomMacros)AppxPackagePublicKeyFile=@(AppxPackagePublicKeyFile->'%(FullPath)');AppxBundleOutput=@(AppxBundleOutput->'%(FullPath)'); - - - - diff --git a/internal/Calculator.TestPackage/Calculator.TestPackage.wm.xml b/internal/Calculator.TestPackage/Calculator.TestPackage.wm.xml deleted file mode 100644 index 1f3e6c26..00000000 --- a/internal/Calculator.TestPackage/Calculator.TestPackage.wm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - diff --git a/internal/Calculator.TestPackage/project.json b/internal/Calculator.TestPackage/project.json deleted file mode 100644 index 3f3f1eb9..00000000 --- a/internal/Calculator.TestPackage/project.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "dependencies": { - "Microsoft.TestInfrastructure.UniversalTest": "1.0.20181107.1" - }, - "frameworks": { - "netcore50": {} - }, - "runtimes": { - "win10-arm": {}, - "win10-x64": {}, - "win10-x86": {} - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Calculator.UIAutomationLibrary.csproj b/internal/Calculator.UIAutomationLibrary/Calculator.UIAutomationLibrary.csproj deleted file mode 100644 index f752c1a2..00000000 --- a/internal/Calculator.UIAutomationLibrary/Calculator.UIAutomationLibrary.csproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - Debug - AnyCPU - {A43517B5-8BE3-4312-913F-004978C34444} - Library - Properties - Calculator.UIAutomationLibrary - Calculator.UIAutomationLibrary - .NETPortable - v5.0 - .NETCore,Version=v5.0 - UAP - 10.0.17763.0 - 10.0.17134.0 - 512 - 15.0 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - true - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15.0 - - - - - \ No newline at end of file diff --git a/internal/Calculator.UIAutomationLibrary/CalculatorAppLauncher.cs b/internal/Calculator.UIAutomationLibrary/CalculatorAppLauncher.cs deleted file mode 100644 index df2ad9c3..00000000 --- a/internal/Calculator.UIAutomationLibrary/CalculatorAppLauncher.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Etw.Managed; -using Microsoft.OneCoreUap.Test.AppModel; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Calculator.UIAutomationLibrary.Components; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary -{ - /// - /// Class that can open and close the Calculator app. - /// - public static class CalculatorAppLauncher - { - public const string CoreWindowClassName = "Windows.UI.Core.CoreWindow"; - - // This doesn't actually work right now because popup will disappear - // Bug 13713223: ContentDialog/Popup does not show up in the UIA tree when Windows.Current.Content has an AutomationName set. - // public static readonly UICondition TopLevelWindowUICondition = UICondition.CreateFromId(Constants.TopLevelWindowAutomationId); - public static readonly UICondition CoreWindowUICondition = UICondition.CreateFromClassName(CoreWindowClassName) - .AndWith(UICondition.CreateFromName(Constants.AppWindowName)); - - /// - /// Launch the app - /// - public static CalculatorAppLfm Launch() - { - Log.Comment("Launching Calculator and waiting for first page load..."); - - // Need to set this for the MITALite Tap~ methods to work on high DPI screens. - UAPApp.SetTestDPIAwareness(); - - // We want to be able to see any element in the tree - Context.RawContext.Activate(); - - // Set default wait timeout. - MS.Internal.Mita.Foundation.Waiters.Waiter.DefaultTimeout = TimeSpan.FromSeconds(30); - - // Enable Mita internal logging. - MS.Internal.Mita.Foundation.Utilities.Log.OutImplementation = (s, a) => { Log.Comment($"- [MitaLite] { string.Format(s, a) }"); }; - - using (EtwWaiter appLaunchWaiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AppLaunchEndETWEventName)) - { - var viewDescriptor = NavigationHelper.LaunchApplication(Constants.PackageAppUserModelId); - appLaunchWaiter.Wait(TimeSpan.FromSeconds(30)); - - Window calculatorWindow = new Window(UIObject.Root.Descendants.Find(CoreWindowUICondition)); - Debug.Assert(calculatorWindow.ClassName == CoreWindowClassName); - - // Move our window to the foreground. - WindowHelper.SetAsForeground(calculatorWindow.GetTopLevelWindow()); - - return new CalculatorAppLfm(new CalculatorAppPom(calculatorWindow), viewDescriptor); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppLfm.cs deleted file mode 100644 index 41c34be5..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppLfm.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.OneCoreUap.Test.AppModel; -using MS.Internal.Mita.Foundation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class CalculatorAppLfm - { - private readonly IViewDescriptor viewDescriptor; - - public CalculatorAppLfm(CalculatorAppPom objectModel, IViewDescriptor viewDescriptor) - { - this.ObjectModel = objectModel; - this.viewDescriptor = viewDescriptor; - } - - public CalculatorAppPom ObjectModel { get; } - - public MainPageLfm MainPageLfm - { - get - { - return new MainPageLfm(this.ObjectModel.MainPagePom); - } - } - - public void Close() - { - // ObjectModel is essentially the window ui object. - if (this.viewDescriptor != null) - { - NavigationHelper.CloseApplication(this.viewDescriptor.AUMID); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppPom.cs b/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppPom.cs deleted file mode 100644 index dac82b21..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/App/CalculatorAppPom.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class CalculatorAppPom : UIObject - { - /// - /// Creates a new instance of the class. - /// - /// UIObject for the calculator app window. - public CalculatorAppPom(UIObject uiObject) - : base(uiObject) - { - } - public MainPagePom MainPagePom - { - get - { - return new MainPagePom(this); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/ContentDialogLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/ContentDialogLfm.cs deleted file mode 100644 index 08888c96..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/ContentDialogLfm.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class ContentDialogLfm - { - public ContentDialogLfm(ContentDialogPom objectModel) - { - this.ObjectModel = objectModel; - } - - public ContentDialogPom ObjectModel { get; } - - public void InvokePrimary() - { - this.ObjectModel.PrimaryButton.Invoke(); - } - public void InvokeSecondary() - { - this.ObjectModel.SecondaryButton.Invoke(); - } - - public void InvokeClose() - { - this.ObjectModel.CloseButton.Invoke(); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/ContentDialogPom.cs b/internal/Calculator.UIAutomationLibrary/Components/ContentDialogPom.cs deleted file mode 100644 index b52e97db..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/ContentDialogPom.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class ContentDialogPom : UIObject - { - private static readonly UICondition textScrollViewerCondition = UICondition.CreateFromId("ContentScrollViewer"); - private static readonly UICondition titleTextBlockCondition = UICondition.CreateFromClassName("TextBlock"); - - private static readonly UICondition primaryButtonCondition = - UICondition.CreateFromClassName("Button") - .AndWith(UICondition.CreateFromId("PrimaryButton")); - private static readonly UICondition secondaryButtonCondition = - UICondition.CreateFromClassName("Button") - .AndWith(UICondition.CreateFromId("SecondaryButton")); - private static readonly UICondition closeButtonCondition = - UICondition.CreateFromClassName("Button") - .AndWith(UICondition.CreateFromId("CloseButton")); - - public ContentDialogPom(UIObject uiObject) : base(uiObject) - { - } - - public Button PrimaryButton - { - get - { - return new Button(this.Children.Find(primaryButtonCondition)); - } - } - - public Button SecondaryButton - { - get - { - return new Button(this.Children.Find(secondaryButtonCondition)); - } - } - - public Button CloseButton - { - get - { - return new Button(this.Children.Find(closeButtonCondition)); - } - } - - public string Title - { - get - { - var scrollViewer = this.Children.Find(textScrollViewerCondition); - var textBlock = scrollViewer.Children.Find(titleTextBlockCondition); - return textBlock.Name; - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutLfm.cs deleted file mode 100644 index d6b3e00c..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutLfm.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Calculator.UIAutomationLibrary.Components -{ - public class AboutFlyoutLfm : ICanFocusWithClicks - { - private const string FlyoutId = "FlyoutNav"; - - /// - /// Initializes a new instance of the class. - /// - /// The AboutFlyoutPom that represents the About flyout panel. - public AboutFlyoutLfm(AboutFlyoutPom objectModel) - { - this.ObjectModel = objectModel; - } - - public AboutFlyoutPom ObjectModel { get; } - - public void FocusWithClicks() - { - this.ObjectModel.Title.DoubleClick(); - } - - public void Close() - { - this.ObjectModel.SendKeys("{ESC}"); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutPom.cs deleted file mode 100644 index 29fa28ce..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/AboutFlyoutPom.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class AboutFlyoutPom : UIObject - { - private const string TitleId = "Header"; - - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the navigation menu. - public AboutFlyoutPom(UIObject uiObject) : base(uiObject) - { - } - - public UIObject Title - { - get - { - return new UIObject(this.Descendants.Find(TitleId)); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/CalculatorBasePom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/CalculatorBasePom.cs deleted file mode 100644 index c8cdd952..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/CalculatorBasePom.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the Display section of the calculator modes. - /// - public class CalculatorBasePom : UIObject - { - private const string ExpressionContainerId = "CalculatorExpression"; - private const string NormalOutputId = "normalOutput"; - - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the standard calculator. - public CalculatorBasePom(UIObject uiObject) : base(uiObject) - { - } - - public TextBlock Expression - { - get - { - return new TextBlock(this.Descendants.Find(ExpressionContainerId)); - } - } - - public UIEventWaiter GetExpressionChangedWaiter() - { - return new PropertyChangedEventWaiter(this.Expression, UIProperty.Get("Name")); - } - - public TextBlock Display - { - get - { - return new TextBlock(this.Descendants.Find(NormalOutputId)); - } - } - - public UIEventWaiter GetDisplayChangedWaiter() - { - return new PropertyChangedEventWaiter(this.Display, UIProperty.Get("Name")); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorLfm.cs deleted file mode 100644 index 727b5dd9..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorLfm.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Calculator.UIAutomationLibrary.Components -{ - public class DateCalculatorLfm - { - public DateCalculatorLfm(DateCalculatorPom dateCalculatorPom) - { - this.ObjectModel = dateCalculatorPom; - } - - public DateCalculatorPom ObjectModel { get; } - - public void EnsureDateDifferenceMode() - { - this.OpenModeSelector(); - this.ObjectModel.ModeSelector.AllItems[0].Select(); - } - - public void EnsureAddSubtractMode() - { - this.OpenModeSelector(); - this.ObjectModel.ModeSelector.AllItems[1].Select(); - } - - private void OpenModeSelector() - { - using (var waiter = this.ObjectModel.ModeSelector.GetExpandedWaiter()) - { - this.ObjectModel.ModeSelector.Expand(); - waiter.TryWait(); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorPom.cs deleted file mode 100644 index 2c240f0c..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/DateCalculatorPom.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class DateCalculatorPom : UIObject - { - private const string ModeSelectorId = "DateCalculationOption"; - - public DateCalculatorPom(UIObject uiObject) : base(uiObject) - { - } - - public ComboBox ModeSelector - { - get - { - return new ComboBox(this.Descendants.Find(ModeSelectorId)); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPageLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPageLfm.cs deleted file mode 100644 index 41f12eaf..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPageLfm.cs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Etw.Managed; -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class MainPageLfm : ICanFocusWithClicks - { - public MainPageLfm(MainPagePom objectModel) - { - this.ObjectModel = objectModel; - } - - public MainPagePom ObjectModel { get; } - - public NavBarLfm OpenNavBar() - { - using (EtwWaiter waiter = this.ObjectModel.GetNavBarOpenedWaiter()) - { - this.ObjectModel.NavButton.Invoke(); - waiter.Wait(); - } - - return new NavBarLfm(this.ObjectModel.NavBarPom); - } - - public void CloseNavBar() - { - this.ObjectModel.NavBarPom.CloseButton.Invoke(); - } - - public void FocusWithClicks() - { - this.ObjectModel.Header.DoubleClick(); - } - - public StandardCalculatorLfm NavigateToStandardCalculator() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectStandard(); - waiter.TryWait(); - } - - return new StandardCalculatorLfm(this.ObjectModel.StandardCalculatorPom); - } - - public ScientificCalculatorLfm NavigateToScientificCalculator() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectScientific(); - waiter.TryWait(); - } - - return new ScientificCalculatorLfm(this.ObjectModel.ScientificCalculatorPom); - } - - public ProgrammerCalculatorLfm NavigateToProgrammerCalculator() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectProgrammer(); - waiter.TryWait(); - } - - return new ProgrammerCalculatorLfm(this.ObjectModel.ProgrammerCalculatorPom); - } - - public DateCalculatorLfm NavigateToDateCalculator() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectDate(); - waiter.TryWait(); - } - - return new DateCalculatorLfm(this.ObjectModel.DateCalculatorPom); - } - - public UnitConverterLfm NavigateToCurrencyConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectCurrency(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToVolumeConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectVolume(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToLengthConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectLength(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToWeightConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectWeight(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToTemperatureConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectTemperature(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToEnergyConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectEnergy(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToAreaConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectArea(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToSpeedConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectSpeed(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToTimeConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectTime(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToPowerConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectPower(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToDataConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectData(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToPressureConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectPressure(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public UnitConverterLfm NavigateToAngleConverter() - { - var navBar = this.OpenNavBar(); - using (var waiter = this.ObjectModel.GetModeChangedWaiter()) - { - navBar.SelectAngle(); - waiter.TryWait(); - } - - return new UnitConverterLfm(this.ObjectModel.UnitConverterPom); - } - - public AboutFlyoutLfm OpenAboutFlyout() - { - var navBar = this.OpenNavBar(); - using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AboutFlyoutOpenedETWEventName)) - { - navBar.SelectAbout(); - waiter.Wait(); - } - - return new AboutFlyoutLfm(this.ObjectModel.AboutFlyoutPom); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPagePom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPagePom.cs deleted file mode 100644 index 7fa70bb9..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/MainPagePom.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Etw.Managed; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Physical Object Model for the app window. - /// POM is the implementation model of the app. - /// See https://en.wikipedia.org/wiki/Model-based_testing for model-based testing. - /// - public class MainPagePom : UIObject - { - private const string NavButtonId = "TogglePaneButton"; - private const string SplitViewPaneRootId = "PaneRoot"; - private const string NavBarFlyoutId = "FlyoutNav"; - private const string HeaderId = "Header"; - private const string AboutPageFlyoutId = "AboutPageFlyout"; - - public MainPagePom(UIObject uiObject) - : base(uiObject) - { - } - - public Button NavButton => new Button(this.Descendants.Find(UICondition.CreateFromId(NavButtonId))); - - public UIObject Header => new UIObject(this.Descendants.Find(HeaderId)); - - public NavBarPom NavBarPom => new NavBarPom(this.Children.Find(SplitViewPaneRootId)); - - public EtwWaiter GetNavBarOpenedWaiter() => new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.NavBarOpenedETWEventName); - - public StandardCalculatorPom StandardCalculatorPom => new StandardCalculatorPom(this); - - public ScientificCalculatorPom ScientificCalculatorPom => new ScientificCalculatorPom(this); - - public ProgrammerCalculatorPom ProgrammerCalculatorPom => new ProgrammerCalculatorPom(this); - - public DateCalculatorPom DateCalculatorPom => new DateCalculatorPom(this); - - public UnitConverterPom UnitConverterPom => new UnitConverterPom(this); - - public AboutFlyoutPom AboutFlyoutPom => new AboutFlyoutPom(this.Descendants.Find(AboutPageFlyoutId)); - - public EtwWaiter GetModeChangedWaiter() => new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.AppModeChangeEndETWEventName); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarLfm.cs deleted file mode 100644 index 9201eb97..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarLfm.cs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Windows.Automation; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Patterns; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the NavBar of the Calculator app. - /// - public class NavBarLfm : ICanFocusWithClicks - { - /// - /// Initializes a new instance of the class. - /// - /// The NavBarPom that represents the NavBar. - public NavBarLfm(NavBarPom objectModel) - { - this.ObjectModel = objectModel; - } - - public NavBarPom ObjectModel { get; } - - public void SelectStandard() - { - SelectItem(this.ObjectModel.StandardMenuItem); - } - - public void SelectScientific() - { - SelectItem(this.ObjectModel.ScientificMenuItem); - } - - public void SelectProgrammer() - { - SelectItem(this.ObjectModel.ProgrammerMenuItem); - } - - public void SelectDate() - { - SelectItem(this.ObjectModel.DateMenuItem); - } - - public void SelectCurrency() - { - SelectItem(this.ObjectModel.CurrencyMenuItem); - } - - public void SelectVolume() - { - SelectItem(this.ObjectModel.VolumeMenuItem); - } - - public void SelectLength() - { - SelectItem(this.ObjectModel.LengthMenuItem); - } - - public void SelectWeight() - { - SelectItem(this.ObjectModel.WeightMenuItem); - } - - public void SelectTemperature() - { - SelectItem(this.ObjectModel.TemperatureMenuItem); - } - - public void SelectEnergy() - { - SelectItem(this.ObjectModel.EnergyMenuItem); - } - - public void SelectArea() - { - SelectItem(this.ObjectModel.AreaMenuItem); - } - - public void SelectSpeed() - { - SelectItem(this.ObjectModel.SpeedMenuItem); - } - - public void SelectTime() - { - SelectItem(this.ObjectModel.TimeMenuItem); - } - - public void SelectPower() - { - SelectItem(this.ObjectModel.PowerMenuItem); - } - - public void SelectData() - { - SelectItem(this.ObjectModel.DataMenuItem); - } - - public void SelectPressure() - { - SelectItem(this.ObjectModel.PressureMenuItem); - } - - public void SelectAngle() - { - SelectItem(this.ObjectModel.AngleMenuItem); - } - - public void SelectAbout() - { - this.ObjectModel.AboutButton.Invoke(); - } - - public void Close() - { - this.ObjectModel.CloseButton.Invoke(); - } - - public void FocusWithClicks() - { - // To focus (for AccSpot) without changing anything, click to the right of the close button. - Button button = this.ObjectModel.CloseButton; - int xPos = button.BoundingRectangle.Width + Constants.ClickMargin; - int yPos = button.BoundingRectangle.Height / 2; - button.DoubleClick(PointerButtons.Primary, xPos, yPos); - } - - private void SelectItem(ListViewItem item) - { - if (item.IsSelected) - { - this.Close(); - } - else - { - item.Select(); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarPom.cs deleted file mode 100644 index 0b8b47a2..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/NavBarPom.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Linq; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the navigation menu. - /// - public class NavBarPom : UIObject - { - private const string StandardId = "Standard"; - private const string ScientificId = "Scientific"; - private const string ProgrammerId = "Programmer"; - private const string DateId = "Date"; - private const string CurrencyId = "Currency"; - private const string VolumeId = "Volume"; - private const string LengthId = "Length"; - private const string WeightId = "Weight"; - private const string TemperatureId = "Temperature"; - private const string EnergyId = "Energy"; - private const string AreaId = "Area"; - private const string SpeedId = "Speed"; - private const string TimeId = "Time"; - private const string PowerId = "Power"; - private const string DataId = "Data"; - private const string PressureId = "Pressure"; - private const string AngleId = "Angle"; - private const string AboutId = "AboutButton"; - private const string CloseId = "TogglePaneButton"; - private const string FlyoutListViewId = "MenuItemsHost"; - private const string ConverterSectionId = "Converter"; - private const string ConverterTextKey = "ConverterModeText"; - - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the navigation menu. - public NavBarPom(UIObject uiObject) : base(uiObject) - { - } - - public ListViewItem StandardMenuItem => ScrollAndGetItem(StandardId); - - public ListViewItem ScientificMenuItem => ScrollAndGetItem(ScientificId); - - public ListViewItem ProgrammerMenuItem => ScrollAndGetItem(ProgrammerId); - - public ListViewItem DateMenuItem => ScrollAndGetItem(DateId); - - public ListViewItem CurrencyMenuItem => ScrollAndGetItem(CurrencyId); - - public ListViewItem VolumeMenuItem => ScrollAndGetItem(VolumeId); - - public ListViewItem LengthMenuItem => ScrollAndGetItem(LengthId); - - public ListViewItem WeightMenuItem => ScrollAndGetItem(WeightId); - - public ListViewItem TemperatureMenuItem => ScrollAndGetItem(TemperatureId); - - public ListViewItem EnergyMenuItem => ScrollAndGetItem(EnergyId); - - public ListViewItem AreaMenuItem => ScrollAndGetItem(AreaId); - - public ListViewItem SpeedMenuItem => ScrollAndGetItem(SpeedId); - - public ListViewItem TimeMenuItem => ScrollAndGetItem(TimeId); - - public ListViewItem PowerMenuItem => ScrollAndGetItem(PowerId); - - public ListViewItem DataMenuItem => ScrollAndGetItem(DataId); - - public ListViewItem PressureMenuItem => ScrollAndGetItem(PressureId); - - public ListViewItem AngleMenuItem => ScrollAndGetItem(AngleId); - - public Button AboutButton => new Button(this.Descendants.Find(AboutId)); - - public Button CloseButton => new Button(this.Parent.Children.Find(CloseId)); - - public ListView ModeListView => new ListView(this.Descendants.Find(FlyoutListViewId)); - - private ListViewItem ScrollAndGetItem(string id) - { - ListViewItem item; - var res = this.ModeListView.AllItems.TryFind(id, out item); - - item.ScrollIntoView(); - - return item; - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorLfm.cs deleted file mode 100644 index 916ec0bc..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorLfm.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class ProgrammerCalculatorLfm - { - public ProgrammerCalculatorLfm(ProgrammerCalculatorPom programmerCalculatorPom) - { - this.ObjectModel = programmerCalculatorPom; - } - - public ProgrammerCalculatorPom ObjectModel { get; } - - public void EnsureFullKeypad() - { - if (!this.ObjectModel.FullKeypadButton.IsSelected) - { - this.ObjectModel.FullKeypadButton.Select(); - } - } - - public void EnsureBitTogglingKeypad() - { - if (!this.ObjectModel.BitFlipKeypadButton.IsSelected) - { - this.ObjectModel.BitFlipKeypadButton.Select(); - } - } - - public void ChangeBitLength() - { - this.ObjectModel.GetCurrentBitLengthButton().Invoke(); - } - - public MemoryLfm OpenMemory() - { - MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls); - lfm.OpenBody(); - return lfm; - } - - public void FiveMemorySet() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - this.ObjectModel.NumberPad.FiveButton.Invoke(); - waiter.TryWait(); - } - - this.ObjectModel.MemoryControls.SetButton.Invoke(); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorPom.cs deleted file mode 100644 index 90a07867..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/ProgrammerCalculatorPom.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class ProgrammerCalculatorPom : UIObject - { - private const string FullKeypadButtonId = "fullKeypad"; - private const string BitFlipKeypadButtonId = "bitFlip"; - private const string CalculatorResultsId = "CalculatorResults"; - private const string NumberPadId = "NumberPad"; - - private readonly string[] BitLengthButtonIds = - { - "qwordButton", - "dwordButton", - "wordButton", - "byteButton" - }; - - public ProgrammerCalculatorPom(UIObject uiObject) : base(uiObject) - { - } - - public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId)); - - public MemoryPom MemoryControls => new MemoryPom(this); - - public RadioButton FullKeypadButton => new RadioButton(this.Descendants.Find(FullKeypadButtonId)); - - public RadioButton BitFlipKeypadButton => new RadioButton(this.Descendants.Find(BitFlipKeypadButtonId)); - - public TextBlock Display => new TextBlock(this.Descendants.Find(CalculatorResultsId)); - - public UIEventWaiter GetDisplayChangedWaiter() => this.Display.GetNameChangedWaiter(); - - public Button GetCurrentBitLengthButton() - { - // There are four bit length buttons, with only one visible at a time. - UIObject button = null; - foreach (var buttonId in this.BitLengthButtonIds) - { - if (this.Descendants.TryFind(buttonId, out button)) - { - break; - } - } - - return new Button(button); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorLfm.cs deleted file mode 100644 index 0320ee3c..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorLfm.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation.Waiters; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the Standard calculator view. - /// - public class ScientificCalculatorLfm - { - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the scientific Calculator. - public ScientificCalculatorLfm(ScientificCalculatorPom objectModel) - { - this.ObjectModel = objectModel; - } - - public ScientificCalculatorPom ObjectModel { get; } - - public void Press1() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking 1"); - this.ObjectModel.OneButton.Invoke(); - waiter.TryWait(); - } - } - - public void Press2() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking 2"); - this.ObjectModel.NumberPad.TwoButton.Invoke(); - waiter.TryWait(); - } - } - - public void Press3() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking 3"); - this.ObjectModel.ThreeButton.Invoke(); - waiter.TryWait(); - } - } - - public void Press4() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking 4"); - this.ObjectModel.FourButton.Invoke(); - waiter.TryWait(); - } - } - - public void PressSqrt() - { - // When invoking sqrt, both the expression changes. - using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter()) - { - Log.Comment("Invoking sqrt"); - this.ObjectModel.SqrtButton.Invoke(); - waiter.TryWait(); - } - } - - public void PressMinus() - { - using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter()) - { - Log.Comment("Invoking minus"); - this.ObjectModel.MinusButton.Invoke(); - waiter.TryWait(); - } - } - - public void PressPlus() - { - using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter()) - { - Log.Comment("Invoking plus"); - this.ObjectModel.PlusButton.Invoke(); - waiter.TryWait(); - } - } - - public void PressEquals() - { - // When invoking equals, both the display and the expression change. - using (UIEventWaiter expressionWaiter = this.ObjectModel.GetExpressionChangedWaiter()) - using (UIEventWaiter displayWaiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking equals"); - this.ObjectModel.EqualButton.Invoke(); - expressionWaiter.TryWait(); - displayWaiter.TryWait(); - } - } - - public void OnePlusTwoEnter() - { - Press1(); - PressPlus(); - Press2(); - PressEquals(); - } - - public void MemorySet() => this.ObjectModel.MemoryControls.SetButton.Invoke(); - - public MemoryLfm OpenMemory() - { - MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls); - lfm.OpenBody(); - return lfm; - } - - public HistoryLfm OpenHistory() - { - HistoryLfm lfm = new HistoryLfm(this.ObjectModel.HistoryControls); - lfm.OpenBody(); - return lfm; - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorPom.cs deleted file mode 100644 index bb729613..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/ScientificCalculatorPom.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the Scientific Calculator - /// - public class ScientificCalculatorPom : CalculatorBasePom - { - private const string NumberPadId = "NumberPad"; - private const string StandardOperatorsId = "StandardOperators"; - private const string DisplayControlsId = "DisplayControls"; - private const string ScientificFunctionsId = "ScientificFunctions"; - private const string OneButtonId = "num1Button"; - private const string ThreeButtonId = "num3Button"; - private const string FourButtonId = "num4Button"; - private const string SqrtButtonId = "squareRootButton"; - private const string MinusButtonId = "minusButton"; - private const string PlusButtonId = "plusButton"; - private const string EqualButtonId = "equalButton"; - private const string ClearButtonId = "clearButton"; - - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the scientific calculator. - public ScientificCalculatorPom(UIObject uiObject) : base(uiObject) - { - } - - public UIObject StandardOperatorsGroup => this.Descendants.Find(StandardOperatorsId); - - public UIObject DisplayControlsGroup => this.Descendants.Find(DisplayControlsId); - - public UIObject ScientificFunctionsGroup => this.Descendants.Find(ScientificFunctionsId); - - public Button OneButton => this.NumberPad.OneButton; - - public Button ThreeButton => this.NumberPad.ThreeButton; - - public Button FourButton => this.NumberPad.FourButton; - - public Button SqrtButton => new Button(this.ScientificFunctionsGroup.Children.Find(SqrtButtonId)); - - public Button MinusButton => new Button(this.StandardOperatorsGroup.Children.Find(MinusButtonId)); - - public Button PlusButton => new Button(this.StandardOperatorsGroup.Children.Find(PlusButtonId)); - - public Button EqualButton => new Button(this.StandardOperatorsGroup.Children.Find(EqualButtonId)); - - public Button ClearButton => new Button(this.DisplayControlsGroup.Children.Find(ClearButtonId)); - - public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId)); - - public HistoryPom HistoryControls => new HistoryPom(this); - - public MemoryPom MemoryControls => new MemoryPom(this); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorLfm.cs deleted file mode 100644 index 503ece53..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorLfm.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation.Waiters; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the Standard calculator view. - /// - public class StandardCalculatorLfm - { - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the Standard Calculator. - public StandardCalculatorLfm(StandardCalculatorPom objectModel) - { - this.ObjectModel = objectModel; - } - - public StandardCalculatorPom ObjectModel { get; } - - public void OnePlusTwoEnter() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking 1"); - this.ObjectModel.NumPad.OneButton.Invoke(); - waiter.TryWait(); - } - using (UIEventWaiter waiter = this.ObjectModel.GetExpressionChangedWaiter()) - { - Log.Comment("Pressing +"); - this.ObjectModel.SendKeys("{ADD}"); - // PropertyChangeWaiter is unreliable for the first name changed notification - // Bug 17624996: PropertyChanged event not fired when Name is updated for the first time for a control with custom automation peer. - waiter.TryWait(); - } - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Pressing 2"); - this.ObjectModel.SendKeys("2"); - waiter.TryWait(); - } - // When pressing enter, both the display and the expression change. - using (UIEventWaiter expressionWaiter = this.ObjectModel.GetExpressionChangedWaiter()) - using (UIEventWaiter displayWaiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Invoking equals"); - this.ObjectModel.EqualButton.Invoke(); - expressionWaiter.TryWait(); - displayWaiter.TryWait(); - } - } - - public void Clear() - { - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - Log.Comment("Pressing escape"); - this.ObjectModel.ClearButton.Invoke(); - waiter.TryWait(); - } - } - - public void ClearFiveMemorySet() - { - this.Clear(); - using (UIEventWaiter waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - this.ObjectModel.NumPad.FiveButton.Invoke(); - waiter.TryWait(); - } - - this.ObjectModel.MemoryControls.SetButton.Invoke(); - } - - public MemoryLfm OpenMemory() - { - MemoryLfm lfm = new MemoryLfm(this.ObjectModel.MemoryControls); - lfm.OpenBody(); - return lfm; - } - - public HistoryLfm OpenHistory() - { - HistoryLfm lfm = new HistoryLfm(this.ObjectModel.HistoryControls); - lfm.OpenBody(); - return lfm; - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorPom.cs deleted file mode 100644 index 8e8d5c41..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/StandardCalculatorPom.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - /// - /// Represents the Standard Calculator - /// - public class StandardCalculatorPom : CalculatorBasePom - { - private const string CalculatorResultsId = "CalculatorResults"; - private const string ExpressionContainerId = "CalculatorExpression"; - private const string NumberPadId = "NumberPad"; - private const string StandardOperatorsId = "StandardOperators"; - private const string DisplayControlsId = "DisplayControls"; - private const string EqualButtonId = "equalButton"; - private const string ClearButtonId = "clearButton"; - - /// - /// Initializes a new instance of the class. - /// - /// The UIObject that is the root of the standard calculator. - public StandardCalculatorPom(UIObject uiObject) : base(uiObject) - { - } - - public NumberPadPom NumPad => new NumberPadPom(this.Descendants.Find(NumberPadId)); - - public MemoryPom MemoryControls => new MemoryPom(this); - - public HistoryPom HistoryControls => new HistoryPom(this); - - public UIObject StandardOperatorsGroup => this.Descendants.Find(StandardOperatorsId); - - public UIObject DisplayControlsGroup => this.Descendants.Find(DisplayControlsId); - - public Button EqualButton => new Button(this.StandardOperatorsGroup.Children.Find(EqualButtonId)); - - public Button ClearButton => new Button(this.DisplayControlsGroup.Children.Find(ClearButtonId)); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterLfm.cs deleted file mode 100644 index 9a17c743..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterLfm.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Calculator.UIAutomationLibrary.Components -{ - public class UnitConverterLfm - { - public UnitConverterLfm(UnitConverterPom unitConverterPom) - { - this.ObjectModel = unitConverterPom; - } - - public UnitConverterPom ObjectModel { get; } - - public void Eight() - { - using (var waiter = this.ObjectModel.GetDisplayChangedWaiter()) - { - this.ObjectModel.NumberPad.EightButton.Invoke(); - waiter.TryWait(); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterPom.cs deleted file mode 100644 index 23e6a80d..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Pages/UnitConverterPom.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class UnitConverterPom : UIObject - { - private const string DisplayId = "Value1"; - private const string NumberPadId = "numberPad"; - - public UnitConverterPom(UIObject uiObject) : base(uiObject) - { - } - - public NumberPadPom NumberPad => new NumberPadPom(this.Descendants.Find(NumberPadId)); - - public TextBlock Display => new TextBlock(this.Descendants.Find(DisplayId)); - - public PropertyChangedEventWaiter GetDisplayChangedWaiter() => this.Display.GetNameChangedWaiter(); - - public ElementAddedWaiter GetDisplayElementAddedWaiter() => new ElementAddedWaiter(this, Scope.Descendants, DisplayId); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryLfm.cs deleted file mode 100644 index a186a351..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryLfm.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Etw.Managed; -using MS.Internal.Mita.Foundation; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class HistoryLfm : ICanFocusWithClicks - { - public HistoryLfm(HistoryPom historyPom) - { - this.ObjectModel = historyPom; - } - - public HistoryPom ObjectModel { get; } - - public void FocusWithClicks() - { - // On the Programming calc, the default click location can land on the first memory item, dismissing the flyout. - // Instead, click just below, in the gutter to the left of the trash can. - var body = this.ObjectModel.Body; - int height = body.BoundingRectangle.Height; - body.DoubleClick(PointerButtons.Primary, Constants.ClickMargin, height + Constants.ClickMargin); - } - - public void OpenBody() - { - using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.HistoryBodyOpenedETWEventName)) - { - // Only one exists at a given time. - if (this.ObjectModel.IsHistoryButtonVisible) - { - if (!this.ObjectModel.IsBodyOpen) - { - this.ObjectModel.HistoryButton.Invoke(); - waiter.Wait(); - } - } - else if (!this.ObjectModel.HistoryPivot.IsSelected) - { - this.ObjectModel.HistoryPivot.Click(); - waiter.Wait(); - } - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryPom.cs deleted file mode 100644 index d5d8d809..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/HistoryPom.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class HistoryPom : UIObject - { - private const string HistoryButtonId = "HistoryButton"; - private const string HistoryPivotId = "HistoryLabel"; - private const string BodyId = "HistoryListView"; - - public HistoryPom(UIObject uiObject) : base(uiObject) - { - } - - public Button HistoryButton => new Button(this.Descendants.Find(HistoryButtonId)); - - public bool IsHistoryButtonVisible => this.DoesDescendantExist(HistoryButtonId); - - public TabItem HistoryPivot => new TabItem(this.Descendants.Find(HistoryPivotId)); - - public UIObject Body => this.Descendants.Find(BodyId); - - public bool IsBodyOpen => this.DoesDescendantExist(BodyId); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/ICanFocusWithClicks.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/ICanFocusWithClicks.cs deleted file mode 100644 index bd27cc16..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/ICanFocusWithClicks.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Calculator.UIAutomationLibrary.Components -{ - public interface ICanFocusWithClicks - { - /// - /// Sets focus on an object by clicking on it, without causing further action. - /// Supports AccSpot scans by raising click events. - /// - void FocusWithClicks(); - } -} - diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryLfm.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryLfm.cs deleted file mode 100644 index 216acfbc..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryLfm.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Etw.Managed; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Waiters; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class MemoryLfm : ICanFocusWithClicks - { - public MemoryLfm(MemoryPom memoryPom) - { - this.ObjectModel = memoryPom; - } - - public MemoryPom ObjectModel { get; } - - public void FocusWithClicks() - { - // On the Programming calc, the default click location can land on the first memory item, dismissing the flyout. - // Instead, click just below, in the gutter to the left of the trash can. - var body = this.ObjectModel.Body; - int height = body.BoundingRectangle.Height; - body.DoubleClick(PointerButtons.Primary, Constants.ClickMargin, height + Constants.ClickMargin); - } - - public void OpenBody() - { - using (EtwWaiter waiter = new EtwWaiter(Constants.CalculatorETWProviderGUID, Constants.MemoryBodyOpenedETWEventName)) - { - // Only one exists at a given time - if (this.ObjectModel.IsMemoryButtonVisible) - { - if (!this.ObjectModel.IsBodyOpen) - { - this.ObjectModel.MemoryButton.Invoke(); - waiter.Wait(); - } - } - else if (!this.ObjectModel.MemoryPivot.IsSelected) - { - this.ObjectModel.MemoryPivot.Click(); - waiter.Wait(); - } - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryPom.cs deleted file mode 100644 index a235258d..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/MemoryPom.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class MemoryPom : UIObject - { - private const string ClearMemoryButtonId = "ClearMemoryButton"; - private const string RecallButtonId = "MemRecall"; - private const string PlusButtonId = "MemPlus"; - private const string MinusButtonId = "MemMinus"; - private const string SetButtonId = "memButton"; - private const string MemoryButtonId = "MemoryButton"; - private const string MemoryPivotId = "MemoryLabel"; - private const string BodyId = "MemoryListView"; - - public MemoryPom(UIObject uiObject) : base(uiObject) - { - } - - public Button ClearButton => new Button(this.Descendants.Find(ClearMemoryButtonId)); - - public Button RecallButton => new Button(this.Descendants.Find(RecallButtonId)); - - public Button PlusButton => new Button(this.Descendants.Find(PlusButtonId)); - - public Button MinusButton => new Button(this.Descendants.Find(MinusButtonId)); - - public Button SetButton => new Button(this.Descendants.Find(SetButtonId)); - - public Button MemoryButton => new Button(this.Descendants.Find(MemoryButtonId)); - - public bool IsMemoryButtonVisible => this.DoesDescendantExist(MemoryButtonId); - - public TabItem MemoryPivot => new TabItem(this.Descendants.Find(MemoryPivotId)); - - public UIObject Body => this.Descendants.Find(BodyId); - - public bool IsBodyOpen => this.DoesDescendantExist(BodyId); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Components/Shared/NumberPadPom.cs b/internal/Calculator.UIAutomationLibrary/Components/Shared/NumberPadPom.cs deleted file mode 100644 index 6da757f5..00000000 --- a/internal/Calculator.UIAutomationLibrary/Components/Shared/NumberPadPom.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; - -namespace Calculator.UIAutomationLibrary.Components -{ - public class NumberPadPom : UIObject - { - private const string OneButtonId = "num1Button"; - private const string TwoButtonId = "num2Button"; - private const string ThreeButtonId = "num3Button"; - private const string FourButtonId = "num4Button"; - private const string FiveButtonId = "num5Button"; - private const string SixButtonId = "num6Button"; - private const string SevenButtonId = "num7Button"; - private const string EightButtonId = "num8Button"; - private const string NineButtonId = "num9Button"; - private const string ZeroButtonId = "num0Button"; - private const string DecimalButtonId = "decimalSeparatorButton"; - - public NumberPadPom(UIObject uiObject) : base(uiObject) - { - } - - public Button ZeroButton => new Button(this.Children.Find(ZeroButtonId)); - - public Button OneButton => new Button(this.Children.Find(OneButtonId)); - - public Button TwoButton => new Button(this.Children.Find(TwoButtonId)); - - public Button ThreeButton => new Button(this.Children.Find(ThreeButtonId)); - - public Button FourButton => new Button(this.Children.Find(FourButtonId)); - - public Button FiveButton => new Button(this.Children.Find(FiveButtonId)); - - public Button SixButton => new Button(this.Children.Find(SixButtonId)); - - public Button SevenButton => new Button(this.Children.Find(SevenButtonId)); - - public Button EightButton => new Button(this.Children.Find(EightButtonId)); - - public Button NineButton => new Button(this.Children.Find(NineButtonId)); - - public Button DecimalButton => new Button(this.Children.Find(DecimalButtonId)); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Properties/AssemblyInfo.cs b/internal/Calculator.UIAutomationLibrary/Properties/AssemblyInfo.cs deleted file mode 100644 index 89cda6fe..00000000 --- a/internal/Calculator.UIAutomationLibrary/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Calculator.UIAutomationLibrary")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("D6913DAD-1C3B-4229-915F-8301A57970FC")] - diff --git a/internal/Calculator.UIAutomationLibrary/Tests/BasicCalculationTest.cs b/internal/Calculator.UIAutomationLibrary/Tests/BasicCalculationTest.cs deleted file mode 100644 index 737584eb..00000000 --- a/internal/Calculator.UIAutomationLibrary/Tests/BasicCalculationTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Etw.Managed; -using Calculator.UIAutomationLibrary.Components; -using WEX.TestExecution; - -namespace Calculator.UIAutomationLibrary.Tests -{ - public static class BasicCalculationTest - { - /// - /// This test uses LFMs to add and then delete an alarm. - /// - /// The LFM for the alarms app window. - public static void CalculateOnePlusTwo(this CalculatorAppLfm calculatorAppLfm) - { - var mainPage = calculatorAppLfm.MainPageLfm; - - var standardCalculator = mainPage.NavigateToStandardCalculator(); - - standardCalculator.OnePlusTwoEnter(); - - Verify.AreEqual("\u202D3\u202C", standardCalculator.ObjectModel.Display.Name, "Ensure display value is 3"); - - standardCalculator.Clear(); - - Verify.AreEqual("\u202D0\u202C", standardCalculator.ObjectModel.Display.Name, "Ensure display value is 0"); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Tests/ScientificCalculationTest.cs b/internal/Calculator.UIAutomationLibrary/Tests/ScientificCalculationTest.cs deleted file mode 100644 index 7a2b8e7c..00000000 --- a/internal/Calculator.UIAutomationLibrary/Tests/ScientificCalculationTest.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Calculator.UIAutomationLibrary.Components; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using WEX.TestExecution; - -namespace Calculator.UIAutomationLibrary.Tests -{ - public static class ScientificCalculationTest - { - /// - /// This test uses LFMs to calculate the sqrt(4) - 2. - /// - /// The LFM for the calculator app window. - public static void CalculateSqrt4Minus2(this CalculatorAppLfm calculatorAppLfm) - { - var mainPage = calculatorAppLfm.MainPageLfm; - - var scientificCalculator = mainPage.NavigateToScientificCalculator(); - - scientificCalculator.Press4(); - scientificCalculator.PressSqrt(); - scientificCalculator.PressMinus(); - scientificCalculator.Press3(); - scientificCalculator.PressPlus(); - scientificCalculator.Press1(); - scientificCalculator.PressEquals(); - - Verify.AreEqual("\u202D0\u202C", scientificCalculator.ObjectModel.Display.Name); - } - } -} - diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/Constants.cs b/internal/Calculator.UIAutomationLibrary/Utilities/Constants.cs deleted file mode 100644 index 4a8957d8..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/Constants.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Calculator.UIAutomationLibrary -{ - public class Constants - { - /// - /// The path to the certificate file. - /// - public const string CertificateFileName = @"Calculator.cer"; - - /// - /// The path to the appxbundle file. - /// - public const string PackageFileName = @"Calculator.appxbundle"; - - /// - /// The path to the appxbundle file. - /// - public const string VCLibsPackageFileName = @"Microsoft.VCLibs.appx"; - - /// - /// The path to the appxbundle file. - /// - public const string WinUIPackageFileName = @"Microsoft.UI.Xaml.appx"; - - /// - /// Name of the CoreWindow. - /// - public const string AppWindowName = "Calculator"; - - /// - /// Name of the process executable. - /// - public const string ProcessName = "Calculator.exe"; - - /// - /// The package name. - /// - public const string PackageName = "Microsoft.WindowsCalculator"; - - /// - /// The package family name for the app to test. - /// - public const string PackageFamilyName = PackageName + "_8wekyb3d8bbwe"; - - /// - /// The package App User Model Id. - /// - public const string PackageAppUserModelId = PackageFamilyName + "!App"; - - /// - /// AutomationId for the top level UI element. - /// - public const string TopLevelWindowAutomationId = "CalculatorWindow"; - - /// - /// Event fired when the first page is loaded. - /// - public const string AppLaunchEndETWEventName = "AppLaunchEnd"; - - /// - /// App Provider GUID for ETW Events - /// - public static readonly Guid CalculatorETWProviderGUID = new Guid("0905CA09-610E-401E-B650-2F212980B9E0"); - - /// - /// Event fired when a calculator mode change is complete. - /// - public const string AppModeChangeEndETWEventName = "ModeChangeEnd"; - - /// - /// Event fired when the History panel is opened by flyout or by changing pivot tabs. - /// - public const string HistoryBodyOpenedETWEventName = "HistoryBodyOpened"; - - /// - /// Event fired when the Memory panel is opened by flyout or by changing pivot tabs. - /// - public const string MemoryBodyOpenedETWEventName = "MemoryBodyOpened"; - - /// - /// Event fired when the About flyout control is loaded. - /// - public const string AboutFlyoutOpenedETWEventName = "AboutFlyoutOpened"; - - /// - /// Event fired when the Nav Bar control is opened. - /// - public const string NavBarOpenedETWEventName = "NavBarOpened"; - - /// - /// Margin used to click in the gutter beneath the History and Memory lists - /// - public const int ClickMargin = 10; - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/EtwHelper.cs b/internal/Calculator.UIAutomationLibrary/Utilities/EtwHelper.cs deleted file mode 100644 index d1615eed..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/EtwHelper.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.OneCoreUap.Test.AppModel; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary -{ - public class EtwHelper - { - private static bool etwServiceWasInstalled = false; - - /// - /// Installs and starts the Etw.Service so that Tests may utilize Etw Waiters. - /// Wex.Services.exe is part of TAEF. - /// - public static void InstallAndStartETWService() - { - etwServiceWasInstalled = ServiceHelper.IsInstalled("Etw.Service"); - if (!etwServiceWasInstalled) - { - string wexServices = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), "Wex.Services.exe"); - if (!File.Exists(wexServices)) - { - throw new FileNotFoundException(wexServices); - } - - Log.Comment("Attempting to install Etw.Service..."); - var startInfo = new ProcessStartInfo(); - startInfo.FileName = wexServices; - startInfo.Arguments = "/install:Etw.Service"; - - var process = new Process(); - process.StartInfo = startInfo; - if (process.Start()) - { - process.WaitForExit(); - Log.Comment("Completed installation of Etw.Service"); - } - else - { - throw new Exception("ETW service was not able to be installed. Process didn't start."); - } - } - - Log.Comment("Attempting to start Etw.Service..."); - ServiceHelper.Start("Etw.Service"); - Log.Comment("Etw.Service started"); - } - - /// - /// Stops the Etw.Service. - /// - public static void StopAndRemoveETWService() - { - if (ServiceHelper.IsInstalled("Etw.Service")) - { - Log.Comment("Attempting to stop Etw.Service..."); - ServiceHelper.Stop("Etw.Service"); - Log.Comment("Etw.Service stopped"); - - // if we installed the Etw.Service as part of this test we should also remove it. - // This prevents cases where TDP is upgraded but the service tregistration is referencing the old - // location that no longer exists. - if (!etwServiceWasInstalled) - { - - string wexServices = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), "Wex.Services.exe"); - if (!File.Exists(wexServices)) - { - throw new FileNotFoundException(wexServices); - } - - Log.Comment("Attempting to remove Etw.Service..."); - var startInfo = new ProcessStartInfo(); - startInfo.FileName = wexServices; - startInfo.Arguments = "/remove:Etw.Service"; - - var process = new Process(); - process.StartInfo = startInfo; - if (process.Start()) - { - process.WaitForExit(); - Log.Comment("Completed removal of Etw.Service"); - } - else - { - throw new Exception("ETW service could not be removed. Process didn't start."); - } - } - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/Impersonator.cs b/internal/Calculator.UIAutomationLibrary/Utilities/Impersonator.cs deleted file mode 100644 index 1b31b385..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/Impersonator.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Win32.SafeHandles; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Principal; -using System.Text; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary -{ - public static class Impersonater - { - public static void RunAs(RunAsUser user, Action action) - { - IntPtr errorInfo; - SafeAccessTokenHandle restrictedToken; - GetRunAsUserToken getRunAsUserToken = ResolveGetRunAsUserTokenMethod(); - Marshal.ThrowExceptionForHR(getRunAsUserToken(user, out restrictedToken, out errorInfo), errorInfo); - - WindowsIdentity.RunImpersonated(restrictedToken, action); - } - - public static void RunAs(RunAsUser user, Func function) - { - IntPtr errorInfo; - SafeAccessTokenHandle restrictedToken; - GetRunAsUserToken getRunAsUserToken = ResolveGetRunAsUserTokenMethod(); - Marshal.ThrowExceptionForHR(getRunAsUserToken(user, out restrictedToken, out errorInfo), errorInfo); - - WindowsIdentity.RunImpersonated(restrictedToken, function); - } - - // From: https://microsoft.visualstudio.com/EngSys/_git/validation.taef?path=%2Fsrc%2FTAEF%2FCommon%2FPublished%2FRunAsFromSystem.h&version=GBdevelop - public enum RunAsUser - { - ElevatedUser, - User, - RestrictedUser, - LowIL, - InteractiveUser, - }; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int GetRunAsUserToken(RunAsUser user, out SafeAccessTokenHandle token, out IntPtr errorInfo); - - // GetRunAsUserToken is defined in a namespace so we have to do some tricks to use P/Invoke. - // We got the actual exported method name by running dumpbin /exports TE.Common.dll - private static GetRunAsUserToken ResolveGetRunAsUserTokenMethod() - { - IntPtr teCommonDll = IntPtr.Zero; - try - { - teCommonDll = LoadLibrary(@"TE.Common.dll"); - - IntPtr x64GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PEAPEAXPEAPEAUIErrorInfo@@@Z"); - if (x64GetRunAsUserToken != IntPtr.Zero) - { - return Marshal.GetDelegateForFunctionPointer(x64GetRunAsUserToken); - } - IntPtr x86GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YGJW4RunAsUser@12@PAPAXPAPAUIErrorInfo@@@Z"); - if (x86GetRunAsUserToken != IntPtr.Zero) - { - return Marshal.GetDelegateForFunctionPointer(x86GetRunAsUserToken); - } - IntPtr armGetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PAPAXPAPAUIErrorInfo@@@Z"); - if (armGetRunAsUserToken != IntPtr.Zero) - { - return Marshal.GetDelegateForFunctionPointer(armGetRunAsUserToken); - } - IntPtr arm64GetRunAsUserToken = GetProcAddress(teCommonDll, "?GetRunAsUserToken@TestExecution@WEX@@YAJW4RunAsUser@12@PEAPEAXPEAPEAUIErrorInfo@@@Z"); - if (arm64GetRunAsUserToken != IntPtr.Zero) - { - return Marshal.GetDelegateForFunctionPointer(arm64GetRunAsUserToken); - } - - throw new Exception("Unable to find the GetRunAsUserToken function in DLL 'TE.Common.dll'"); - } - finally - { - if (teCommonDll != IntPtr.Zero) - { - FreeLibrary(teCommonDll); - } - } - } - - [DllImport("kernel32.dll")] - public static extern IntPtr LoadLibrary(string dllToLoad); - - [DllImport("kernel32.dll")] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); - - [DllImport("kernel32.dll")] - public static extern bool FreeLibrary(IntPtr hModule); - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/InstallHelper.cs b/internal/Calculator.UIAutomationLibrary/Utilities/InstallHelper.cs deleted file mode 100644 index d9760be5..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/InstallHelper.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using Microsoft.OneCoreUap.Test.AppModel; -using WEX.Logging.Interop; -using Windows.Foundation; -using Windows.Management.Deployment; - -namespace Calculator.UIAutomationLibrary -{ - public class InstallHelper - { - public static void InstallCertFile(string certFilePath) - { - // Ensure cert exists. - if (!File.Exists(certFilePath)) - { - throw new FileNotFoundException(certFilePath); - } - - // For some reason, attempting to use CertificateHelper.InstallFromSignedPackage() to install - // the certificate associated with the appx package fails with the error: - // "A certificate chain could not be built to a trusted root authority." - // The reason is that the cert needs to be installed in 'StoreName.TrustedPeople', - // but DeploymentHelper.AddPackage() attempts to install it in 'StoreName.Root'. - // Therefore, the cert has been installed manually beforehand. - Log.Comment($"Starting install of certificate at {certFilePath}"); - - X509Certificate2 certificate = new X509Certificate2(certFilePath); - X509Store trustedPeopleStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine); - trustedPeopleStore.Open(OpenFlags.MaxAllowed); - trustedPeopleStore.Add(certificate); - - Log.Comment($"Completed install of certificate"); - } - - /// - /// Upgrades the appx/appxbundle from the path if needed. - /// - public static void InstallPackage(string appxFilePath, params string[] dependencyAppxFilePaths) - { - // Ensure the files exist. - if (!File.Exists(appxFilePath)) - { - throw new FileNotFoundException(appxFilePath); - } - foreach (var path in dependencyAppxFilePaths.Where(p => !File.Exists(p))) - { - throw new FileNotFoundException(path); - } - - Log.Comment($"Starting install of app package at {appxFilePath}"); - - PackageManager packageManager = new PackageManager(); - var deploymentOperation = packageManager.AddPackageAsync( - new Uri(appxFilePath), - dependencyAppxFilePaths.Select(d => new Uri(d)), - DeploymentOptions.ForceApplicationShutdown | DeploymentOptions.ForceTargetApplicationShutdown | DeploymentOptions.ForceUpdateFromAnyVersion); - - WaitForDeploymentOperation(deploymentOperation); - - Log.Comment("Completed install of app package"); - } - - /// - /// Waits for a deployment operation to complete - /// - private static void WaitForDeploymentOperation(IAsyncOperationWithProgress operation) - { - ManualResetEvent isCompletedEvent = new ManualResetEvent(false); - operation.Completed = (IAsyncOperationWithProgress asyncInfo, AsyncStatus asyncStatus) => - { - isCompletedEvent.Set(); - }; - - isCompletedEvent.WaitOne(); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/NativeMethods.cs b/internal/Calculator.UIAutomationLibrary/Utilities/NativeMethods.cs deleted file mode 100644 index 39d50bbe..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/NativeMethods.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Calculator.UIAutomationLibrary -{ - internal static class NativeMethods - { - internal const int GW_OWNER = 4; - - internal delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern int GetWindowTextLength(IntPtr hWnd); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern bool IsWindowVisible(IntPtr hWnd); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); - - [DllImport("api-ms-win-ntuser-ie-window-l1-1-0.dll", SetLastError = true)] - internal static extern bool SetForegroundWindow(IntPtr hWnd); - - [DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool StartService(IntPtr hService, int dwNumServiceArgs, string[] lpServiceArgVectors); - - [DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool ControlService(IntPtr hService, SERVICE_CONTROL dwControl, ref SERVICE_STATUS lpServiceStatus); - - [DllImport("api-ms-win-service-management-l1-1-0.dll")] - internal static extern IntPtr OpenSCManager(string machineName, string databaseName, uint dwAccess); - - [DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool CloseServiceHandle(IntPtr hSCObject); - - [DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess); - - [DllImport("api-ms-win-service-management-l1-1-0.dll", SetLastError = true)] - internal static extern bool QueryServiceStatus(IntPtr hService, ref SERVICE_STATUS dwServiceStatus); - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - internal struct SERVICE_STATUS - { - public int dwServiceType; - public int dwCurrentState; - public int dwControlsAccepted; - public int dwWin32ExitCode; - public int dwServiceSpecificExitCode; - public int dwCheckPoint; - public int dwWaitHint; - } - - internal static uint STANDARD_RIGHTS_REQUIRED = 0xF0000; - - internal static uint SC_MANAGER_CONNECT = 0x0001; - internal static uint SC_MANAGER_CREATE_SERVICE = 0x0002; - internal static uint SC_MANAGER_ENUMERATE_SERVICE = 0x0004; - internal static uint SC_MANAGER_LOCK = 0x0008; - internal static uint SC_MANAGER_QUERY_LOCK_STATUS = 0x0010; - internal static uint SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020; - internal static uint SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | - SC_MANAGER_CONNECT | - SC_MANAGER_CREATE_SERVICE | - SC_MANAGER_ENUMERATE_SERVICE | - SC_MANAGER_LOCK | - SC_MANAGER_QUERY_LOCK_STATUS | - SC_MANAGER_MODIFY_BOOT_CONFIG); - - internal static uint SERVICE_QUERY_CONFIG = 0x0001; - internal static uint SERVICE_CHANGE_CONFIG = 0x0002; - internal static uint SERVICE_QUERY_STATUS = 0x0004; - internal static uint SERVICE_ENUMERATE_DEPENDENTS = 0x0008; - internal static uint SERVICE_START = 0x0010; - internal static uint SERVICE_STOP = 0x0020; - internal static uint SERVICE_PAUSE_CONTINUE = 0x0040; - internal static uint SERVICE_INTERROGATE = 0x0080; - internal static uint SERVICE_USER_DEFINED_CONTROL = 0x0100; - internal static uint SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | - SERVICE_QUERY_CONFIG | - SERVICE_CHANGE_CONFIG | - SERVICE_QUERY_STATUS | - SERVICE_ENUMERATE_DEPENDENTS | - SERVICE_START | - SERVICE_STOP | - SERVICE_PAUSE_CONTINUE | - SERVICE_INTERROGATE | - SERVICE_USER_DEFINED_CONTROL); - - [Flags] - internal enum SERVICE_CONTROL : uint - { - STOP = 0x00000001, - PAUSE = 0x00000002, - CONTINUE = 0x00000003, - INTERROGATE = 0x00000004, - SHUTDOWN = 0x00000005, - PARAMCHANGE = 0x00000006, - NETBINDADD = 0x00000007, - NETBINDREMOVE = 0x00000008, - NETBINDENABLE = 0x00000009, - NETBINDDISABLE = 0x0000000A, - DEVICEEVENT = 0x0000000B, - HARDWAREPROFILECHANGE = 0x0000000C, - POWEREVENT = 0x0000000D, - SESSIONCHANGE = 0x0000000E - } - - internal enum SERVICE_STATE : int - { - SERVICE_STOPPED = 0x00000001, - SERVICE_START_PENDING = 0x00000002, - SERVICE_STOP_PENDING = 0x00000003, - SERVICE_RUNNING = 0x00000004, - SERVICE_CONTINUE_PENDING = 0x00000005, - SERVICE_PAUSE_PENDING = 0x00000006, - SERVICE_PAUSED = 0x00000007 - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/PerfTestConstants.cs b/internal/Calculator.UIAutomationLibrary/Utilities/PerfTestConstants.cs deleted file mode 100644 index 02c0206d..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/PerfTestConstants.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Calculator.UIAutomationLibrary -{ - public class PerfConstants - { - /// - /// Path where the regions, wprprofiles and wpaprofiles will be deployed to by the spkg. - /// - public const string ConfigDirectory = @"Config\"; - - /// - /// Our FunGates source, where we can view test results. - /// - public const string FunGatesSource = -#if DEBUG - "TestSite"; -#else - "Utility Apps Performance Tests"; -#endif - - /// - /// The Windows Performance Recorder profile. These strings must have the config directory prefix. - /// For use with the WPRProfileFile test attribute. - /// - public const string AppLifecycleWPRProfile = ConfigDirectory + "AppLifecycle.Profile.wprp"; - - /// - /// The regions of interest file that contains the events we are interested in measuring. - /// - public const string AppLifecycleRegions = ConfigDirectory + "AppLifecycle.regions.xml"; - - /// - /// These are uses with the DataSource test property to specify iteration info. - /// - public const string AppLifecycleIterationsSource = "Table:" + ConfigDirectory + "AppLifecycle.Iterations.xml#PerformanceConfigurations"; - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/ServiceHelper.cs b/internal/Calculator.UIAutomationLibrary/Utilities/ServiceHelper.cs deleted file mode 100644 index 4cae7728..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/ServiceHelper.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Calculator.UIAutomationLibrary -{ - public class ServiceHelper - { - public static void Start(string serviceName, int timeoutInMilliSeconds = 30000) - { - IntPtr hService = IntPtr.Zero; - IntPtr hSCManager = IntPtr.Zero; - try - { - hSCManager = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS); - if (IntPtr.Zero == hSCManager) - { - throw new Exception($"Start: Cannot Open OpenSCManager, {Marshal.GetLastWin32Error()}"); - } - - hService = NativeMethods.OpenService(hSCManager, serviceName, NativeMethods.SERVICE_ALL_ACCESS); - if (IntPtr.Zero == hService) - { - throw new Exception($"Start: Cannot Open Service, {Marshal.GetLastWin32Error()}"); - } - - NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS(); - if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus)) - { - throw new Exception($"Start: Unable to query status of Service, {Marshal.GetLastWin32Error()}"); - } - - if (serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_RUNNING && - serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_START_PENDING) - { - if (!NativeMethods.StartService(hService, 0, null)) - { - throw new Exception($"Start: Service cannot be started, {Marshal.GetLastWin32Error()}"); - } - } - - WaitForStatus(hService, NativeMethods.SERVICE_STATE.SERVICE_RUNNING, TimeSpan.FromMilliseconds(timeoutInMilliSeconds)); - } - finally - { - if (IntPtr.Zero != hService) - { - NativeMethods.CloseServiceHandle(hService); - } - if (IntPtr.Zero != hSCManager) - { - NativeMethods.CloseServiceHandle(hSCManager); - } - } - } - - public static void Stop(string serviceName, int timeoutInMilliSeconds = 30000) - { - IntPtr hSCManager = IntPtr.Zero; - IntPtr hService = IntPtr.Zero; - try - { - hSCManager = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS); - if (IntPtr.Zero == hSCManager) - { - throw new Exception($"Stop: Cannot Open OpenSCManager, {Marshal.GetLastWin32Error()}"); - } - - hService = NativeMethods.OpenService(hSCManager, serviceName, NativeMethods.SERVICE_ALL_ACCESS); - if (IntPtr.Zero == hService) - { - throw new Exception($"Stop: Cannot Open Service, {Marshal.GetLastWin32Error()}"); - } - - NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS(); - if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus)) - { - throw new Exception($"Stop: Unable to query status of Service, {Marshal.GetLastWin32Error()}"); - } - - if (serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_STOPPED && - serviceStatus.dwCurrentState != (int)NativeMethods.SERVICE_STATE.SERVICE_STOP_PENDING) - { - if (!NativeMethods.ControlService(hService, NativeMethods.SERVICE_CONTROL.STOP, ref serviceStatus)) - { - throw new Exception($"Stop: Service cannot be stopped, {Marshal.GetLastWin32Error()}"); - } - } - - WaitForStatus(hService, NativeMethods.SERVICE_STATE.SERVICE_STOPPED, TimeSpan.FromMilliseconds(timeoutInMilliSeconds)); - } - finally - { - if (IntPtr.Zero != hService) - { - NativeMethods.CloseServiceHandle(hService); - } - - if (IntPtr.Zero != hSCManager) - { - NativeMethods.CloseServiceHandle(hSCManager); - } - } - } - - public static bool IsInstalled(string svcName) - { - IntPtr sc_handle = NativeMethods.OpenSCManager(null, null, NativeMethods.SC_MANAGER_ALL_ACCESS); - if (sc_handle == IntPtr.Zero) - { - throw new Exception($"IsInstalled: Cannot open service manager, {Marshal.GetLastWin32Error()}"); - } - - bool bResult = false; - IntPtr sv_handle = NativeMethods.OpenService(sc_handle, svcName, NativeMethods.SERVICE_QUERY_CONFIG); - if (sv_handle.ToInt64() != 0) - { - bResult = true; - NativeMethods.CloseServiceHandle(sv_handle); - } - NativeMethods.CloseServiceHandle(sc_handle); - return bResult; - } - - private static void WaitForStatus(IntPtr hService, NativeMethods.SERVICE_STATE desiredStatus, TimeSpan timeout) - { - Stopwatch swLoop = new Stopwatch(); - swLoop.Start(); - - NativeMethods.SERVICE_STATUS serviceStatus = new NativeMethods.SERVICE_STATUS(); - - do - { - Thread.Sleep(500); - if (!NativeMethods.QueryServiceStatus(hService, ref serviceStatus)) - { - throw new Exception($"WaitForStatus: Unable to query status of service, {Marshal.GetLastWin32Error()}"); - } - } - while (serviceStatus.dwCurrentState != (int)desiredStatus && (swLoop.ElapsedMilliseconds <= timeout.TotalMilliseconds)); - - if (serviceStatus.dwCurrentState != (int)desiredStatus) - { - throw new Exception($"WaitForStatus: Service failed to reach desired state: {desiredStatus}, current state: {serviceStatus.dwCurrentState}"); - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/UIObjectExtensions.cs b/internal/Calculator.UIAutomationLibrary/Utilities/UIObjectExtensions.cs deleted file mode 100644 index c77f4d64..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/UIObjectExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Linq; -using System.Windows.Automation; -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary -{ - public static class UIObjectExtensions - { - private const string NamePropertyName = "Name"; - private static UICondition CoreWindowCondition = UICondition.CreateFromClassName("Windows.UI.Core.CoreWindow"); - - public static Window GetParentCoreWindow(this UIObject uiObject) - { - if (uiObject.Matches(CoreWindowCondition)) - { - return new Window(uiObject); - } - - return new Window(uiObject.Ancestors.Find(CoreWindowCondition)); - } - - public static Window GetTopLevelWindow(this Window window) - { - var node = window; - while (node != null && node.Parent != null && node.Parent.ControlType == ControlType.Window) - { - node = new Window(node.Parent); - } - return node; - } - - public static void VerifyParentTreeStructure(this UIObject uiObject) - { - var node = uiObject; - while (node != null && node.Parent != null) - { - if (!node.Parent.Children.Contains(node)) - { - Log.Comment($"- [VerifyingTree] {node} specifies {node.Parent} as parent but is not part of its children."); - } - if (!node.Parent.Descendants.Contains(node)) - { - Log.Comment($"- [VerifyingTree] {node} specifies {node.Parent} as parent but is not part of its descendants."); - } - node = node.Parent; - } - } - - public static bool DoesDescendantExist(this UIObject uiObject, string automationId) - { - UIObject temp; - return uiObject.Descendants.TryFind(automationId, out temp); - } - - public static PropertyChangedEventWaiter GetNameChangedWaiter(this TextBlock textBlock) - { - return new PropertyChangedEventWaiter(textBlock, UIProperty.Get(NamePropertyName)); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/Utilities.cs b/internal/Calculator.UIAutomationLibrary/Utilities/Utilities.cs deleted file mode 100644 index c8dd58d3..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/Utilities.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Diagnostics; -using System.IO; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary -{ - public class Utilities - { - public static void KillExistingCalculatorProcesses() - { - Log.Comment("Killing any existing Calculator processes"); - - foreach (var process in Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Constants.ProcessName))) - { - try - { - process.Kill(); - Log.Comment($"Killed {process.ProcessName}, Id: {process.Id}"); - } - catch (Exception) when (process.HasExited) - { - Log.Comment($"{process.ProcessName}, Id: {process.Id} already exited."); - } - } - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/Utilities/WindowHelper.cs b/internal/Calculator.UIAutomationLibrary/Utilities/WindowHelper.cs deleted file mode 100644 index bf19700c..00000000 --- a/internal/Calculator.UIAutomationLibrary/Utilities/WindowHelper.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation.Controls; -using WEX.Logging.Interop; - -namespace Calculator.UIAutomationLibrary -{ - public class WindowHelper - { - public static void SetAsForeground(Window window) - { - Log.Comment($"Set window {window.NativeWindowHandle} as the foreground window."); - - NativeMethods.SetForegroundWindow(window.NativeWindowHandle); - } - } -} diff --git a/internal/Calculator.UIAutomationLibrary/project.json b/internal/Calculator.UIAutomationLibrary/project.json deleted file mode 100644 index 71f67e19..00000000 --- a/internal/Calculator.UIAutomationLibrary/project.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "dependencies": { - "AppModel.TestHelper": "2018.3.22", - "EtwProcessor.Managed": "10.34.181220007", - "MITALite": "1.0.180128001", - "Taef.Managed": "10.34.181220007", - "Test.Net.Redist": "2.0.1" - }, - "frameworks": { - "netcore50": {} - } -} \ No newline at end of file diff --git a/internal/Calculator.UITests/Calculator.UITests.csproj b/internal/Calculator.UITests/Calculator.UITests.csproj deleted file mode 100644 index 70da6c2f..00000000 --- a/internal/Calculator.UITests/Calculator.UITests.csproj +++ /dev/null @@ -1,145 +0,0 @@ - - - - - Debug - x86 - {0224A709-0C48-4C4F-BA17-843A49842C15} - Library - Properties - Calculator.UITests - Calculator.UITests - .NETPortable - v5.0 - .NETCore,Version=v5.0 - UAP - 10.0.17763.0 - 10.0.17134.0 - 512 - true - - - true - full - false - bin\Debug\x86 - DEBUG;TRACE - prompt - 4 - x86 - - - pdbonly - true - bin\Release\x86 - TRACE - prompt - 4 - x86 - - - true - full - false - bin\Debug\x64 - DEBUG;TRACE - prompt - 4 - x64 - - - pdbonly - true - bin\Release\x64 - TRACE - prompt - 4 - x64 - - - true - full - false - bin\Debug\arm - DEBUG;TRACE - prompt - 4 - ARM - - - pdbonly - true - bin\Release\arm - TRACE - prompt - 4 - ARM - - - true - full - false - bin\Debug\ARM64\ - DEBUG;TRACE - prompt - 4 - ARM64 - - - pdbonly - true - bin\Release\ARM64\ - TRACE - prompt - 4 - ARM64 - - - - - - - - - - - PreserveNewest - - - TextTemplatingFileGenerator - AppLifecycle.Regions.xml - Designer - - - AppLifecycle.Regions.tt - True - PreserveNewest - True - - - - - - - {a43517b5-8be3-4312-913f-004978c34444} - Calculator.UIAutomationLibrary - - - - - Designer - PreserveNewest - - - - 15.0 - - - - \ No newline at end of file diff --git a/internal/Calculator.UITests/Config/AppLifecycle.Iterations.xml b/internal/Calculator.UITests/Config/AppLifecycle.Iterations.xml deleted file mode 100644 index 65cec859..00000000 --- a/internal/Calculator.UITests/Config/AppLifecycle.Iterations.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - -
-
diff --git a/internal/Calculator.UITests/Config/AppLifecycle.Profile.wprp b/internal/Calculator.UITests/Config/AppLifecycle.Profile.wprp deleted file mode 100644 index 6fe8afd6..00000000 --- a/internal/Calculator.UITests/Config/AppLifecycle.Profile.wprp +++ /dev/null @@ -1,526 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/internal/Calculator.UITests/Config/AppLifecycle.Regions.tt b/internal/Calculator.UITests/Config/AppLifecycle.Regions.tt deleted file mode 100644 index acd8ead6..00000000 --- a/internal/Calculator.UITests/Config/AppLifecycle.Regions.tt +++ /dev/null @@ -1,364 +0,0 @@ -<# // Copyright (c) Microsoft Corporation. All rights reserved. #> -<#@ template language="C#" hostspecific="True" #> -<#@ output extension=".xml" #> -<# // Set options specific to your app here -var options = new { - RegionPrefix = "Calculator", - ImageFileName = "Calculator.exe", - PackageFamilyName = "Microsoft.WindowsCalculator", - FirstView = "ms-resource:///Files/Views/MainPage.xbf" -}; -#> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - - - - - - - - - - - - CPU - <#= options.ImageFileName #> - Commit - <#= options.ImageFileName #> - - - - - - - diff --git a/internal/Calculator.UITests/Config/AppLifecycle.Regions.xml b/internal/Calculator.UITests/Config/AppLifecycle.Regions.xml deleted file mode 100644 index e9ee8aed..00000000 --- a/internal/Calculator.UITests/Config/AppLifecycle.Regions.xml +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - - - - - - - - - - - - CPU - Calculator.exe - Commit - Calculator.exe - - - - - - - diff --git a/internal/Calculator.UITests/Initialization.cs b/internal/Calculator.UITests/Initialization.cs deleted file mode 100644 index bb254fbd..00000000 --- a/internal/Calculator.UITests/Initialization.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Runtime.InteropServices; -using Calculator.UIAutomationLibrary; -using Microsoft.OneCoreUap.Test.AppModel; -using WEX.TestExecution; -using WEX.TestExecution.Markup; - -namespace Calculator.UITests -{ - [TestClass] - public class Initialization - { - [DllImport("AppModel.TestHelper.dll")] - private static extern Int32 WinRTHelper_Register(); - - [AssemblyInitialize] - [TestProperty("CoreClrProfile", "TestNetv2.0")] - [TestProperty("RunFixtureAs:Assembly", "System")] - public static void AssemblySetup(TestContext context) - { - Verify.AreEqual(0, WinRTHelper_Register()); - TestHelper.Initialize(); - - // Install and Start the Etw.Service service to enable the use of EtwWaiter. - EtwHelper.InstallAndStartETWService(); - - bool installApp = false; - if (context.Properties.Contains("InstallApp") && (bool.TryParse(context.Properties["InstallApp"].ToString(), out installApp)) && installApp) - { - string certToDeploy = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), Constants.CertificateFileName); - InstallHelper.InstallCertFile(certToDeploy); - - string vcLibsToDeploy = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), Constants.VCLibsPackageFileName); - string winUIToDeploy = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), Constants.WinUIPackageFileName); - string appxToDeploy = Path.Combine(TAEFHelper.GetTestDeploymentDirectory(), Constants.PackageFileName); - Impersonater.RunAs(Impersonater.RunAsUser.RestrictedUser, () => InstallHelper.InstallPackage(appxToDeploy, vcLibsToDeploy, winUIToDeploy)); - } - } - - [AssemblyCleanup] - [TestProperty("RunFixtureAs:Assembly", "System")] - public static void AssemblyCleanup() - { - // Stop and remove the Etw.Service service. - EtwHelper.StopAndRemoveETWService(); - - TestHelper.Uninitialize(); - } - } -} diff --git a/internal/Calculator.UITests/Properties/AssemblyInfo.cs b/internal/Calculator.UITests/Properties/AssemblyInfo.cs deleted file mode 100644 index c6441765..00000000 --- a/internal/Calculator.UITests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("UIAutomationTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] - -[assembly: AssemblyCulture("")] - - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f5e8f9b8-f7f1-4300-a5cb-35bfab83f51e")] -// The following was autogenerated by UpdateVersion.ps1 -[assembly: AssemblyProduct(@"10.16.1128.1252")] diff --git a/internal/Calculator.UITests/Tests/AppLifecycleTests.cs b/internal/Calculator.UITests/Tests/AppLifecycleTests.cs deleted file mode 100644 index 2b346251..00000000 --- a/internal/Calculator.UITests/Tests/AppLifecycleTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Linq; -using Etw.Managed; -using Microsoft.Windows.Apps.Performance; -using WEX.Logging.Interop; -using WEX.TestExecution; -using WEX.TestExecution.Markup; -using Calculator.UIAutomationLibrary; - -namespace Calculator.PerfTests -{ - [TestClass] - public class AppLifecycleTests - { - [TestInitialize] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodSetup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - [TestCleanup] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodCleanup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - /// - /// This method executes the AppLifecycle performance test. - /// The test launches the application, suspend and resumes it and then terminates the app. - /// - /// A TAEF data source is used to specify the Windows Performance Recorder profile and - /// regions of interest file to use and defines three different configurations to execute this test with. - /// Cold: The initial run to get dlls loaded into memory and to execute any first-run app logic. - /// Warm: The run that will produce consistent results and that would be used for measures. - /// Memory: A run that is focused on collecting more info on allocations. - /// - [TestMethod] - [TestProperty("RunAs", "User")] - [TestProperty("Category", "Performance")] - [TestProperty(WinperfConstants.DataSource, PerfConstants.AppLifecycleIterationsSource)] - public void AppLifecycleTest() - { - AppLifecycle.Run(Constants.PackageAppUserModelId); - } - } -} diff --git a/internal/Calculator.UITests/Tests/CalculatorTests.cs b/internal/Calculator.UITests/Tests/CalculatorTests.cs deleted file mode 100644 index 718830e2..00000000 --- a/internal/Calculator.UITests/Tests/CalculatorTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Calculator.UIAutomationLibrary; -using Calculator.UIAutomationLibrary.Tests; -using WEX.TestExecution.Markup; - -namespace Calculator.UITests -{ - [TestClass] - public class CalculatorTests - { - [TestInitialize] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodSetup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - [TestCleanup] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodCleanup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - [TestMethod] - [TestProperty("RunAs", "User")] - public void OnePlusTwoTest() - { - var calculatorLfm = CalculatorAppLauncher.Launch(); - - calculatorLfm.CalculateOnePlusTwo(); - - calculatorLfm.Close(); - } - - [TestMethod] - [TestProperty("RunAs", "User")] - public void Sqrt4Minus2Test() - { - var calculatorLfm = CalculatorAppLauncher.Launch(); - - calculatorLfm.CalculateSqrt4Minus2(); - - calculatorLfm.Close(); - } - } -} diff --git a/internal/Calculator.UITests/Tests/LaunchTests.cs b/internal/Calculator.UITests/Tests/LaunchTests.cs deleted file mode 100644 index 9af376a5..00000000 --- a/internal/Calculator.UITests/Tests/LaunchTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using MS.Internal.Mita.Foundation; -using MS.Internal.Mita.Foundation.Controls; -using MS.Internal.Mita.Foundation.Waiters; -using System; -using WEX.TestExecution; -using WEX.TestExecution.Markup; -using System.Runtime.InteropServices; -using Microsoft.OneCoreUap.Test.AppModel; -using System.IO; -using Calculator.UITests; -using Calculator.UIAutomationLibrary; - -namespace Calculator.UITests -{ - [TestClass] - public class LaunchTests - { - [TestInitialize] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodSetup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - [TestCleanup] - [TestProperty("RunAs", "ElevatedUserOrSystem")] - public void MethodCleanup() - { - Utilities.KillExistingCalculatorProcesses(); - } - - [TestMethod] - [TestProperty("RunAs", "User")] - public void NormalLaunchTest() - { - var calculatorLfm = CalculatorAppLauncher.Launch(); - calculatorLfm.Close(); - } - } -} diff --git a/internal/Calculator.UITests/project.json b/internal/Calculator.UITests/project.json deleted file mode 100644 index 790fc2a5..00000000 --- a/internal/Calculator.UITests/project.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "dependencies": { - "AppModel.TestHelper": "2018.3.22", - "EtwProcessor.Managed": "10.34.181220007", - "Microsoft.TestInfrastructure.UniversalTest": "1.0.20181107.1", - "Microsoft.Windows.Apps.Performance": "1.0.7", - "MITALite": "1.0.180128001", - "Taef.Managed": "10.34.181220007", - "Test.Net.Redist": "2.0.1" - }, - "frameworks": { - "netcore50": {} - }, - "runtimes": { - "win10-arm": {}, - "win10-x64": {}, - "win10-x86": {} - } -} \ No newline at end of file diff --git a/internal/Calculator.UITests/testmd.definition b/internal/Calculator.UITests/testmd.definition deleted file mode 100644 index c65b246e..00000000 --- a/internal/Calculator.UITests/testmd.definition +++ /dev/null @@ -1,73 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testmddefinition-4.json", - "Package": { - "ComponentName": "Calculator", - "SubComponentName": "UITests" - }, - "SupportedArchitectures": [ "All" ], - "Execution": { - "Type": "TAEF", - "Parameter": "/ScreenCaptureOnError /TestMode:EnsureLoggedOnUser", - "ExecutionTimeoutInMinutes": "30" - }, - "Dependencies": { - "Files": [ - { - "SourcePath": "$(OUT_DIR)Calculator.UIAutomationLibrary.dll", - "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)" - }, - { - "SourcePath": "$(OUT_DIR)Microsoft.Windows.Apps.Performance.dll", - "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)" - }, - { - "SourcePath": "$(OUT_DIR)Config\\AppLifecycle.Profile.wprp", - "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)\\Config\\" - }, - { - "SourcePath": "$(OUT_DIR)Config\\AppLifecycle.Regions.xml", - "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)\\Config\\" - }, - { - "SourcePath": "$(OUT_DIR)Config\\AppLifecycle.Iterations.xml", - "DestinationFolderPath": "$$(TEST_DEPLOY_BIN)\\Config\\" - } - ], - "Packages": [ - "Microsoft-Windows-Test-Taef", - "Microsoft-Windows-Test-EtwProcessor", - "Microsoft-Test-Taef-EnsureLoggedOnUserTestMode", - "Microsoft-Test-Taef-EtwLoggerTestMode", - "Microsoft-Windows-Test-MitaLite", - "Microsoft-Windows-Test-TestNetV2.0", - "Microsoft-OneCoreUap-Test-AppModel-AreaLibrary" - ] - }, - "Logs": [], - "Plugins": [], - "Profiles": [ - { - "Name": "All", - "Execution": { - "AdditionalParameter": "/TestMode:EtwLogger /p:InstallApp=true /select:not(@Category='Performance')" - }, - "Dependencies": { - "AdditionalPackages": [ - "Microsoft-Calculator-App" - ] - } - }, - { - "Name": "Performance", - "Execution": { - "AdditionalParameter": "/TestMode:WinPerf /WinPerf:Upload=Full /winperf:WinPerfSource=\"Utility Apps Performance Tests\" /winperf:VersionProcess=Calculator.exe /winperf:VersionImage=Calculator.exe /p:InstallApp=true /select:@Category='Performance'" - }, - "Dependencies": { - "AdditionalPackages": [ - "Microsoft-Windows-Performance-Winperf-Winperf", - "Microsoft-Calculator-App" - ] - } - } - ] -} diff --git a/internal/CalculatorInternal.sln b/internal/CalculatorInternal.sln deleted file mode 100644 index 98be0fbb..00000000 --- a/internal/CalculatorInternal.sln +++ /dev/null @@ -1,77 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.UIAutomationLibrary", "Calculator.UIAutomationLibrary\Calculator.UIAutomationLibrary.csproj", "{A43517B5-8BE3-4312-913F-004978C34444}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.UITests", "Calculator.UITests\Calculator.UITests.csproj", "{0224A709-0C48-4C4F-BA17-843A49842C15}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E73C8A6E-BB94-4258-ACED-7C837A6A587B}" - ProjectSection(SolutionItems) = preProject - nuget.config = nuget.config - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.TestPackage", "Calculator.TestPackage\Calculator.TestPackage.csproj", "{24767C43-CD5A-4DC9-8D6B-429F255524E5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM = Debug|ARM - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM = Release|ARM - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|ARM.ActiveCfg = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|ARM.Build.0 = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|x64.ActiveCfg = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|x64.Build.0 = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|x86.ActiveCfg = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Debug|x86.Build.0 = Debug|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|ARM.ActiveCfg = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|ARM.Build.0 = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|ARM64.ActiveCfg = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|x64.ActiveCfg = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|x64.Build.0 = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|x86.ActiveCfg = Release|Any CPU - {A43517B5-8BE3-4312-913F-004978C34444}.Release|x86.Build.0 = Release|Any CPU - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|ARM.ActiveCfg = Debug|ARM - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|ARM.Build.0 = Debug|ARM - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|x64.ActiveCfg = Debug|x64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|x64.Build.0 = Debug|x64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|x86.ActiveCfg = Debug|x86 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Debug|x86.Build.0 = Debug|x86 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|ARM.ActiveCfg = Release|ARM - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|ARM.Build.0 = Release|ARM - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|ARM64.ActiveCfg = Release|ARM64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|x64.ActiveCfg = Release|x64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|x64.Build.0 = Release|x64 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|x86.ActiveCfg = Release|x86 - {0224A709-0C48-4C4F-BA17-843A49842C15}.Release|x86.Build.0 = Release|x86 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|ARM.ActiveCfg = Debug|ARM - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|ARM.Build.0 = Debug|ARM - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|x64.ActiveCfg = Debug|x64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|x64.Build.0 = Debug|x64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|x86.ActiveCfg = Debug|x86 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Debug|x86.Build.0 = Debug|x86 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|ARM.ActiveCfg = Release|ARM - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|ARM.Build.0 = Release|ARM - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|ARM64.ActiveCfg = Release|ARM64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|x64.ActiveCfg = Release|x64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|x64.Build.0 = Release|x64 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|x86.ActiveCfg = Release|x86 - {24767C43-CD5A-4DC9-8D6B-429F255524E5}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {97D9888A-CF9F-4147-8CCE-71093C057452} - EndGlobalSection -EndGlobal diff --git a/internal/Readme.txt b/internal/Readme.txt deleted file mode 100644 index b5154325..00000000 --- a/internal/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -This directory contains projects that have dependencies on internal Microsoft tools and that are not necessary to build Calculator. \ No newline at end of file diff --git a/internal/Settings.XamlStyler b/internal/Settings.XamlStyler deleted file mode 100644 index 6fc7c481..00000000 --- a/internal/Settings.XamlStyler +++ /dev/null @@ -1,39 +0,0 @@ -{ - "AttributesTolerance": 2, - "KeepFirstAttributeOnSameLine": true, - "MaxAttributeCharatersPerLine": 0, - "MaxAttributesPerLine": 1, - "NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransfom, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter", - "SeparateByGroups": false, - "EnableAttributeReordering": true, - "AttributeOrderingRuleGroups": [ - "x:Class*", - "xmlns, xmlns:x", - "xmlns:*", - "x:Key, Key, x:Name, Name, x:Uid, Uid, Title", - "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom", - "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight", - "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex", - "Style, Background, Foreground, Fill, BorderBrush, BorderThickness, Stroke, StrokeThickness, Opacity", - "FontFamily, FontSize, LineHeight, FontWeight, FontStyle, FontStretch", - "*:*, *", - "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint", - "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText" - ], - "OrderAttributesByName": true, - "PutEndingBracketOnNewLine": false, - "RemoveEndingTagOfEmptyElement": true, - "SpaceBeforeClosingSlash": false, - "RootElementLineBreakRule": 0, - "ReorderVSM": 1, - "ReorderGridChildren": false, - "ReorderCanvasChildren": false, - "ReorderSetters": 0, - "FormatMarkupExtension": true, - "NoNewLineMarkupExtensions": "x:Bind, Binding", - "ThicknessSeparator": 2, - "ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin", - "FormatOnSave": true, - "CommentPadding": 1, - "IndentSize": 4 -} \ No newline at end of file diff --git a/internal/nuget.config b/internal/nuget.config deleted file mode 100644 index 606b6309..00000000 --- a/internal/nuget.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/nuget.config b/nuget.config index 0286336f..6ef7f342 100644 --- a/nuget.config +++ b/nuget.config @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/src/CalcManager/CEngine/CalcInput.cpp b/src/CalcManager/CEngine/CalcInput.cpp index 4994e025..5bbc1408 100644 --- a/src/CalcManager/CEngine/CalcInput.cpp +++ b/src/CalcManager/CEngine/CalcInput.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CalcEngine.h" using namespace std; @@ -56,7 +56,7 @@ bool CalcInput::TryToggleSign(bool isIntegerMode, wstring_view maxNumStr) return true; } -bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, wstring_view maxNumStr, long wordBitWidth, int maxDigits) +bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, wstring_view maxNumStr, int32_t wordBitWidth, int maxDigits) { // Convert from an integer into a character // This includes both normal digits and alpha 'digits' for radixes > 10 @@ -124,15 +124,15 @@ bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMo else if (radix == 10) { // If value length is at least the max, we know we can't add another digit. - if(pNumSec->value.size() < maxNumStr.size()) + if (pNumSec->value.size() < maxNumStr.size()) { // Compare value to substring of maxNumStr of value.size() length. // If cmpResult > 0: - // eg. max is "127", and the current number is "20". first digit itself says we are out. + // eg. max is "127", and the current number is "20". first digit itself says we are out. // Additional digit is not possible // If cmpResult < 0: - // Success case. eg. max is "127", and current number is say "11". The second digit '1' being < + // Success case. eg. max is "127", and current number is say "11". The second digit '1' being < // corresponding digit '2', means all digits are possible to append, like 119 will still be < 127 // If cmpResult == 0: @@ -151,7 +151,7 @@ bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMo } else if (pNumSec->IsNegative() && chDigit <= lastChar + 1) { - // Negative value case, eg. max is "127", and current number is "-12". Then 8 is also valid, as the range + // Negative value case, eg. max is "127", and current number is "-12". Then 8 is also valid, as the range // is always from -(max+1)...max in signed mode allowExtraDigit = true; } @@ -179,7 +179,7 @@ bool CalcInput::TryAddDecimalPt() if (m_base.IsEmpty()) { - m_base.value += L"0"; // Add a leading zero + m_base.value += L'0'; // Add a leading zero } m_decPtIndex = m_base.value.size(); @@ -231,6 +231,10 @@ void CalcInput::Backspace() if (!m_base.IsEmpty()) { m_base.value.pop_back(); + if (m_base.value == L"0") + { + m_base.value.pop_back(); + } } if (m_base.value.size() <= m_decPtIndex) @@ -261,37 +265,55 @@ void CalcInput::SetDecimalSymbol(wchar_t decSymbol) } } +bool CalcInput::IsEmpty() +{ + return m_base.IsEmpty() && !m_hasExponent && m_exponent.IsEmpty() && !m_hasDecimal; +} + wstring CalcInput::ToString(uint32_t radix) { // In theory both the base and exponent could be C_NUM_MAX_DIGITS long. - wstringstream resStream; - if ((m_base.value.size() > MAX_STRLEN) || (m_hasExponent && m_exponent.value.size() > MAX_STRLEN)) { return wstring(); } + wstring result; + if (m_base.IsNegative()) { - resStream << L'-'; + result = L'-'; } - resStream << (m_base.IsEmpty() ? L"0" : m_base.value); + if (m_base.IsEmpty()) + { + result += L'0'; + } + else + { + result += m_base.value; + } if (m_hasExponent) { // Add a decimal point if it is not already there if (!m_hasDecimal) { - resStream << m_decSymbol; + result += m_decSymbol; } - resStream << ((radix == 10) ? L'e' : L'^'); - resStream << (m_exponent.IsNegative() ? L'-' : L'+'); - resStream << (m_exponent.IsEmpty() ? L"0" : m_exponent.value); - } + result += ((radix == 10) ? L'e' : L'^'); + result += (m_exponent.IsNegative() ? L'-' : L'+'); - auto result = resStream.str(); + if (m_exponent.IsEmpty()) + { + result += L'0'; + } + else + { + result += m_exponent.value; + } + } // Base and Exp can each be up to C_NUM_MAX_DIGITS in length, plus 4 characters for sign, dec, exp, and expSign. if (result.size() > C_NUM_MAX_DIGITS * 2 + 4) diff --git a/src/CalcManager/CEngine/CalcUtils.cpp b/src/CalcManager/CEngine/CalcUtils.cpp index fe488009..8807627f 100644 --- a/src/CalcManager/CEngine/CalcUtils.cpp +++ b/src/CalcManager/CEngine/CalcUtils.cpp @@ -1,28 +1,27 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" #include "Header Files/CalcUtils.h" -bool IsOpInRange(WPARAM op, uint32_t x, uint32_t y) +bool IsOpInRange(OpCode op, uint32_t x, uint32_t y) { return ((op >= x) && (op <= y)); } -bool IsBinOpCode(WPARAM opCode) +bool IsBinOpCode(OpCode opCode) { - return IsOpInRange(opCode, IDC_AND, IDC_PWR); + return IsOpInRange(opCode, IDC_AND, IDC_PWR) || IsOpInRange(opCode, IDC_BINARYEXTENDEDFIRST, IDC_BINARYEXTENDEDLAST); } // WARNING: IDC_SIGN is a special unary op but still this doesn't catch this. Caller has to be aware // of it and catch it themselves or not needing this -bool IsUnaryOpCode(WPARAM opCode) +bool IsUnaryOpCode(OpCode opCode) { - return IsOpInRange(opCode, IDC_UNARYFIRST, IDC_UNARYLAST); + return (IsOpInRange(opCode, IDC_UNARYFIRST, IDC_UNARYLAST) || IsOpInRange(opCode, IDC_UNARYEXTENDEDFIRST, IDC_UNARYEXTENDEDLAST)); } -bool IsDigitOpCode(WPARAM opCode) +bool IsDigitOpCode(OpCode opCode) { return IsOpInRange(opCode, IDC_0, IDC_F); } @@ -32,11 +31,9 @@ bool IsDigitOpCode(WPARAM opCode) // so we abstract this as a separate routine. Note: There is another side to this. Some commands are not // gui mode setting to begin with, but once it is discovered it is invalid and we want to behave as though it // was never inout, we need to revert the state changes made as a result of this test -bool IsGuiSettingOpCode(WPARAM opCode) +bool IsGuiSettingOpCode(OpCode opCode) { - if (IsOpInRange(opCode, IDM_HEX, IDM_BIN) || - IsOpInRange(opCode, IDM_QWORD, IDM_BYTE) || - IsOpInRange(opCode, IDM_DEG, IDM_GRAD)) + if (IsOpInRange(opCode, IDM_HEX, IDM_BIN) || IsOpInRange(opCode, IDM_QWORD, IDM_BYTE) || IsOpInRange(opCode, IDM_DEG, IDM_GRAD)) { return true; } diff --git a/src/CalcManager/CEngine/History.cpp b/src/CalcManager/CEngine/History.cpp index 8dda170e..ea73e18c 100644 --- a/src/CalcManager/CEngine/History.cpp +++ b/src/CalcManager/CEngine/History.cpp @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" #include "Command.h" -#include "CalculatorVector.h" #include "ExpressionCommand.h" constexpr int ASCII_0 = 48; @@ -12,15 +10,21 @@ constexpr int ASCII_0 = 48; using namespace std; using namespace CalcEngine; -namespace { - void IFT(HRESULT hr) +namespace +{ + template + static void Truncate(vector& v, unsigned int index) { - if (FAILED(hr)) + if (index >= v.size()) { - throw hr; + throw E_BOUNDS; } + + auto startIter = v.begin() + index; + v.erase(startIter, v.end()); } } + void CHistoryCollector::ReinitHistory() { m_lastOpStartIndex = -1; @@ -29,21 +33,21 @@ void CHistoryCollector::ReinitHistory() m_bLastOpndBrace = false; if (m_spTokens != nullptr) { - m_spTokens->Clear(); + m_spTokens->clear(); } if (m_spCommands != nullptr) { - m_spCommands->Clear(); + m_spCommands->clear(); } } // Constructor // Can throw Out of memory error -CHistoryCollector::CHistoryCollector(ICalcDisplay *pCalcDisplay, std::shared_ptr pHistoryDisplay, wchar_t decimalSymbol) : - m_pHistoryDisplay(pHistoryDisplay), - m_pCalcDisplay(pCalcDisplay), - m_iCurLineHistStart(-1), - m_decimalSymbol(decimalSymbol) +CHistoryCollector::CHistoryCollector(ICalcDisplay* pCalcDisplay, std::shared_ptr pHistoryDisplay, wchar_t decimalSymbol) + : m_pHistoryDisplay(pHistoryDisplay) + , m_pCalcDisplay(pCalcDisplay) + , m_iCurLineHistStart(-1) + , m_decimalSymbol(decimalSymbol) { ReinitHistory(); } @@ -55,13 +59,13 @@ CHistoryCollector::~CHistoryCollector() if (m_spTokens != nullptr) { - m_spTokens->Clear(); + m_spTokens->clear(); } } void CHistoryCollector::AddOpndToHistory(wstring_view numStr, Rational const& rat, bool fRepetition) { - std::shared_ptr> commands = std::make_shared>(); + std::shared_ptr> commands = std::make_shared>(); // Check for negate bool fNegative = (numStr[0] == L'-'); bool fSciFmt = false; @@ -71,7 +75,7 @@ void CHistoryCollector::AddOpndToHistory(wstring_view numStr, Rational const& ra { if (numStr[i] == m_decimalSymbol) { - IFT(commands->Append(IDC_PNT)); + commands->push_back(IDC_PNT); if (!fSciFmt) { fDecimal = true; @@ -79,12 +83,12 @@ void CHistoryCollector::AddOpndToHistory(wstring_view numStr, Rational const& ra } else if (numStr[i] == L'e') { - IFT(commands->Append(IDC_EXP)); + commands->push_back(IDC_EXP); fSciFmt = true; } else if (numStr[i] == L'-') { - IFT(commands->Append(IDC_SIGN)); + commands->push_back(IDC_SIGN); } else if (numStr[i] == L'+') { @@ -95,7 +99,7 @@ void CHistoryCollector::AddOpndToHistory(wstring_view numStr, Rational const& ra { int num = static_cast(numStr[i]) - ASCII_0; num += IDC_0; - IFT(commands->Append(num)); + commands->push_back(num); } } @@ -120,12 +124,12 @@ void CHistoryCollector::RemoveLastOpndFromHistory() // This will not restore the m_lastBinOpStartIndex, as it isn't possible to remove that also later } -void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool fNoRepetition) +void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool isIntegerMode, bool fNoRepetition) { int iCommandEnd = AddCommand(std::make_shared(nOpCode)); m_lastBinOpStartIndex = IchAddSzToEquationSz(L" ", -1); - IchAddSzToEquationSz(CCalcEngine::OpCodeToString(nOpCode), iCommandEnd); + IchAddSzToEquationSz(CCalcEngine::OpCodeToBinaryString(nOpCode, isIntegerMode), iCommandEnd); IchAddSzToEquationSz(L" ", -1); if (fNoRepetition) @@ -136,16 +140,16 @@ void CHistoryCollector::AddBinOpToHistory(int nOpCode, bool fNoRepetition) } // This is expected to be called when a binary op in the last say 1+2+ is changing to another one say 1+2* (+ changed to *) -// It needs to know by this change a Precedence inversion happened. i.e. previous op was lower or equal to its previous op, but the new +// It needs to know by this change a Precedence inversion happened. i.e. previous op was lower or equal to its previous op, but the new // one isn't. (Eg. 1*2* to 1*2^). It can add explicit brackets to ensure the precedence is inverted. (Eg. (1*2) ^) -void CHistoryCollector::ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher) +void CHistoryCollector::ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher, bool isIntgerMode) { TruncateEquationSzFromIch(m_lastBinOpStartIndex); if (fPrecInvToHigher) { EnclosePrecInversionBrackets(); } - AddBinOpToHistory(nOpCode); + AddBinOpToHistory(nOpCode, isIntgerMode); } void CHistoryCollector::PushLastOpndStart(int ichOpndStart) @@ -203,10 +207,10 @@ bool CHistoryCollector::FOpndAddedToHistory() // AddUnaryOpToHistory // -// This is does the postfix to prefix translation of the input and adds the text to the history. Eg. doing 2 + 4 (sqrt), +// 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 @@ -226,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; } @@ -266,6 +270,30 @@ void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE a command = fInv ? static_cast(CalculationManager::Command::CommandATANH) : IDC_TANH; spExpressionCommand = std::make_shared(command); break; + case IDC_SEC: + command = fInv ? static_cast(CalculationManager::Command::CommandASEC) : IDC_SEC; + spExpressionCommand = std::make_shared(static_cast(angleOpCode), command); + break; + case IDC_CSC: + command = fInv ? static_cast(CalculationManager::Command::CommandACSC) : IDC_CSC; + spExpressionCommand = std::make_shared(static_cast(angleOpCode), command); + break; + case IDC_COT: + command = fInv ? static_cast(CalculationManager::Command::CommandACOT) : IDC_COT; + spExpressionCommand = std::make_shared(static_cast(angleOpCode), command); + break; + case IDC_SECH: + command = fInv ? static_cast(CalculationManager::Command::CommandASECH) : IDC_SECH; + spExpressionCommand = std::make_shared(command); + break; + case IDC_CSCH: + command = fInv ? static_cast(CalculationManager::Command::CommandACSCH) : IDC_CSCH; + spExpressionCommand = std::make_shared(command); + break; + case IDC_COTH: + command = fInv ? static_cast(CalculationManager::Command::CommandACOTH) : IDC_COTH; + spExpressionCommand = std::make_shared(command); + break; case IDC_LN: command = fInv ? static_cast(CalculationManager::Command::CommandPOWE) : IDC_LN; spExpressionCommand = std::make_shared(command); @@ -297,15 +325,10 @@ void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE a } // Called after = with the result of the equation -// Responsible for clearing the top line of current running history display, as well as adding yet another element to +// Responsible for clearing the top line of current running history display, as well as adding yet another element to // history of equations void CHistoryCollector::CompleteHistoryLine(wstring_view numStr) { - if (nullptr != m_pCalcDisplay) - { - m_pCalcDisplay->SetExpressionDisplay(std::make_shared>>(), std::make_shared>>()); - } - if (nullptr != m_pHistoryDisplay) { unsigned int addedItemIndex = m_pHistoryDisplay->AddToHistory(m_spTokens, m_spCommands, numStr); @@ -318,46 +341,47 @@ void CHistoryCollector::CompleteHistoryLine(wstring_view numStr) ReinitHistory(); } +void CHistoryCollector::CompleteEquation(std::wstring_view numStr) +{ + // Add only '=' token and not add EQU command, because + // EQU command breaks loading from history (it duplicate history entries). + IchAddSzToEquationSz(CCalcEngine::OpCodeToString(IDC_EQU), -1); + + SetExpressionDisplay(); + CompleteHistoryLine(numStr); +} + void CHistoryCollector::ClearHistoryLine(wstring_view errStr) { if (errStr.empty()) // in case of error let the display stay as it is { if (nullptr != m_pCalcDisplay) { - m_pCalcDisplay->SetExpressionDisplay(std::make_shared>>(), std::make_shared>>()); + m_pCalcDisplay->SetExpressionDisplay( + std::make_shared>>(), std::make_shared>>()); } m_iCurLineHistStart = -1; // It will get recomputed at the first Opnd ReinitHistory(); } } - // Adds the given string psz to the globally maintained current equation string at the end. // Also returns the 0 based index in the string just added. Can throw out of memory error int CHistoryCollector::IchAddSzToEquationSz(wstring_view str, int icommandIndex) { if (m_spTokens == nullptr) { - m_spTokens = std::make_shared>>(); + m_spTokens = std::make_shared>>(); } - if (FAILED(m_spTokens->Append(std::make_pair(wstring(str), icommandIndex)))) - { - throw(CALC_E_OUTOFMEMORY); - } - - unsigned int nTokens; - m_spTokens->GetSize(&nTokens); - return nTokens - 1; + m_spTokens->push_back(std::pair(wstring(str), icommandIndex)); + return static_cast(m_spTokens->size() - 1); } // Inserts a given string into the global m_pszEquation at the given index ich taking care of reallocations etc. void CHistoryCollector::InsertSzInEquationSz(wstring_view str, int icommandIndex, int ich) { - if (FAILED(m_spTokens->InsertAt(ich, std::make_pair(wstring(str), icommandIndex)))) - { - throw(CALC_E_OUTOFMEMORY); - } + m_spTokens->emplace(m_spTokens->begin() + ich, wstring(str), icommandIndex); } // Chops off the current equation string from the given index @@ -365,25 +389,23 @@ void CHistoryCollector::TruncateEquationSzFromIch(int ich) { // Truncate commands int minIdx = -1; - unsigned int nTokens = 0; - std::pair currentPair; - m_spTokens->GetSize(&nTokens); + unsigned int nTokens = static_cast(m_spTokens->size()); for (unsigned int i = ich; i < nTokens; i++) { - IFT(m_spTokens->GetAt(i, ¤tPair)); + const auto& currentPair = (*m_spTokens)[i]; int curTokenId = currentPair.second; if (curTokenId != -1) { if ((minIdx != -1) || (curTokenId < minIdx)) { minIdx = curTokenId; - IFT(m_spCommands->Truncate(minIdx)); + Truncate(*m_spCommands, minIdx); } } } - IFT(m_spTokens->Truncate(ich)); + Truncate(*m_spTokens, ich); } // Adds the m_pszEquation into the running history text @@ -393,57 +415,47 @@ void CHistoryCollector::SetExpressionDisplay() { m_pCalcDisplay->SetExpressionDisplay(m_spTokens, m_spCommands); } - } -int CHistoryCollector::AddCommand(_In_ const std::shared_ptr & spCommand) +int CHistoryCollector::AddCommand(_In_ const std::shared_ptr& spCommand) { if (m_spCommands == nullptr) { - m_spCommands = std::make_shared >>(); + m_spCommands = std::make_shared>>(); } - if (FAILED(m_spCommands->Append(spCommand))) - { - throw(CALC_E_OUTOFMEMORY); - } - - unsigned int nCommands = 0; - m_spCommands->GetSize(&nCommands); - return nCommands - 1; + m_spCommands->push_back(spCommand); + return static_cast(m_spCommands->size() - 1); } -// To Update the operands in the Expression according to the current Radix +// To Update the operands in the Expression according to the current Radix void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precision) { - if (m_spTokens != nullptr) + if (m_spTokens == nullptr) { - unsigned int size; - IFT(m_spTokens->GetSize(&size)); + return; + } - for (unsigned int i = 0; i < size; ++i) + for (auto& token : *m_spTokens) + { + int commandPosition = token.second; + if (commandPosition != -1) { - std::pair token; - IFT(m_spTokens->GetAt(i, &token)); - int commandPosition = token.second; - if (commandPosition != -1) + const std::shared_ptr& expCommand = m_spCommands->at(commandPosition); + + if (expCommand != nullptr && CalculationManager::CommandType::OperandCommand == expCommand->GetCommandType()) { - std::shared_ptr expCommand; - IFT(m_spCommands->GetAt(commandPosition, &expCommand)); - if (expCommand != nullptr && CalculationManager::CommandType::OperandCommand == expCommand->GetCommandType()) + const std::shared_ptr& opndCommand = std::static_pointer_cast(expCommand); + if (opndCommand != nullptr) { - std::shared_ptr opndCommand = std::static_pointer_cast(expCommand); - if (opndCommand != nullptr) - { - token.first = opndCommand->GetString(radix, precision); - IFT(m_spTokens->SetAt(i, token)); - opndCommand->SetCommands(GetOperandCommandsFromString(token.first)); - } + token.first = opndCommand->GetString(radix, precision); + opndCommand->SetCommands(GetOperandCommandsFromString(token.first)); } } } - SetExpressionDisplay(); } + + SetExpressionDisplay(); } void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol) @@ -452,9 +464,9 @@ void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol) } // Update the commands corresponding to the passed string Number -std::shared_ptr> CHistoryCollector::GetOperandCommandsFromString(wstring_view numStr) +std::shared_ptr> CHistoryCollector::GetOperandCommandsFromString(wstring_view numStr) { - std::shared_ptr> commands = std::make_shared>(); + std::shared_ptr> commands = std::make_shared>(); // Check for negate bool fNegative = (numStr[0] == L'-'); @@ -462,15 +474,15 @@ std::shared_ptr> CHistoryCollector::GetOperandCommandsFrom { if (numStr[i] == m_decimalSymbol) { - IFT(commands->Append(IDC_PNT)); + commands->push_back(IDC_PNT); } else if (numStr[i] == L'e') { - IFT(commands->Append(IDC_EXP)); + commands->push_back(IDC_EXP); } else if (numStr[i] == L'-') { - IFT(commands->Append(IDC_SIGN)); + commands->push_back(IDC_SIGN); } else if (numStr[i] == L'+') { @@ -481,14 +493,14 @@ std::shared_ptr> CHistoryCollector::GetOperandCommandsFrom { int num = static_cast(numStr[i]) - ASCII_0; num += IDC_0; - IFT(commands->Append(num)); + commands->push_back(num); } } // If the number is negative, append a sign command at the end. if (fNegative) { - IFT(commands->Append(IDC_SIGN)); + commands->push_back(IDC_SIGN); } return commands; } diff --git a/src/CalcManager/CEngine/Number.cpp b/src/CalcManager/CEngine/Number.cpp index 0d809917..7b3be21b 100644 --- a/src/CalcManager/CEngine/Number.cpp +++ b/src/CalcManager/CEngine/Number.cpp @@ -1,26 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -#include "pch.h" +#include #include "Header Files/Number.h" using namespace std; namespace CalcEngine { - Number::Number() noexcept : - Number(1, 0, { 0 }) - {} + Number::Number() noexcept + : Number(1, 0, { 0 }) + { + } - Number::Number(int32_t sign, int32_t exp, vector const& mantissa) noexcept : - m_sign{ sign }, - m_exp{ exp }, - m_mantissa{ mantissa } - {} + Number::Number(int32_t sign, int32_t exp, vector const& mantissa) noexcept + : m_sign{ sign } + , m_exp{ exp } + , m_mantissa{ mantissa } + { + } - Number::Number(PNUMBER p) noexcept : - m_sign{ p->sign }, - m_exp{ p->exp }, - m_mantissa{} + Number::Number(PNUMBER p) noexcept + : m_sign{ p->sign } + , m_exp{ p->exp } + , m_mantissa{} { m_mantissa.reserve(p->cdigit); copy(p->mant, p->mant + p->cdigit, back_inserter(m_mantissa)); @@ -28,12 +30,12 @@ namespace CalcEngine PNUMBER Number::ToPNUMBER() const { - PNUMBER ret = _createnum(static_cast(this->Mantissa().size()) + 1); + PNUMBER ret = _createnum(static_cast(this->Mantissa().size()) + 1); ret->sign = this->Sign(); ret->exp = this->Exp(); - ret->cdigit = static_cast(this->Mantissa().size()); + ret->cdigit = static_cast(this->Mantissa().size()); - MANTTYPE *ptrRet = ret->mant; + MANTTYPE* ptrRet = ret->mant; for (auto const& digit : this->Mantissa()) { *ptrRet++ = digit; @@ -59,6 +61,6 @@ namespace CalcEngine bool Number::IsZero() const { - return all_of(m_mantissa.begin(), m_mantissa.end(), [](auto &&i) { return i == 0; }); + return all_of(m_mantissa.begin(), m_mantissa.end(), [](auto&& i) { return i == 0; }); } } diff --git a/src/CalcManager/CEngine/Rational.cpp b/src/CalcManager/CEngine/Rational.cpp index 22a972a3..c703f61d 100644 --- a/src/CalcManager/CEngine/Rational.cpp +++ b/src/CalcManager/CEngine/Rational.cpp @@ -1,16 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -#include "pch.h" #include "Header Files/Rational.h" using namespace std; namespace CalcEngine { - Rational::Rational() noexcept : - m_p{}, - m_q{ 1, 0, { 1 } } - {} + Rational::Rational() noexcept + : m_p{} + , m_q{ 1, 0, { 1 } } + { + } Rational::Rational(Number const& n) noexcept { @@ -24,14 +24,15 @@ namespace CalcEngine m_q = Number(1, qExp, { 1 }); } - Rational::Rational(Number const& p, Number const& q) noexcept : - m_p{ p }, - m_q{ q } - {} + Rational::Rational(Number const& p, Number const& q) noexcept + : m_p{ p } + , m_q{ q } + { + } Rational::Rational(int32_t i) { - PRAT pr = longtorat(static_cast(i)); + PRAT pr = i32torat(static_cast(i)); m_p = Number{ pr->pp }; m_q = Number{ pr->pq }; @@ -41,7 +42,7 @@ namespace CalcEngine Rational::Rational(uint32_t ui) { - PRAT pr = Ulongtorat(static_cast(ui)); + PRAT pr = Ui32torat(static_cast(ui)); m_p = Number{ pr->pp }; m_q = Number{ pr->pq }; @@ -51,8 +52,8 @@ namespace CalcEngine Rational::Rational(uint64_t ui) { - uint32_t hi = HIDWORD(ui); - uint32_t lo = LODWORD(ui); + uint32_t hi = (uint32_t) (((ui) >> 32) & 0xffffffff); + uint32_t lo = (uint32_t) ui; Rational temp = (Rational{ hi } << 32) | lo; @@ -60,10 +61,11 @@ namespace CalcEngine m_q = Number{ temp.Q() }; } - Rational::Rational(PRAT prat) noexcept : - m_p{ Number{prat->pp} }, - m_q{ Number{prat->pq} } - {} + Rational::Rational(PRAT prat) noexcept + : m_p{ Number{ prat->pp } } + , m_q{ Number{ prat->pq } } + { + } PRAT Rational::ToPRAT() const { @@ -100,7 +102,7 @@ namespace CalcEngine addrat(&lhsRat, rhsRat, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -123,7 +125,7 @@ namespace CalcEngine subrat(&lhsRat, rhsRat, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -146,7 +148,7 @@ namespace CalcEngine mulrat(&lhsRat, rhsRat, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -169,7 +171,7 @@ namespace CalcEngine divrat(&lhsRat, rhsRat, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -182,6 +184,13 @@ namespace CalcEngine return *this; } + /// + /// Calculate the remainder after division, the sign of a result will match the sign of the current object. + /// + /// + /// This function has the same behavior as the standard C/C++ operator '%' + /// to calculate the modulus after division instead, use instead. + /// Rational& Rational::operator%=(Rational const& rhs) { PRAT lhsRat = this->ToPRAT(); @@ -189,10 +198,10 @@ namespace CalcEngine try { - modrat(&lhsRat, rhsRat); + remrat(&lhsRat, rhsRat); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -215,7 +224,7 @@ namespace CalcEngine lshrat(&lhsRat, rhsRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -238,7 +247,7 @@ namespace CalcEngine rshrat(&lhsRat, rhsRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -261,7 +270,7 @@ namespace CalcEngine andrat(&lhsRat, rhsRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -283,7 +292,7 @@ namespace CalcEngine orrat(&lhsRat, rhsRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -305,7 +314,7 @@ namespace CalcEngine xorrat(&lhsRat, rhsRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(rhsRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -342,6 +351,13 @@ namespace CalcEngine return lhs; } + /// + /// Calculate the remainder after division, the sign of a result will match the sign of lhs. + /// + /// + /// This function has the same behavior as the standard C/C++ operator '%', to calculate the modulus after division instead, use instead. + /// Rational operator%(Rational lhs, Rational const& rhs) { lhs %= rhs; @@ -388,7 +404,7 @@ namespace CalcEngine { result = rat_equ(lhsRat, rhsRat, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -416,7 +432,7 @@ namespace CalcEngine { result = rat_lt(lhsRat, rhsRat, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(lhsRat); destroyrat(rhsRat); @@ -444,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{}; @@ -453,7 +469,7 @@ namespace CalcEngine { result = RatToString(rat, fmt, radix, precision); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(rat); throw(error); @@ -470,9 +486,9 @@ namespace CalcEngine uint64_t result; try { - result = rattoUlonglong(rat, RATIONAL_BASE, RATIONAL_PRECISION); + result = rattoUi64(rat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(rat); throw(error); diff --git a/src/CalcManager/CEngine/RationalMath.cpp b/src/CalcManager/CEngine/RationalMath.cpp index 46b5a086..00b628ab 100644 --- a/src/CalcManager/CEngine/RationalMath.cpp +++ b/src/CalcManager/CEngine/RationalMath.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/RationalMath.h" using namespace std; @@ -14,7 +13,7 @@ Rational RationalMath::Frac(Rational const& rat) { fracrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -33,7 +32,7 @@ Rational RationalMath::Integer(Rational const& rat) { intrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -55,7 +54,7 @@ Rational RationalMath::Pow(Rational const& base, Rational const& pow) powrat(&baseRat, powRat, RATIONAL_BASE, RATIONAL_PRECISION); destroyrat(powRat); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(baseRat); destroyrat(powRat); @@ -81,7 +80,7 @@ Rational RationalMath::Fact(Rational const& rat) { factrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -101,7 +100,7 @@ Rational RationalMath::Exp(Rational const& rat) { exprat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -121,7 +120,7 @@ Rational RationalMath::Log(Rational const& rat) { lograt(&prat, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -148,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(); @@ -156,7 +155,7 @@ Rational RationalMath::Sin(Rational const& rat, ANGLE_TYPE angletype) { sinanglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -168,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(); @@ -176,7 +175,7 @@ Rational RationalMath::Cos(Rational const& rat, ANGLE_TYPE angletype) { cosanglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -188,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(); @@ -196,7 +195,7 @@ Rational RationalMath::Tan(Rational const& rat, ANGLE_TYPE angletype) { tananglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -208,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(); @@ -216,7 +215,7 @@ Rational RationalMath::ASin(Rational const& rat, ANGLE_TYPE angletype) { asinanglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -228,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(); @@ -236,7 +235,7 @@ Rational RationalMath::ACos(Rational const& rat, ANGLE_TYPE angletype) { acosanglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -248,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(); @@ -256,7 +255,7 @@ Rational RationalMath::ATan(Rational const& rat, ANGLE_TYPE angletype) { atananglerat(&prat, angletype, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -276,7 +275,7 @@ Rational RationalMath::Sinh(Rational const& rat) { sinhrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -296,7 +295,7 @@ Rational RationalMath::Cosh(Rational const& rat) { coshrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -316,7 +315,7 @@ Rational RationalMath::Tanh(Rational const& rat) { tanhrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -336,7 +335,7 @@ Rational RationalMath::ASinh(Rational const& rat) { asinhrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -356,7 +355,7 @@ Rational RationalMath::ACosh(Rational const& rat) { acoshrat(&prat, RATIONAL_BASE, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -376,7 +375,7 @@ Rational RationalMath::ATanh(Rational const& rat) { atanhrat(&prat, RATIONAL_PRECISION); } - catch (DWORD error) + catch (uint32_t error) { destroyrat(prat); throw(error); @@ -387,3 +386,33 @@ Rational RationalMath::ATanh(Rational const& rat) return result; } + +/// +/// Calculate the modulus after division, the sign of the result will match the sign of b. +/// +/// +/// When one of the operand is negative +/// the result will differ from the C/C++ operator '%' +/// use instead to calculate the remainder after division. +/// +Rational RationalMath::Mod(Rational const& a, Rational const& b) +{ + PRAT prat = a.ToPRAT(); + PRAT pn = b.ToPRAT(); + + try + { + modrat(&prat, pn); + destroyrat(pn); + } + catch (uint32_t error) + { + destroyrat(prat); + destroyrat(pn); + throw(error); + } + + auto res = Rational{ prat }; + destroyrat(prat); + return res; +} diff --git a/src/CalcManager/CEngine/calc.cpp b/src/CalcManager/CEngine/calc.cpp index 6f93a600..dd70fb63 100644 --- a/src/CalcManager/CEngine/calc.cpp +++ b/src/CalcManager/CEngine/calc.cpp @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CalcEngine.h" - #include "CalculatorResource.h" using namespace std; @@ -15,7 +14,7 @@ using namespace CalcEngine; static constexpr int DEFAULT_MAX_DIGITS = 32; static constexpr int DEFAULT_PRECISION = 32; -static constexpr long DEFAULT_RADIX = 10; +static constexpr int32_t DEFAULT_RADIX = 10; static constexpr wchar_t DEFAULT_DEC_SEPARATOR = L'.'; static constexpr wchar_t DEFAULT_GRP_SEPARATOR = L','; @@ -25,13 +24,17 @@ static constexpr wstring_view DEFAULT_NUMBER_STR = L"0"; // Read strings for keys, errors, trig types, etc. // These will be copied from the resources to local memory. -array CCalcEngine::s_engineStrings; +unordered_map CCalcEngine::s_engineStrings; void CCalcEngine::LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider) { - for (size_t i = 0; i < s_engineStrings.size(); i++) + for (const auto& sid : g_sids) { - s_engineStrings[i] = resourceProvider.GetCEngineString(g_sids[i]); + auto locString = resourceProvider.GetCEngineString(sid); + if (!locString.empty()) + { + s_engineStrings[sid] = locString; + } } } @@ -54,42 +57,47 @@ void CCalcEngine::InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& // CCalcEngine::CCalcEngine // ////////////////////////////////////////////////// -CCalcEngine::CCalcEngine(bool fPrecedence, bool fIntegerMode, CalculationManager::IResourceProvider* const pResourceProvider, __in_opt ICalcDisplay *pCalcDisplay, __in_opt shared_ptr pHistoryDisplay) : - m_fPrecedence(fPrecedence), - m_fIntegerMode(fIntegerMode), - m_pCalcDisplay(pCalcDisplay), - m_resourceProvider(pResourceProvider), - m_nOpCode(0), - m_nPrevOpCode(0), - m_bChangeOp(false), - m_bRecord(false), - m_bSetCalcState(false), - m_input(DEFAULT_DEC_SEPARATOR), - m_nFE(FMT_FLOAT), - m_memoryValue{ make_unique() }, - m_holdVal{}, - m_currentVal{}, - m_lastVal{}, - m_parenVals{}, - m_precedenceVals{}, - m_bError(false), - m_bInv(false), - m_bNoPrevEqu(true), - m_radix(DEFAULT_RADIX), - m_precision(DEFAULT_PRECISION), - m_cIntDigitsSav(DEFAULT_MAX_DIGITS), - m_decGrouping(), - m_numberString(DEFAULT_NUMBER_STR), - m_nTempCom(0), - m_openParenCount(0), - m_nOp(), - m_nPrecOp(), - m_precedenceOpCount(0), - m_nLastCom(0), - m_angletype(ANGLE_DEG), - m_numwidth(QWORD_WIDTH), - m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR), - m_groupSeparator(DEFAULT_GRP_SEPARATOR) +CCalcEngine::CCalcEngine( + bool fPrecedence, + bool fIntegerMode, + CalculationManager::IResourceProvider* const pResourceProvider, + __in_opt ICalcDisplay* pCalcDisplay, + __in_opt shared_ptr pHistoryDisplay) + : m_fPrecedence(fPrecedence) + , m_fIntegerMode(fIntegerMode) + , m_pCalcDisplay(pCalcDisplay) + , m_resourceProvider(pResourceProvider) + , m_nOpCode(0) + , m_nPrevOpCode(0) + , m_bChangeOp(false) + , m_bRecord(false) + , m_bSetCalcState(false) + , m_input(DEFAULT_DEC_SEPARATOR) + , m_nFE(NumberFormat::Float) + , m_memoryValue{ make_unique() } + , m_holdVal{} + , m_currentVal{} + , m_lastVal{} + , m_parenVals{} + , m_precedenceVals{} + , m_bError(false) + , m_bInv(false) + , m_bNoPrevEqu(true) + , m_radix(DEFAULT_RADIX) + , m_precision(DEFAULT_PRECISION) + , m_cIntDigitsSav(DEFAULT_MAX_DIGITS) + , m_decGrouping() + , m_numberString(DEFAULT_NUMBER_STR) + , m_nTempCom(0) + , m_openParenCount(0) + , m_nOp() + , m_nPrecOp() + , m_precedenceOpCount(0) + , m_nLastCom(0) + , m_angletype(AngleType::Degrees) + , m_numwidth(NUM_WIDTH::QWORD_WIDTH) + , m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR) + , m_groupSeparator(DEFAULT_GRP_SEPARATOR) { InitChopNumbers(); @@ -97,7 +105,7 @@ CCalcEngine::CCalcEngine(bool fPrecedence, bool fIntegerMode, CalculationManager m_maxTrigonometricNum = RationalMath::Pow(10, 100); - SetRadixTypeAndNumWidth(DEC_RADIX, m_numwidth); + SetRadixTypeAndNumWidth(RadixType::Decimal, m_numwidth); SettingsChanged(); DisplayNum(); } @@ -120,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(m_numwidth)]; +} + +std::wstring CCalcEngine::GetMaxDecimalValueString() const +{ + return m_maxDecimalValueStrings[static_cast(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 CCalcEngine::PersistedMemObject() @@ -168,7 +186,7 @@ void CCalcEngine::SettingsChanged() m_HistoryCollector.SetDecimalSymbol(m_decimalSeparator); // put the new decimal symbol into the table used to draw the decimal key - s_engineStrings[IDS_DECIMAL] = m_decimalSeparator; + s_engineStrings[SIDS_DECIMAL_SEPARATOR] = m_decimalSeparator; // we need to redraw to update the decimal point button numChanged = true; diff --git a/src/CalcManager/CEngine/scicomm.cpp b/src/CalcManager/CEngine/scicomm.cpp index cf23cf66..04bdda74 100644 --- a/src/CalcManager/CEngine/scicomm.cpp +++ b/src/CalcManager/CEngine/scicomm.cpp @@ -12,43 +12,39 @@ * * Author: \****************************************************************************/ -#include "pch.h" + +#include #include "Header Files/CalcEngine.h" #include "Header Files/CalcUtils.h" - -#define IDC_RADSIN IDC_UNARYLAST+1 -#define IDC_RADCOS IDC_UNARYLAST+2 -#define IDC_RADTAN IDC_UNARYLAST+3 -#define IDC_GRADSIN IDC_UNARYLAST+4 -#define IDC_GRADCOS IDC_UNARYLAST+5 -#define IDC_GRADTAN IDC_UNARYLAST+6 +#include "NumberFormattingUtils.h" using namespace std; using namespace CalcEngine; -namespace { +namespace +{ // NPrecedenceOfOp // // returns a virtual number for precedence for the operator. We expect binary operator only, otherwise the lowest number // 0 is returned. Higher the number, higher the precedence of the operator. - INT NPrecedenceOfOp(int nopCode) + int NPrecedenceOfOp(int nopCode) { - static BYTE rgbPrec[] = { 0,0, IDC_OR,0, IDC_XOR,0, IDC_AND,1, - IDC_ADD,2, IDC_SUB,2, IDC_RSHF,3, IDC_LSHF,3, - IDC_MOD,3, IDC_DIV,3, IDC_MUL,3, IDC_PWR,4, IDC_ROOT, 4 }; - int iPrec; + static uint16_t rgbPrec[] = { + 0,0, IDC_OR,0, IDC_XOR,0, + IDC_AND,1, IDC_NAND,1, IDC_NOR,1, + 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_LOGBASEY,4 }; - iPrec = 0; - while ((iPrec < size(rgbPrec)) && (nopCode != rgbPrec[iPrec])) + for (unsigned int iPrec = 0; iPrec < size(rgbPrec); iPrec += 2) { - iPrec += 2; + if (nopCode == rgbPrec[iPrec]) + { + return rgbPrec[iPrec + 1]; + } } - if (iPrec >= size(rgbPrec)) - { - iPrec = 0; - } - return rgbPrec[iPrec + 1]; - + return 0; } } @@ -56,7 +52,7 @@ namespace { // // When it is discovered by the state machine that at this point the input is not valid (eg. "1+)"), we want to proceed as though this input never // occurred and may be some feedback to user like Beep. The rest of input can then continue by just ignoring this command. -void CCalcEngine::HandleErrorCommand(WPARAM idc) +void CCalcEngine::HandleErrorCommand(OpCode idc) { if (!IsGuiSettingOpCode(idc)) { @@ -83,7 +79,16 @@ void CCalcEngine::ClearTemporaryValues() m_bError = false; } -void CCalcEngine::ProcessCommand(WPARAM wParam) +void CCalcEngine::ClearDisplay() +{ + if (nullptr != m_pCalcDisplay) + { + m_pCalcDisplay->SetExpressionDisplay( + make_shared>>(), make_shared>>()); + } +} + +void CCalcEngine::ProcessCommand(OpCode wParam) { if (wParam == IDC_SET_RESULT) { @@ -94,10 +99,8 @@ void CCalcEngine::ProcessCommand(WPARAM wParam) ProcessCommandWorker(wParam); } -void CCalcEngine::ProcessCommandWorker(WPARAM 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 @@ -107,7 +110,13 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) if (!IsGuiSettingOpCode(wParam)) { m_nLastCom = m_nTempCom; - m_nTempCom = (INT)wParam; + m_nTempCom = (int)wParam; + } + + // Clear expression shown after = sign, when user do any action. + if (!m_bNoPrevEqu) + { + ClearDisplay(); } if (m_bError) @@ -131,28 +140,29 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) // Toggle Record/Display mode if appropriate. if (m_bRecord) { - if (IsOpInRange(wParam, IDC_AND, IDC_MMINUS) || + if (IsBinOpCode(wParam) || + IsUnaryOpCode(wParam) || + IsOpInRange(wParam, IDC_FE, IDC_MMINUS) || IsOpInRange(wParam, IDC_OPENP, IDC_CLOSEP) || IsOpInRange(wParam, IDM_HEX, IDM_BIN) || IsOpInRange(wParam, IDM_QWORD, IDM_BYTE) || IsOpInRange(wParam, IDM_DEG, IDM_GRAD) || - IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITSTART + 63) || + IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITEND) || (IDC_INV == wParam) || - (IDC_SIGN == wParam && 10 != m_radix)) + (IDC_SIGN == wParam && 10 != m_radix) || + (IDC_RAND == wParam) || + (IDC_EULER == wParam)) { m_bRecord = false; m_currentVal = m_input.ToRational(m_radix, m_precision); - DisplayNum(); // Causes 3.000 to shrink to 3. on first op. + DisplayNum(); // Causes 3.000 to shrink to 3. on first op. } } - else + else if (IsDigitOpCode(wParam) || wParam == IDC_PNT) { - if (IsDigitOpCode(wParam) || wParam == IDC_PNT) - { - m_bRecord = true; - m_input.Clear(); - CheckAndAddLastBinOpToHistory(); - } + m_bRecord = true; + m_input.Clear(); + CheckAndAddLastBinOpToHistory(); } // Interpret digit keys. @@ -167,7 +177,7 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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(); @@ -185,10 +195,9 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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; + m_nOpCode = (int)wParam; // Check to see if by changing this binop, a Precedence inversion is happening. // Eg. 1 * 2 + and + is getting changed to ^. The previous precedence rules would have already computed @@ -196,16 +205,16 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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; m_nPrevOpCode = 0; // Once the precedence inversion has put additional brackets, its no longer required } } - m_HistoryCollector.ChangeLastBinOp(m_nOpCode, fPrecInvToHigher); + m_HistoryCollector.ChangeLastBinOp(m_nOpCode, fPrecInvToHigher, m_fIntegerMode); DisplayAnnounceBinaryOperator(); return; } @@ -225,8 +234,8 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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) { @@ -247,9 +256,9 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) else { /* do the last operation and then if the precedence array is not - * empty or the top is not the '(' demarcator then pop the top - * of the array and recheck precedence against the new operator - */ + * empty or the top is not the '(' demarcator then pop the top + * of the array and recheck precedence against the new operator + */ m_currentVal = DoOperation(m_nOpCode, m_currentVal, m_lastVal); m_nPrevOpCode = m_nOpCode; @@ -278,15 +287,13 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) m_HistoryCollector.PopLastOpndStart(); goto DoPrecedenceCheckAgain; } - } } DisplayAnnounceBinaryOperator(); - m_lastVal = m_currentVal; - m_nOpCode = (INT)wParam; - m_HistoryCollector.AddBinOpToHistory(m_nOpCode); + m_nOpCode = (int)wParam; + m_HistoryCollector.AddBinOpToHistory(m_nOpCode, m_fIntegerMode); m_bNoPrevEqu = m_bChangeOp = true; return; } @@ -313,10 +320,11 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) m_HistoryCollector.AddOpndToHistory(m_numberString, m_currentVal); } - m_HistoryCollector.AddUnaryOpToHistory((INT)wParam, m_bInv, m_angletype); + m_HistoryCollector.AddUnaryOpToHistory((int)wParam, m_bInv, m_angletype); } - if ((wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH)) + if ((wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH) + || (wParam == IDC_SEC) || (wParam == IDC_CSC) || (wParam == IDC_COT) || (wParam == IDC_SECH) || (wParam == IDC_CSCH) || (wParam == IDC_COTH)) { if (IsCurrentTooBigForTrig()) { @@ -326,7 +334,7 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) } } - m_currentVal = SciCalcFunctions(m_currentVal, (DWORD)wParam); + m_currentVal = SciCalcFunctions(m_currentVal, (uint32_t)wParam); if (m_bError) return; @@ -344,10 +352,12 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) and have been used */ if (m_bInv && - ((wParam == IDC_CHOP) || - (wParam == IDC_SIN) || (wParam == IDC_COS) || (wParam == IDC_TAN) || - (wParam == IDC_LN) || (wParam == IDC_DMS) || (wParam == IDC_DEGREES) || - (wParam == IDC_SINH) || (wParam == IDC_COSH) || (wParam == IDC_TANH))) + ((wParam == IDC_CHOP) || (wParam == IDC_SIN) || (wParam == IDC_COS) || + (wParam == IDC_TAN) || (wParam == IDC_LN) || (wParam == IDC_DMS) || + (wParam == IDC_DEGREES) || (wParam == IDC_SINH) || (wParam == IDC_COSH) || + (wParam == IDC_TANH) || (wParam == IDC_SEC) || (wParam == IDC_CSC) || + (wParam == IDC_COT) || (wParam == IDC_SECH) || (wParam == IDC_CSCH) || + (wParam == IDC_COTH))) { m_bInv = false; } @@ -356,17 +366,17 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) } // Tiny binary edit windows clicked. Toggle that bit and update display - if (IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITSTART + 63)) + if (IsOpInRange(wParam, IDC_BINEDITSTART, IDC_BINEDITEND)) { // Same reasoning as for unary operators. We need to seed it previous number - if (m_nLastCom >= IDC_AND && m_nLastCom <= IDC_PWR) + if (IsBinOpCode(m_nLastCom)) { m_currentVal = m_lastVal; } CheckAndAddLastBinOpToHistory(); - if (TryToggleBit(m_currentVal, (DWORD)wParam - IDC_BINEDITSTART)) + if (TryToggleBit(m_currentVal, (uint32_t)wParam - IDC_BINEDITSTART)) { DisplayNum(); } @@ -381,24 +391,25 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) { if (!m_bChangeOp) { - // A special goody we are doing to preserve the history, if all was done was serious of unary operations last + // Preserve history, if everything done before was a series of unary operations. CheckAndAddLastBinOpToHistory(false); } m_lastVal = 0; m_bChangeOp = false; - m_precedenceOpCount = m_nTempCom = m_nLastCom = m_nOpCode = m_openParenCount = 0; + m_openParenCount = 0; + m_precedenceOpCount = m_nTempCom = m_nLastCom = m_nOpCode = 0; m_nPrevOpCode = 0; m_bNoPrevEqu = true; - + m_carryBit = 0; /* clear the parenthesis status box indicator, this will not be cleared for CENTR */ if (nullptr != m_pCalcDisplay) { - m_pCalcDisplay->SetParenDisplayText(L""); - m_pCalcDisplay->SetExpressionDisplay(make_shared>>(), make_shared>>()); + m_pCalcDisplay->SetParenthesisNumber(0); + ClearDisplay(); } m_HistoryCollector.ClearHistoryLine(wstring()); @@ -441,8 +452,8 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) // automatic closing of all the parenthesis to get a meaningful result as well as ensure data integrity m_nTempCom = m_nLastCom; // Put back this last saved command to the prev state so ) can be handled properly ProcessCommand(IDC_CLOSEP); - m_nLastCom = m_nTempCom; // Actually this is IDC_CLOSEP - m_nTempCom = (INT)wParam; // put back in the state where last op seen was IDC_CLOSEP, and current op is IDC_EQU + m_nLastCom = m_nTempCom; // Actually this is IDC_CLOSEP + m_nTempCom = (int)wParam; // put back in the state where last op seen was IDC_CLOSEP, and current op is IDC_EQU } if (!m_bNoPrevEqu) @@ -463,50 +474,17 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) m_HistoryCollector.AddOpndToHistory(m_numberString, m_currentVal); } - do { - - if (m_nOpCode) /* Is there a valid operation around? */ - { - /* If this is the first EQU in a string, set m_holdVal=m_currentVal */ - /* Otherwise let m_currentVal=m_holdVal. This keeps m_currentVal constant */ - /* through all EQUs in a row. */ - if (m_bNoPrevEqu) - { - m_holdVal = m_currentVal; - } - else - { - m_currentVal = m_holdVal; - DisplayNum(); // to update the m_numberString - m_HistoryCollector.AddBinOpToHistory(m_nOpCode, false); - m_HistoryCollector.AddOpndToHistory(m_numberString, m_currentVal); // Adding the repeated last op to history - } - - // Do the current or last operation. - m_currentVal = DoOperation(m_nOpCode, m_currentVal, m_lastVal); - m_nPrevOpCode = m_nOpCode; - m_lastVal = m_currentVal; - - /* Check for errors. If this wasn't done, DisplayNum */ - /* would immediately overwrite any error message. */ - if (!m_bError) - DisplayNum(); - - /* No longer the first EQU. */ - m_bNoPrevEqu = false; - } - else if (!m_bError) - DisplayNum(); - - if (m_precedenceOpCount == 0 || !m_fPrecedence) - break; - - m_nOpCode = m_nPrecOp[--m_precedenceOpCount]; + // Evaluate the precedence stack. + ResolveHighestPrecedenceOperation(); + while (m_fPrecedence && m_precedenceOpCount > 0) + { + m_precedenceOpCount--; + m_nOpCode = m_nPrecOp[m_precedenceOpCount]; 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(); @@ -514,16 +492,14 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) m_HistoryCollector.PopLastOpndStart(); m_bNoPrevEqu = true; - } while (m_precedenceOpCount >= 0); + + ResolveHighestPrecedenceOperation(); + } if (!m_bError) { wstring groupedString = GroupDigitsPerRadix(m_numberString, m_radix); - m_HistoryCollector.CompleteHistoryLine(groupedString); - if (nullptr != m_pCalcDisplay) - { - m_pCalcDisplay->SetExpressionDisplay(make_shared>>(), make_shared>>()); - } + m_HistoryCollector.CompleteEquation(groupedString); } m_bChangeOp = false; @@ -533,20 +509,15 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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(); } @@ -555,7 +526,7 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) break; } - if (nx) + if (wParam == IDC_OPENP) { CheckAndAddLastBinOpToHistory(); m_HistoryCollector.AddOpenBraceToHistory(); @@ -603,8 +574,8 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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(); @@ -632,7 +603,7 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) // Set the "(=xx" indicator. if (nullptr != m_pCalcDisplay) { - m_pCalcDisplay->SetParenDisplayText(m_openParenCount ? to_wstring(m_openParenCount) : L""); + m_pCalcDisplay->SetParenthesisNumber(m_openParenCount >= 0 ? static_cast(m_openParenCount) : 0); } if (!m_bError) @@ -648,7 +619,7 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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; } @@ -664,20 +635,20 @@ void CCalcEngine::ProcessCommandWorker(WPARAM 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(wParam - IDM_DEG); + m_angletype = static_cast(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(); } @@ -744,7 +715,6 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) case IDC_MCLEAR: m_memoryValue = make_unique(wParam == IDC_STORE ? TruncateNumForIntMath(m_currentVal) : 0); break; - case IDC_PI: if (!m_fIntegerMode) { @@ -757,10 +727,46 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) } HandleErrorCommand(wParam); break; + case IDC_RAND: + if (!m_fIntegerMode) + { + CheckAndAddLastBinOpToHistory(); // rand is like entering the number + wstringstream str; + str << fixed << setprecision(m_precision) << GenerateRandomNumber(); + + auto rat = StringToRat(false, str.str(), false, L"", m_radix, m_precision); + if (rat != nullptr) + { + m_currentVal = Rational{ rat }; + } + else + { + m_currentVal = Rational{ 0 }; + } + destroyrat(rat); + + DisplayNum(); + m_bInv = false; + break; + } + HandleErrorCommand(wParam); + break; + case IDC_EULER: + if (!m_fIntegerMode) + { + CheckAndAddLastBinOpToHistory(); // e is like entering the number + m_currentVal = Rational{ rat_exp }; + + DisplayNum(); + m_bInv = false; + break; + } + HandleErrorCommand(wParam); + break; case IDC_FE: // Toggle exponential notation display. - m_nFE = NUMOBJ_FMT(!(int)m_nFE); + m_nFE = NumberFormat(!(int)m_nFE); DisplayNum(); break; @@ -786,7 +792,48 @@ void CCalcEngine::ProcessCommandWorker(WPARAM wParam) m_bInv = !m_bInv; break; } +} +// Helper function to resolve one item on the precedence stack. +void CCalcEngine::ResolveHighestPrecedenceOperation() +{ + // Is there a valid operation around? + if (m_nOpCode) + { + // If this is the first EQU in a string, set m_holdVal=m_currentVal + // Otherwise let m_currentVal=m_holdVal. This keeps m_currentVal constant + // through all EQUs in a row. + if (m_bNoPrevEqu) + { + m_holdVal = m_currentVal; + } + else + { + m_currentVal = m_holdVal; + DisplayNum(); // to update the m_numberString + m_HistoryCollector.AddBinOpToHistory(m_nOpCode, m_fIntegerMode, false); + m_HistoryCollector.AddOpndToHistory(m_numberString, m_currentVal); // Adding the repeated last op to history + } + + // Do the current or last operation. + m_currentVal = DoOperation(m_nOpCode, m_currentVal, m_lastVal); + m_nPrevOpCode = m_nOpCode; + m_lastVal = m_currentVal; + + // Check for errors. If this wasn't done, DisplayNum + // would immediately overwrite any error message. + if (!m_bError) + { + DisplayNum(); + } + + // No longer the first EQU. + m_bNoPrevEqu = false; + } + else if (!m_bError) + { + DisplayNum(); + } } // CheckAndAddLastBinOpToHistory @@ -809,7 +856,6 @@ void CCalcEngine::CheckAndAddLastBinOpToHistory(bool addToHistory) // So erase the last operand m_HistoryCollector.RemoveLastOpndFromHistory(); } - } else if (m_HistoryCollector.FOpndAddedToHistory() && !m_bError) { @@ -818,8 +864,7 @@ void CCalcEngine::CheckAndAddLastBinOpToHistory(bool addToHistory) // Let us make a current value =. So in case of 4 SQRT (or a equation under braces) and then a new equation is started, we can just form // a useful equation of sqrt(4) = 2 and continue a new equation from now on. But no point in doing this for things like // MR, SUM etc. All you will get is 5 = 5 kind of no useful equation. - if ((IsUnaryOpCode(m_nLastCom) || IDC_SIGN == m_nLastCom || IDC_CLOSEP == m_nLastCom) && - 0 == m_openParenCount) + if ((IsUnaryOpCode(m_nLastCom) || IDC_SIGN == m_nLastCom || IDC_CLOSEP == m_nLastCom) && 0 == m_openParenCount) { if (addToHistory) { @@ -857,155 +902,142 @@ void CCalcEngine::DisplayAnnounceBinaryOperator() // Unary operator Function Name table Element // since unary operators button names aren't exactly friendly for history purpose, // we have this separate table to get its localized name and for its Inv function if it exists. -typedef struct +struct FunctionNameElement { - int idsFunc; // index of string for the unary op function. Can be NULL, in which case it same as button name - int idsFuncInv; // index of string for Inv of unary op. Can be NULL, in case it is same as idsFunc - bool fDontUseInExpEval; // true if this cant be used in reverse direction as well, ie. during expression evaluation -} UFNE; + wstring degreeString; // Used by default if there are no rad or grad specific strings. + wstring inverseDegreeString; // Will fall back to degreeString if empty -// Table for each unary operator -static const UFNE rgUfne[] = -{ - /* IDC_CHOP */{ 0, IDS_FRAC, false }, - /* IDC_ROL */{ 0, 0, true }, - /* IDC_ROR */{ 0, 0, true }, + wstring radString; + wstring inverseRadString; // Will fall back to radString if empty - /* IDC_COM */{ 0, 0, true }, - /* IDC_SIN */{ IDS_SIND, IDS_ASIND, false }, // default in this table is degrees for sin,cos & tan - /* IDC_COS */{ IDS_COSD, IDS_ACOSD, false }, - /* IDC_TAN */{ IDS_TAND, IDS_ATAND, false }, + wstring gradString; + wstring inverseGradString; // Will fall back to gradString if empty - /* IDC_SINH */{ 0, IDS_ASINH, false }, - /* IDC_COSH */{ 0, IDS_ACOSH, false }, - /* IDC_TANH */{ 0, IDS_ATANH, false }, + wstring programmerModeString; - /* IDC_LN */{ 0, IDS_POWE, false }, - /* IDC_LOG */{ 0, 0, false }, - /* IDC_SQRT */{ 0, 0, false }, - /* IDC_SQR */{ IDS_SQR, 0, false }, - /* IDC_CUB */{ IDS_CUBE, 0, false }, - /* IDC_FAC */{ IDS_FACT, 0, false }, - /* IDC_REC */{ IDS_REC, 0, false }, - /* IDC_DMS */{ 0, IDS_DEGREES, false }, - /* IDC_CUBEROOT */{ 0, 0, false }, - /* IDC_POW10 */{ 0, 0, false }, - /* IDC_PERCENT */{ 0, 0, false }, - - /* IDC_RADSIN */{ IDS_SINR, IDS_ASINR, false }, - /* IDC_RADCOS */{ IDS_COSR, IDS_ACOSR, false }, - /* IDC_RADTAN */{ IDS_TANR, IDS_ATANR, false }, - /* IDC_GRADCOS */{ IDS_SING, IDS_ASING, false }, - /* IDC_GRADCOS */{ IDS_COSG, IDS_ACOSG, false }, - /* IDC_GRADTAN */{ IDS_TANG, IDS_ATANG, false }, + bool hasAngleStrings = ((!radString.empty()) || (!inverseRadString.empty()) || (!gradString.empty()) || (!inverseGradString.empty())); }; -wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype) +// Table for each unary operator +static const std::unordered_map operatorStringTable = { - // Special cases for Sign and Degrees - if (IDC_SIGN == nOpCode) - { - return GetString(IDS_NEGATE); - } - if (IDC_DEGREES == nOpCode) - { - return GetString(IDS_DEGREES); - } + { IDC_CHOP, { L"", SIDS_FRAC } }, - // Correct the trigonometric functions with type of angle argument they take - if (ANGLE_RAD == angletype) - { - switch (nOpCode) - { - case IDC_SIN: - nOpCode = IDC_RADSIN; - break; - case IDC_COS: - nOpCode = IDC_RADCOS; - break; - case IDC_TAN: - nOpCode = IDC_RADTAN; - break; - } - } - else if (ANGLE_GRAD == angletype) - { - switch (nOpCode) - { - case IDC_SIN: - nOpCode = IDC_GRADSIN; - break; - case IDC_COS: - nOpCode = IDC_GRADCOS; - break; - case IDC_TAN: - nOpCode = IDC_GRADTAN; - break; - } - } + { IDC_SIN, { SIDS_SIND, SIDS_ASIND, SIDS_SINR, SIDS_ASINR, SIDS_SING, SIDS_ASING } }, + { IDC_COS, { SIDS_COSD, SIDS_ACOSD, SIDS_COSR, SIDS_ACOSR, SIDS_COSG, SIDS_ACOSG } }, + { IDC_TAN, { SIDS_TAND, SIDS_ATAND, SIDS_TANR, SIDS_ATANR, SIDS_TANG, SIDS_ATANG } }, + { IDC_SINH, { L"", SIDS_ASINH } }, + { IDC_COSH, { L"", SIDS_ACOSH } }, + { IDC_TANH, { L"", SIDS_ATANH } }, + + { IDC_SEC, { SIDS_SECD, SIDS_ASECD, SIDS_SECR, SIDS_ASECR, SIDS_SECG, SIDS_ASECG } }, + { IDC_CSC, { SIDS_CSCD, SIDS_ACSCD, SIDS_CSCR, SIDS_ACSCR, SIDS_CSCG, SIDS_ACSCG } }, + { IDC_COT, { SIDS_COTD, SIDS_ACOTD, SIDS_COTR, SIDS_ACOTR, SIDS_COTG, SIDS_ACOTG } }, + + { IDC_SECH, { SIDS_SECH, SIDS_ASECH } }, + { IDC_CSCH, { SIDS_CSCH, SIDS_ACSCH } }, + { IDC_COTH, { SIDS_COTH, SIDS_ACOTH } }, + + { IDC_LN, { L"", SIDS_POWE } }, + { IDC_SQR, { SIDS_SQR } }, + { IDC_CUB, { SIDS_CUBE } }, + { IDC_FAC, { SIDS_FACT } }, + { IDC_REC, { SIDS_RECIPROC } }, + { IDC_DMS, { L"", SIDS_DEGREES } }, + { IDC_SIGN, { SIDS_NEGATE } }, + { IDC_DEGREES, { SIDS_DEGREES } }, + { IDC_POW2, { SIDS_TWOPOWX } }, + { IDC_LOGBASEY, { SIDS_LOGBASEY } }, + { IDC_ABS, { SIDS_ABS } }, + { IDC_CEIL, { SIDS_CEIL } }, + { IDC_FLOOR, { SIDS_FLOOR } }, + { IDC_NAND, { SIDS_NAND } }, + { IDC_NOR, { SIDS_NOR } }, + { IDC_RSHFL, { SIDS_RSH } }, + { IDC_RORC, { SIDS_ROR } }, + { IDC_ROLC, { SIDS_ROL } }, + { IDC_CUBEROOT, { SIDS_CUBEROOT } }, + { IDC_MOD, { SIDS_MOD, L"", L"", L"", L"", L"", SIDS_PROGRAMMER_MOD } }, +}; + +wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype) +{ // Try to lookup the ID in the UFNE table - int ids = 0; - int iufne = nOpCode - IDC_UNARYFIRST; - if (iufne >= 0 && iufne < size(rgUfne)) + wstring ids = L""; + + if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end()) { - if (fInv) + const FunctionNameElement& element = pair->second; + if (!element.hasAngleStrings || AngleType::Degrees == angletype) { - ids = rgUfne[iufne].idsFuncInv; + if (fInv) + { + ids = element.inverseDegreeString; + } + + if (ids.empty()) + { + ids = element.degreeString; + } } - if (0 == ids) + else if (AngleType::Radians == angletype) { - ids = rgUfne[iufne].idsFunc; + if (fInv) + { + ids = element.inverseRadString; + } + if (ids.empty()) + { + ids = element.radString; + } } + else if (AngleType::Gradians == angletype) + { + if (fInv) + { + ids = element.inverseGradString; + } + if (ids.empty()) + { + ids = element.gradString; + } + } + } + + if (!ids.empty()) + { + return GetString(ids); } // If we didn't find an ID in the table, use the op code. - if (0 == ids) - { - ids = IdStrFromCmdId(nOpCode); - } - - return GetString(ids); + return OpCodeToString(nOpCode); } -// -// Sets the Angle Mode for special unary op IDC's which are used to index to the table rgUfne -// and returns the equivalent plain IDC for trigonometric function. If it isn't a trigonometric function -// returns the passed in idc itself. -int CCalcEngine::IdcSetAngleTypeDecMode(int idc) +wstring_view CCalcEngine::OpCodeToBinaryString(int nOpCode, bool isIntegerMode) { - int idcAngleCmd = IDM_DEG; + // Try to lookup the ID in the UFNE table + wstring ids = L""; - switch (idc) + if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end()) { - case IDC_RADSIN: - idcAngleCmd = IDM_RAD; - idc = IDC_SIN; - break; - case IDC_RADCOS: - idcAngleCmd = IDM_RAD; - idc = IDC_COS; - break; - case IDC_RADTAN: - idcAngleCmd = IDM_RAD; - idc = IDC_TAN; - break; - case IDC_GRADSIN: - idcAngleCmd = IDM_GRAD; - idc = IDC_SIN; - break; - case IDC_GRADCOS: - idcAngleCmd = IDM_GRAD; - idc = IDC_COS; - break; - case IDC_GRADTAN: - idcAngleCmd = IDM_GRAD; - idc = IDC_TAN; - break; + if (isIntegerMode && !pair->second.programmerModeString.empty()) + { + ids = pair->second.programmerModeString; + } + else + { + ids = pair->second.degreeString; + } } - ProcessCommand(idcAngleCmd); - return idc; + if (!ids.empty()) + { + return GetString(ids); + } + + // If we didn't find an ID in the table, use the op code. + return OpCodeToString(nOpCode); } bool CCalcEngine::IsCurrentTooBigForTrig() @@ -1018,7 +1050,7 @@ int CCalcEngine::GetCurrentRadix() return m_radix; } -wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision) +wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { Rational rat = (m_bRecord ? m_input.ToRational(m_radix, m_precision) : m_currentVal); @@ -1031,7 +1063,14 @@ wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision) ChangeConstants(m_radix, m_precision); } - return GroupDigitsPerRadix(numberString, radix); + if (groupDigitsPerRadix) + { + return GroupDigitsPerRadix(numberString, radix); + } + else + { + return numberString; + } } wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix) @@ -1055,15 +1094,26 @@ 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); } - catch (DWORD) + catch (uint32_t) { } } return result; } + +double CCalcEngine::GenerateRandomNumber() +{ + if (m_randomGeneratorEngine == nullptr) + { + random_device rd; + m_randomGeneratorEngine = std::make_unique(rd()); + m_distr = std::make_unique>(0, 1); + } + return (*m_distr.get())(*m_randomGeneratorEngine.get()); +} diff --git a/src/CalcManager/CEngine/scidisp.cpp b/src/CalcManager/CEngine/scidisp.cpp index e01cc239..0074c77a 100644 --- a/src/CalcManager/CEngine/scidisp.cpp +++ b/src/CalcManager/CEngine/scidisp.cpp @@ -12,7 +12,9 @@ * * Author: \****************************************************************************/ -#include "pch.h" + +#include +#include #include "Header Files/CalcEngine.h" using namespace std; @@ -23,7 +25,6 @@ constexpr uint32_t MAX_GROUPING_SIZE = 16; constexpr wstring_view c_decPreSepStr = L"[+-]?(\\d*)["; constexpr wstring_view c_decPostSepStr = L"]?(\\d*)(?:e[+-]?(\\d*))?$"; - /****************************************************************************\ * void DisplayNum(void) * @@ -35,18 +36,19 @@ constexpr wstring_view c_decPostSepStr = L"]?(\\d*)(?:e[+-]?(\\d*))?$"; // // State of calc last time DisplayNum was called // -typedef struct { +typedef struct +{ Rational value; int32_t precision; uint32_t radix; - INT nFE; - NUM_WIDTH numwidth; - bool fIntMath; - bool bRecord; - bool bUseSep; + int nFE; + NUM_WIDTH numwidth; + bool fIntMath; + bool bRecord; + bool bUseSep; } LASTDISP; -LASTDISP gldPrevious = { 0, -1, 0, -1, (NUM_WIDTH)-1, false, false, false }; +static LASTDISP gldPrevious = { 0, -1, 0, -1, (NUM_WIDTH)-1, false, false, false }; // Truncates if too big, makes it a non negative - the number in rat. Doesn't do anything if not in INT mode CalcEngine::Rational CCalcEngine::TruncateNumForIntMath(CalcEngine::Rational const& rat) @@ -64,11 +66,11 @@ CalcEngine::Rational CCalcEngine::TruncateNumForIntMath(CalcEngine::Rational con if (result < 0) { // if negative make positive by doing a twos complement - result = -(result) - 1; - result ^= m_chopNumbers[m_numwidth]; + result = -(result)-1; + result ^= GetChopNumber(); } - result &= m_chopNumbers[m_numwidth]; + result &= GetChopNumber(); return result; } @@ -82,15 +84,8 @@ void CCalcEngine::DisplayNum(void) // something important has changed since the last time DisplayNum was // 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) + if (m_bRecord || gldPrevious.value != m_currentVal || gldPrevious.precision != m_precision || gldPrevious.radix != m_radix || gldPrevious.nFE != (int)m_nFE + || !gldPrevious.bUseSep || gldPrevious.numwidth != m_numwidth || gldPrevious.fIntMath != m_fIntegerMode || gldPrevious.bRecord != m_bRecord) { gldPrevious.precision = m_precision; gldPrevious.radix = m_radix; @@ -220,10 +215,12 @@ int CCalcEngine::IsNumberInvalid(const wstring& numberString, int iMaxExp, int i \****************************************************************************/ vector CCalcEngine::DigitGroupingStringToGroupingVector(wstring_view groupingString) { - vector grouping{}; + vector grouping; uint32_t currentGroup = 0; wchar_t* next = nullptr; - for (const wchar_t* itr = groupingString.data(); *itr != L'\0'; ++itr) + const wchar_t* begin = groupingString.data(); + const wchar_t* end = begin + groupingString.length(); + for (auto itr = begin; itr != end; ++itr) { // Try to parse a grouping number from the string currentGroup = wcstoul(itr, &next, 10); @@ -237,7 +234,7 @@ vector CCalcEngine::DigitGroupingStringToGroupingVector(wstring_view g // If we found a grouping and aren't at the end of the string yet, // jump to the next position in the string (the ';'). // The loop will then increment us to the next character, which should be a number. - if (next && (static_cast(next - groupingString.data()) < groupingString.length())) + if (next && (static_cast(next - begin) < groupingString.length())) { itr = next; } @@ -317,7 +314,7 @@ wstring CCalcEngine::GroupDigits(wstring_view delimiter, vector const& ritr = displayString.rbegin(); } - wstringstream groupedStream{}; + wstring result; uint32_t groupingSize = 0; auto groupItr = grouping.begin(); @@ -328,7 +325,7 @@ wstring CCalcEngine::GroupDigits(wstring_view delimiter, vector const& auto reverse_end = displayString.rend() - (isNumNegative ? 1 : 0); while (ritr != reverse_end) { - groupedStream << *ritr++; + result += *ritr++; groupingSize++; // If a group is complete, add a separator @@ -337,7 +334,7 @@ wstring CCalcEngine::GroupDigits(wstring_view delimiter, vector const& // - we are at the end of the digit string if (currGrouping != 0 && (groupingSize % currGrouping) == 0 && ritr != reverse_end) { - groupedStream << wstring{ delimiter }; + result += delimiter; groupingSize = 0; // reset for a new group // Shift the grouping to next values if they exist @@ -369,11 +366,10 @@ wstring CCalcEngine::GroupDigits(wstring_view delimiter, vector const& // now copy the negative sign if it is there if (isNumNegative) { - groupedStream << displayString[0]; + result += displayString[0]; } - auto groupedString = groupedStream.str(); - wstring result(groupedString.rbegin(), groupedString.rend()); + reverse(result.begin(), result.end()); // Add the right (fractional or exponential) part of the number to the final string. if (hasDecimal) { diff --git a/src/CalcManager/CEngine/scifunc.cpp b/src/CalcManager/CEngine/scifunc.cpp index 9c7ef7a3..b767968a 100644 --- a/src/CalcManager/CEngine/scifunc.cpp +++ b/src/CalcManager/CEngine/scifunc.cpp @@ -16,7 +16,6 @@ /*** ***/ /*** ***/ /**************************************************************************/ -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace std; @@ -24,7 +23,7 @@ using namespace CalcEngine; using namespace CalcEngine::RationalMath; /* Routines for more complex mathematical functions/error checking. */ -CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& rat, DWORD op) +CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& rat, uint32_t op) { Rational result{}; try @@ -43,27 +42,36 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r } else { - result = rat ^ m_chopNumbers[m_numwidth]; + result = rat ^ GetChopNumber(); } break; - // Rotate Left with hi bit wrapped over to lo bit case IDC_ROL: + case IDC_ROLC: if (m_fIntegerMode) { result = Integer(rat); uint64_t w64Bits = result.ToUInt64_t(); uint64_t msb = (w64Bits >> (m_dwWordBitWidth - 1)) & 1; - w64Bits <<= 1; // LShift by 1 - w64Bits |= msb; // Set the prev Msb as the current Lsb + w64Bits <<= 1; // LShift by 1 + + if (op == IDC_ROL) + { + w64Bits |= msb; // Set the prev Msb as the current Lsb + } + else + { + w64Bits |= m_carryBit; // Set the carry bit as the LSB + m_carryBit = msb; // Store the msb as the next carry bit + } result = w64Bits; } break; - // Rotate right with lo bit wrapped over to hi bit case IDC_ROR: + case IDC_RORC: if (m_fIntegerMode) { result = Integer(rat); @@ -71,7 +79,16 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r uint64_t w64Bits = result.ToUInt64_t(); uint64_t lsb = ((w64Bits & 0x01) == 1) ? 1 : 0; w64Bits >>= 1; // RShift by 1 - w64Bits |= (lsb << (m_dwWordBitWidth - 1)); + + if (op == IDC_ROR) + { + w64Bits |= (lsb << (m_dwWordBitWidth - 1)); + } + else + { + w64Bits |= (m_carryBit << (m_dwWordBitWidth - 1)); + m_carryBit = lsb; + } result = w64Bits; } @@ -134,6 +151,48 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r } break; + case IDC_SEC: + if (!m_fIntegerMode) + { + result = m_bInv ? ACos(Invert(rat), m_angletype) : Invert(Cos(rat, m_angletype)); + } + break; + + case IDC_CSC: + if (!m_fIntegerMode) + { + result = m_bInv ? ASin(Invert(rat), m_angletype) : Invert(Sin(rat, m_angletype)); + } + break; + + case IDC_COT: + if (!m_fIntegerMode) + { + result = m_bInv ? ATan(Invert(rat), m_angletype) : Invert(Tan(rat, m_angletype)); + } + break; + + case IDC_SECH: + if (!m_fIntegerMode) + { + result = m_bInv ? ACosh(Invert(rat)) : Invert(Cosh(rat)); + } + break; + + case IDC_CSCH: + if (!m_fIntegerMode) + { + result = m_bInv ? ASinh(Invert(rat)) : Invert(Sinh(rat)); + } + break; + + case IDC_COTH: + if (!m_fIntegerMode) + { + result = m_bInv ? ATanh(Invert(rat)) : Invert(Tanh(rat)); + } + break; + case IDC_REC: /* Reciprocal. */ result = Invert(rat); break; @@ -159,6 +218,10 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r result = Pow(10, rat); break; + case IDC_POW2: + result = Pow(2, rat); + break; + case IDC_LN: /* Functions for natural log. */ result = m_bInv ? Exp(rat) : Log(rat); break; @@ -169,9 +232,9 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r case IDC_DEGREES: ProcessCommand(IDC_INV); - // This case falls through to IDC_DMS case because in the old Win32 Calc, + // This case falls through to IDC_DMS case because in the old Win32 Calc, // the degrees functionality was achieved as 'Inv' of 'dms' operation, - // so setting the IDC_INV command first and then performing 'dms' operation as global variables m_bInv, m_bRecord + // so setting the IDC_INV command first and then performing 'dms' operation as global variables m_bInv, m_bRecord // are set properly through ProcessCommand(IDC_INV) [[fallthrough]]; case IDC_DMS: @@ -203,9 +266,21 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r } break; } - } // end switch( op ) + case IDC_CEIL: + result = (Frac(rat) > 0) ? Integer(rat + 1) : Integer(rat); + break; + + case IDC_FLOOR: + result = (Frac(rat) < 0) ? Integer(rat - 1 ) : Integer(rat); + break; + + case IDC_ABS: + result = Abs(rat); + break; + + } // end switch( op ) } - catch (DWORD nErrCode) + catch (uint32_t nErrCode) { DisplayError(nErrCode); result = rat; @@ -215,9 +290,9 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r } /* Routine to display error messages and set m_bError flag. Errors are */ -/* called with DisplayError (n), where n is a DWORD between 0 and 5. */ +/* called with DisplayError (n), where n is a uint32_t between 0 and 5. */ -void CCalcEngine::DisplayError(DWORD nError) +void CCalcEngine::DisplayError(uint32_t nError) { wstring errorString{ GetString(IDS_ERRORS_FIRST + SCODE_CODE(nError)) }; @@ -227,4 +302,3 @@ void CCalcEngine::DisplayError(DWORD nError) m_HistoryCollector.ClearHistoryLine(errorString); } - diff --git a/src/CalcManager/CEngine/scioper.cpp b/src/CalcManager/CEngine/scioper.cpp index 06fe3450..fdb9a7c0 100644 --- a/src/CalcManager/CEngine/scioper.cpp +++ b/src/CalcManager/CEngine/scioper.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace CalcEngine; @@ -29,6 +28,14 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa result ^= rhs; break; + case IDC_NAND: + result = (result & rhs) ^ GetChopNumber(); + break; + + case IDC_NOR: + result = (result | rhs) ^ GetChopNumber(); + break; + case IDC_RSHF: { if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0 @@ -46,14 +53,23 @@ 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; } + case IDC_RSHFL: + { + if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0 + { + throw CALC_E_NORESULT; + } + result = rhs >> result; + break; + } case IDC_LSHF: if (m_fIntegerMode && result >= m_dwWordBitWidth) // Lsh/Rsh >= than current word size is always 0 { @@ -78,7 +94,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa case IDC_DIV: case IDC_MOD: { - int iNumeratorSign = 1, iDenominatorSign = 1, iFinalSign = 1; + int iNumeratorSign = 1, iDenominatorSign = 1; auto temp = result; result = rhs; @@ -89,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; } @@ -99,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; } @@ -107,20 +123,30 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa if (operation == IDC_DIV) { - iFinalSign = iNumeratorSign * iDenominatorSign; result /= temp; + if (m_fIntegerMode && (iNumeratorSign * iDenominatorSign) == -1) + { + result = -(Integer(result)); + } } else { - iFinalSign = iNumeratorSign; - result %= temp; - } + if (m_fIntegerMode) + { + // Programmer mode, use remrat (remainder after division) + result %= temp; - if (m_fIntegerMode && iFinalSign == -1) - { - result = -(Integer(result)); + if (iNumeratorSign == -1) + { + result = -(Integer(result)); + } + } + else + { + // other modes, use modrat (modulus after division) + result = Mod(result, temp); + } } - break; } @@ -131,9 +157,13 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa case IDC_ROOT: // Calculates rhs to the result(th) root. result = Root(rhs, result); break; + + case IDC_LOGBASEY: + result = (Log(rhs) / Log(result)); + break; } } - catch (DWORD dwErrCode) + catch (uint32_t dwErrCode) { DisplayError(dwErrCode); diff --git a/src/CalcManager/CEngine/sciset.cpp b/src/CalcManager/CEngine/sciset.cpp index 99f0b0b6..cb143559 100644 --- a/src/CalcManager/CEngine/sciset.cpp +++ b/src/CalcManager/CEngine/sciset.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" #include "Header Files/CalcEngine.h" using namespace CalcEngine; @@ -10,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 @@ -25,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); @@ -51,35 +50,42 @@ void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwid DisplayNum(); } -LONG CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH /*numwidth*/) +int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth) { - static constexpr int nBitMax[] = { 64, 32, 16, 8 }; - LONG wmax = nBitMax[0]; - - if (m_numwidth >= 0 && 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 && 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. -bool CCalcEngine::TryToggleBit(CalcEngine::Rational& rat, DWORD wbitno) +bool CCalcEngine::TryToggleBit(CalcEngine::Rational& rat, uint32_t wbitno) { - DWORD wmax = DwWordBitWidthFromeNumWidth(m_numwidth); + uint32_t wmax = DwWordBitWidthFromeNumWidth(m_numwidth); if (wbitno >= wmax) { return false; // ignore error cant happen @@ -142,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(m_maxDecimalValueStrings[m_numwidth].length()) - 1; + m_cIntDigitsSav = static_cast(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 } diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index 4a098141..80940c6a 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -45,7 +45,7 @@ true Windows Store 10.0 - 10.0.17763.0 + 10.0.18362.0 10.0.17134.0 @@ -56,46 +56,54 @@ StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary false true - v141 + v142 + NativeRecommendedRules.ruleset + true StaticLibrary false true - v141 + v142 + NativeRecommendedRules.ruleset + true StaticLibrary false true - v141 + v142 + NativeRecommendedRules.ruleset + true StaticLibrary false true - v141 + v142 + NativeRecommendedRules.ruleset + true @@ -124,39 +132,20 @@ - - false - - - false - - - false - - - false - - - false - - - false - - - false - - + false + true Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -169,10 +158,12 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h + true Console @@ -185,10 +176,11 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -201,10 +193,12 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h + true Console @@ -217,11 +211,12 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) _UNICODE;UNICODE;%(PreprocessorDefinitions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -234,10 +229,12 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h + true Console @@ -250,10 +247,11 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h Console @@ -266,10 +264,12 @@ Use false true - /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- %(AdditionalOptions) + /Zm250 /await /std:c++17 /permissive- /Zc:twoPhase- /utf-8 %(AdditionalOptions) $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 true + pch.h + true Console @@ -278,11 +278,9 @@ - - @@ -302,6 +300,7 @@ + @@ -342,8 +341,9 @@ Create Create + - \ No newline at end of file + diff --git a/src/CalcManager/CalcManager.vcxproj.filters b/src/CalcManager/CalcManager.vcxproj.filters index 2ca11666..ad7670a0 100644 --- a/src/CalcManager/CalcManager.vcxproj.filters +++ b/src/CalcManager/CalcManager.vcxproj.filters @@ -89,6 +89,7 @@ CEngine + @@ -119,7 +120,6 @@ RatPack - Header Files @@ -160,5 +160,6 @@ Header Files + - + \ No newline at end of file diff --git a/src/CalcManager/CalculatorHistory.cpp b/src/CalcManager/CalculatorHistory.cpp index 6b18a8e8..467cd420 100644 --- a/src/CalcManager/CalculatorHistory.cpp +++ b/src/CalcManager/CalculatorHistory.cpp @@ -1,61 +1,75 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "CalculatorHistory.h" using namespace std; using namespace CalculationManager; -CalculatorHistory::CalculatorHistory(CALCULATOR_MODE eMode, size_t maxSize) : - m_mode(eMode), - m_maxHistorySize(maxSize) -{} - -unsigned int CalculatorHistory::AddToHistory(_In_ shared_ptr>> const &tokens, _In_ shared_ptr>> const &commands, _In_ wstring_view result) +namespace +{ + static wstring GetGeneratedExpression(const vector>& tokens) + { + wstring expression; + bool isFirst = true; + + for (auto const& token : tokens) + { + if (isFirst) + { + isFirst = false; + } + else + { + expression += L' '; + } + expression.append(token.first); + } + + return expression; + } +} + +CalculatorHistory::CalculatorHistory(size_t maxSize) + : m_maxHistorySize(maxSize) +{ +} + +unsigned int CalculatorHistory::AddToHistory( + _In_ shared_ptr>> const& tokens, + _In_ shared_ptr>> const& commands, + wstring_view result) { - unsigned int addedIndex; - wstring generatedExpression; shared_ptr spHistoryItem = make_shared(); spHistoryItem->historyItemVector.spTokens = tokens; spHistoryItem->historyItemVector.spCommands = commands; - - // to be changed when pszexp is back - tokens->GetString(&generatedExpression); - // 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 const &spHistoryItem) +unsigned int CalculatorHistory::AddItem(_In_ shared_ptr const& spHistoryItem) { - int lastIndex; - if (m_historyItems.size() >= m_maxHistorySize) { m_historyItems.erase(m_historyItems.begin()); } m_historyItems.push_back(spHistoryItem); - lastIndex = static_cast(m_historyItems.size() - 1); - return lastIndex; + return static_cast(m_historyItems.size() - 1); } -bool CalculatorHistory::RemoveItem(_In_ unsigned int uIdx) +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; } - m_historyItems.erase(m_historyItems.begin() + uIdx); - return true; + return false; } vector> const& CalculatorHistory::GetHistory() @@ -63,17 +77,12 @@ vector> const& CalculatorHistory::GetHistory() return m_historyItems; } -shared_ptr const& CalculatorHistory::GetHistoryItem(_In_ unsigned int uIdx) +shared_ptr const& CalculatorHistory::GetHistoryItem(unsigned int uIdx) { assert(uIdx >= 0 && uIdx < m_historyItems.size()); return m_historyItems.at(uIdx); } -CalculatorHistory::~CalculatorHistory(void) -{ - ClearHistory(); -} - void CalculatorHistory::ClearHistory() { m_historyItems.clear(); diff --git a/src/CalcManager/CalculatorHistory.h b/src/CalcManager/CalculatorHistory.h index aececbda..2c849906 100644 --- a/src/CalcManager/CalculatorHistory.h +++ b/src/CalcManager/CalculatorHistory.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once @@ -7,18 +7,12 @@ namespace CalculationManager { - enum CALCULATOR_MODE - { - CM_STD = 0, - CM_SCI, - }; - struct HISTORYITEMVECTOR { - std::shared_ptr>> spTokens; - std::shared_ptr>> spCommands; - std::wstring expression; - std::wstring result; + std::shared_ptr>> spTokens; + std::shared_ptr>> spCommands; + std::wstring expression; + std::wstring result; }; struct HISTORYITEM @@ -26,24 +20,26 @@ namespace CalculationManager HISTORYITEMVECTOR historyItemVector; }; - class CalculatorHistory : - public IHistoryDisplay + class CalculatorHistory : public IHistoryDisplay { - public: - CalculatorHistory(CALCULATOR_MODE eMode, const size_t maxSize); - unsigned int AddToHistory(_In_ std::shared_ptr>> const &spTokens, _In_ std::shared_ptr>> const &spCommands, std::wstring_view result); + CalculatorHistory(const size_t maxSize); + unsigned int AddToHistory( + _In_ std::shared_ptr>> const& spTokens, + _In_ std::shared_ptr>> const& spCommands, + std::wstring_view result); std::vector> const& GetHistory(); std::shared_ptr const& GetHistoryItem(unsigned int uIdx); void ClearHistory(); - unsigned int AddItem(_In_ std::shared_ptr const &spHistoryItem); + unsigned int AddItem(_In_ std::shared_ptr const& spHistoryItem); bool RemoveItem(unsigned int uIdx); - const size_t MaxHistorySize() const { return m_maxHistorySize; } - ~CalculatorHistory(void); + size_t MaxHistorySize() const + { + return m_maxHistorySize; + } private: std::vector> m_historyItems; - CALCULATOR_MODE m_mode; const size_t m_maxHistorySize; }; } diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index 0f6ccd49..e26ad4f8 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include // for UCHAR_MAX #include "Header Files/CalcEngine.h" #include "CalculatorManager.h" #include "CalculatorResource.h" @@ -10,42 +10,28 @@ using namespace std; using namespace CalcEngine; static constexpr size_t MAX_HISTORY_ITEMS = 20; -static constexpr size_t SERIALIZED_NUMBER_MINSIZE = 3; -// 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(c)\ - __pragma(warning(pop)) +#ifndef _MSC_VER +#define __pragma(x) +#endif namespace CalculationManager { - CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider) : - m_displayCallback(displayCallback), - m_resourceProvider(resourceProvider), - m_currentDegreeMode(Command::CommandNULL), - m_savedDegreeMode(Command::CommandDEG), - m_isExponentialFormat(false), - m_persistedPrimaryValue(), - m_currentCalculatorEngine(nullptr), - m_pStdHistory(new CalculatorHistory(CM_STD, MAX_HISTORY_ITEMS)), - m_pSciHistory(new CalculatorHistory(CM_SCI, MAX_HISTORY_ITEMS)), - m_inHistoryItemLoadMode(false) + CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider) + : m_displayCallback(displayCallback) + , m_currentCalculatorEngine(nullptr) + , m_resourceProvider(resourceProvider) + , m_inHistoryItemLoadMode(false) + , m_persistedPrimaryValue() + , m_isExponentialFormat(false) + , m_currentDegreeMode(Command::CommandNULL) + , m_pStdHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) + , m_pSciHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) + , m_pHistory(nullptr) { CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider); } - /// - /// Destructor for CalculatorManager - /// Ends two CCalcEngine - /// - CalculatorManager::~CalculatorManager() - { - this->MemorizedNumberClearAll(); - } - /// /// Call the callback function using passed in IDisplayHelper. /// Used to set the primary display value on ViewModel @@ -84,12 +70,19 @@ namespace CalculationManager m_displayCallback->MemoryItemChanged(indexOfMemory); } + void CalculatorManager::InputChanged() + { + m_displayCallback->InputChanged(); + } + /// /// Call the callback function using passed in IDisplayHelper. /// Used to set the expression display value on ViewModel /// /// wstring representing expression to be displayed - void CalculatorManager::SetExpressionDisplay(_Inout_ shared_ptr>> const &tokens, _Inout_ shared_ptr>> const &commands) + void CalculatorManager::SetExpressionDisplay( + _Inout_ shared_ptr>> const& tokens, + _Inout_ shared_ptr>> const& commands) { if (!m_inHistoryItemLoadMode) { @@ -111,9 +104,9 @@ namespace CalculationManager /// Callback from the engine /// /// string containing the parenthesis count - void CalculatorManager::SetParenDisplayText(const wstring& parenthesisCount) + void CalculatorManager::SetParenthesisNumber(_In_ unsigned int parenthesisCount) { - m_displayCallback->SetParenDisplayText(parenthesisCount); + m_displayCallback->SetParenthesisNumber(parenthesisCount); } /// @@ -133,7 +126,6 @@ namespace CalculationManager /// void CalculatorManager::Reset(bool clearMemory /* = true*/) { - m_savedCommands.clear(); SetStandardMode(); if (m_scientificCalculatorEngine) @@ -165,7 +157,8 @@ namespace CalculationManager { if (!m_standardCalculatorEngine) { - m_standardCalculatorEngine = make_unique(false /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pStdHistory); + m_standardCalculatorEngine = + make_unique(false /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pStdHistory); } m_currentCalculatorEngine = m_standardCalculatorEngine.get(); @@ -183,7 +176,8 @@ namespace CalculationManager { if (!m_scientificCalculatorEngine) { - m_scientificCalculatorEngine = make_unique(true /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pSciHistory); + m_scientificCalculatorEngine = + make_unique(true /* Respect Order of Operations */, false /* Set to Integer Mode */, m_resourceProvider, this, m_pSciHistory); } m_currentCalculatorEngine = m_scientificCalculatorEngine.get(); @@ -198,9 +192,10 @@ namespace CalculationManager /// void CalculatorManager::SetProgrammerMode() { - if(!m_programmerCalculatorEngine) + if (!m_programmerCalculatorEngine) { - m_programmerCalculatorEngine = make_unique(true /* Respect Order of Operations */, true /* Set to Integer Mode */, m_resourceProvider, this, nullptr); + m_programmerCalculatorEngine = + make_unique(true /* Respect Order of Operations */, true /* Set to Integer Mode */, m_resourceProvider, this, nullptr); } m_currentCalculatorEngine = m_programmerCalculatorEngine.get(); @@ -209,10 +204,9 @@ namespace CalculationManager m_currentCalculatorEngine->ChangePrecision(static_cast(CalculatorPrecision::ProgrammerModePrecision)); } - /// /// Send command to the Calc Engine - /// Cast Command Enum to WPARAM. + /// Cast Command Enum to OpCode. /// Handle special commands such as mode change and combination of two commands. /// /// Enum Command @@ -220,8 +214,8 @@ namespace CalculationManager { // When the expression line is cleared, we save the current state, which includes, // primary display, memory, and degree mode - if (command == Command::CommandCLEAR || command == Command::CommandEQU - || command == Command::ModeBasic || command == Command::ModeScientific || command == Command::ModeProgrammer) + if (command == Command::CommandCLEAR || command == Command::CommandEQU || command == Command::ModeBasic || command == Command::ModeScientific + || command == Command::ModeProgrammer) { switch (command) { @@ -235,18 +229,10 @@ namespace CalculationManager this->SetProgrammerMode(); break; default: - m_currentCalculatorEngine->ProcessCommand(static_cast(command)); + m_currentCalculatorEngine->ProcessCommand(static_cast(command)); } - m_savedCommands.clear(); // Clear the previous command history - - if (command != Command::CommandEQU && command != Command::CommandCLEAR) - { - m_savedCommands.push_back(MapCommandForSerialize(command)); - } - this->SerializePrimaryDisplay(); - this->SerializeMemory(); - m_savedDegreeMode = m_currentDegreeMode; + InputChanged(); return; } @@ -255,117 +241,69 @@ 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: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSIN)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSIN)); break; case Command::CommandACOS: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOS)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOS)); break; case Command::CommandATAN: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTAN)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTAN)); break; case Command::CommandPOWE: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandLN)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandLN)); break; case Command::CommandASINH: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSINH)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSINH)); break; case Command::CommandACOSH: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOSH)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOSH)); break; case Command::CommandATANH: - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); - m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTANH)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandTANH)); + break; + case Command::CommandASEC: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSEC)); + break; + case Command::CommandACSC: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCSC)); + break; + case Command::CommandACOT: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOT)); + break; + case Command::CommandASECH: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandSECH)); + break; + case Command::CommandACSCH: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCSCH)); + break; + case Command::CommandACOTH: + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandINV)); + m_currentCalculatorEngine->ProcessCommand(static_cast(Command::CommandCOTH)); break; case Command::CommandFE: m_isExponentialFormat = !m_isExponentialFormat; - // fall through + [[fallthrough]]; default: - m_currentCalculatorEngine->ProcessCommand(static_cast(command)); + m_currentCalculatorEngine->ProcessCommand(static_cast(command)); break; } - } - /// - /// 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. - /// - /// Enum Command - unsigned char CalculatorManager::MapCommandForSerialize(Command command) - { - unsigned int commandToSave = static_cast(command); - commandToSave > UCHAR_MAX ? commandToSave -= UCHAR_MAX : commandToSave; - return static_cast(commandToSave); - } - - /// - /// Convert Command to unsigned int - /// The command that is smaller than 80, CommandSIGN, can be converted back to original value by adding 255. - /// - /// unsigned char value represent the saved command - unsigned int CalculatorManager::MapCommandForDeSerialize(unsigned char command) - { - unsigned int commandToLoad = command; - if (command < static_cast(Command::CommandSIGN)) - { - commandToLoad += UCHAR_MAX; - } - return commandToLoad; - } - - /// - /// Return saved degree mode which is saved when last time the expression was cleared. - /// - Command CalculatorManager::SerializeSavedDegreeMode() - { - return m_savedDegreeMode; - } - - void CalculatorManager::SerializePrimaryDisplay() - { - m_savedPrimaryValue.clear(); - m_currentCalculatorEngine->ProcessCommand(IDC_STORE); - auto memoryObject = m_currentCalculatorEngine->PersistedMemObject(); - if (memoryObject != nullptr) - { - m_savedPrimaryValue = SerializeRational(*memoryObject); - } - } - - /// - /// Return serialized primary display that is saved when the expression line was cleared. - /// - vector CalculatorManager::GetSerializedPrimaryDisplay() - { - return m_savedPrimaryValue; - } - - /// - /// DeSerialize the primary display from vector of long - /// - /// Serialized Rational of primary display - void CalculatorManager::DeSerializePrimaryDisplay(const vector &serializedPrimaryDisplay) - { - if (serializedPrimaryDisplay.size() == 0) - { - return; - } - m_persistedPrimaryValue = DeSerializeRational(serializedPrimaryDisplay.begin()); - this->LoadPersistedPrimaryValue(); + InputChanged(); } /// @@ -375,112 +313,7 @@ namespace CalculationManager { m_currentCalculatorEngine->PersistedMemObject(m_persistedPrimaryValue); m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); - } - - /// - /// Serialize the Memory to vector of long - /// - /// Serialized Rational of memory - void CalculatorManager::SerializeMemory() - { - m_serializedMemory.clear(); - - for (auto const& memoryItem : m_memorizedNumbers) - { - auto serialMem = SerializeRational(memoryItem); - m_serializedMemory.insert(m_serializedMemory.end(), serialMem.begin(), serialMem.end()); - } - } - - vector CalculatorManager::GetSerializedMemory() - { - return m_serializedMemory; - } - - /// - /// DeSerialize the Memory from vector of long - /// - /// Serialized Rational of memory - void CalculatorManager::DeSerializeMemory(const vector &serializedMemory) - { - vector::const_iterator itr = serializedMemory.begin(); - while (itr != serializedMemory.end()) - { - Rational memoryItem = DeSerializeRational(itr); - auto lengthMemoryItem = (2 * SERIALIZED_NUMBER_MINSIZE) + memoryItem.P().Mantissa().size() + memoryItem.Q().Mantissa().size(); - m_memorizedNumbers.push_back(memoryItem); - itr += lengthMemoryItem; - } - this->SetMemorizedNumbersString(); - } - - /// - /// Return the commands saved since the expression has been cleared. - /// - vector CalculatorManager::SerializeCommands() - { - return m_savedCommands; - } - - /// - /// Replay the serialized commands - /// - /// Serialized commands - void CalculatorManager::DeSerializeCommands(_In_ const vector& serializedData) - { - m_savedCommands.clear(); - - for (auto commandItr = serializedData.begin(); commandItr != serializedData.end(); ++commandItr) - { - if (*commandItr >= MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber) && - *commandItr <= MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll)) - { - // MemoryCommands(which have values above 255) are pushed on m_savedCommands upon casting to unsigned char. - // SerializeCommands uses m_savedCommands, which is then used in DeSerializeCommands. - // Hence, a simple cast to MemoryCommand is not sufficient. - MemoryCommand memoryCommand = static_cast(*commandItr + UCHAR_MAX + 1); - unsigned int indexOfMemory = 0; - switch (memoryCommand) - { - case MemoryCommand::MemorizeNumber: - this->MemorizeNumber(); - break; - case MemoryCommand::MemorizedNumberLoad: - if (commandItr + 1 == serializedData.end()) - { - throw out_of_range("Expecting index of memory, data ended prematurely"); - } - indexOfMemory = *(++commandItr); - this->MemorizedNumberLoad(indexOfMemory); - break; - case MemoryCommand::MemorizedNumberAdd: - if (commandItr + 1 == serializedData.end()) - { - throw out_of_range("Expecting index of memory, data ended prematurely"); - } - indexOfMemory = *(++commandItr); - this->MemorizedNumberAdd(indexOfMemory); - break; - case MemoryCommand::MemorizedNumberSubtract: - if (commandItr + 1 == serializedData.end()) - { - throw out_of_range("Expecting index of memory, data ended prematurely"); - } - indexOfMemory = *(++commandItr); - this->MemorizedNumberSubtract(indexOfMemory); - break; - case MemoryCommand::MemorizedNumberClearAll: - this->MemorizedNumberClearAll(); - break; - default: - break; - } - } - else - { - this->SendCommand(static_cast(MapCommandForDeSerialize(*commandItr))); - } - } + InputChanged(); } /// @@ -489,23 +322,24 @@ namespace CalculationManager /// void CalculatorManager::MemorizeNumber() { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber)); - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - m_currentCalculatorEngine->ProcessCommand(IDC_STORE); - - auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject(); - if (memoryObjectPtr != nullptr) - { - m_memorizedNumbers.insert(m_memorizedNumbers.begin(), *memoryObjectPtr); - } - - if (m_memorizedNumbers.size() > m_maximumMemorySize) - { - m_memorizedNumbers.resize(m_maximumMemorySize); - } - this->SetMemorizedNumbersString(); + return; } + + m_currentCalculatorEngine->ProcessCommand(IDC_STORE); + + auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject(); + if (memoryObjectPtr != nullptr) + { + m_memorizedNumbers.insert(m_memorizedNumbers.begin(), *memoryObjectPtr); + } + + if (m_memorizedNumbers.size() > m_maximumMemorySize) + { + m_memorizedNumbers.resize(m_maximumMemorySize); + } + this->SetMemorizedNumbersString(); } /// @@ -515,12 +349,14 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory); - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - this->MemorizedNumberSelect(indexOfMemory); - m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); + return; } + + this->MemorizedNumberSelect(indexOfMemory); + m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); + InputChanged(); } /// @@ -531,32 +367,32 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory); - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - if (m_memorizedNumbers.empty()) - { - this->MemorizeNumber(); - } - else - { - this->MemorizedNumberSelect(indexOfMemory); - m_currentCalculatorEngine->ProcessCommand(IDC_MPLUS); - - this->MemorizedNumberChanged(indexOfMemory); - - this->SetMemorizedNumbersString(); - } - - m_displayCallback->MemoryItemChanged(indexOfMemory); + return; } + + if (m_memorizedNumbers.empty()) + { + this->MemorizeNumber(); + } + else + { + this->MemorizedNumberSelect(indexOfMemory); + m_currentCalculatorEngine->ProcessCommand(IDC_MPLUS); + + this->MemorizedNumberChanged(indexOfMemory); + + this->SetMemorizedNumbersString(); + } + + m_displayCallback->MemoryItemChanged(indexOfMemory); } void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory) { if (indexOfMemory < m_memorizedNumbers.size()) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberClear, indexOfMemory); m_memorizedNumbers.erase(m_memorizedNumbers.begin() + indexOfMemory); } } @@ -569,28 +405,29 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory); - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - // To add negative of the number on display to the memory -x = x - 2x - if (m_memorizedNumbers.empty()) - { - this->MemorizeNumber(); - this->MemorizedNumberSubtract(0); - this->MemorizedNumberSubtract(0); - } - else - { - this->MemorizedNumberSelect(indexOfMemory); - m_currentCalculatorEngine->ProcessCommand(IDC_MMINUS); - - this->MemorizedNumberChanged(indexOfMemory); - - this->SetMemorizedNumbersString(); - } - - m_displayCallback->MemoryItemChanged(indexOfMemory); + return; } + + // To add negative of the number on display to the memory -x = x - 2x + if (m_memorizedNumbers.empty()) + { + this->MemorizeNumber(); + this->MemorizedNumberSubtract(0); + this->MemorizedNumberSubtract(0); + } + else + { + this->MemorizedNumberSelect(indexOfMemory); + m_currentCalculatorEngine->ProcessCommand(IDC_MMINUS); + + this->MemorizedNumberChanged(indexOfMemory); + + this->SetMemorizedNumbersString(); + } + + m_displayCallback->MemoryItemChanged(indexOfMemory); } /// @@ -599,7 +436,6 @@ namespace CalculationManager /// void CalculatorManager::MemorizedNumberClearAll() { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll)); m_memorizedNumbers.clear(); m_currentCalculatorEngine->ProcessCommand(IDC_MCLEAR); @@ -607,17 +443,19 @@ namespace CalculationManager } /// -/// Helper function that selects a memory from the vector and set it to CCalcEngine -/// Saved RAT number needs to be copied and passed in, as CCalcEngine destroyed the passed in RAT -/// -/// Index of the target memory + /// Helper function that selects a memory from the vector and set it to CCalcEngine + /// Saved RAT number needs to be copied and passed in, as CCalcEngine destroyed the passed in RAT + /// + /// Index of the target memory void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory) { - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - auto memoryObject = m_memorizedNumbers.at(indexOfMemory); - m_currentCalculatorEngine->PersistedMemObject(memoryObject); + return; } + + auto memoryObject = m_memorizedNumbers.at(indexOfMemory); + m_currentCalculatorEngine->PersistedMemObject(memoryObject); } /// @@ -627,24 +465,16 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory) { - if (!(m_currentCalculatorEngine->FInErrorState())) + if (m_currentCalculatorEngine->FInErrorState()) { - auto memoryObject = m_currentCalculatorEngine->PersistedMemObject(); - if (memoryObject != nullptr) - { - m_memorizedNumbers.at(indexOfMemory) = *memoryObject; - } + return; } - } - void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory) - { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(command)); - if (indexOfMemory > UCHAR_MAX) + auto memoryObject = m_currentCalculatorEngine->PersistedMemObject(); + if (memoryObject != nullptr) { - throw invalid_argument("Unexpected value. IndexOfMemory is bigger than the biggest unsigned char"); + m_memorizedNumbers.at(indexOfMemory) = *memoryObject; } - m_savedCommands.push_back(static_cast(indexOfMemory)); } vector> const& CalculatorManager::GetHistoryItems() @@ -652,11 +482,9 @@ namespace CalculationManager return m_pHistory->GetHistory(); } - vector> const& CalculatorManager::GetHistoryItems(_In_ CALCULATOR_MODE mode) + vector> 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 const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx) @@ -679,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: @@ -726,33 +554,9 @@ namespace CalculationManager return m_currentDegreeMode; } - void CalculatorManager::SetHistory(_In_ CALCULATOR_MODE eMode, _In_ vector> const& history) + wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { - 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 (unsigned int i = 0; i < history.size(); ++i) - { - pHistory->AddItem(history[i]); - } - } - } - - wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision) - { - return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision) : L""; + return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L""; } void CalculatorManager::SetPrecision(int32_t precision) @@ -772,103 +576,16 @@ namespace CalculationManager bool CalculatorManager::IsEngineRecording() { - return m_currentCalculatorEngine->FInRecordingState() ? true : false; + return m_currentCalculatorEngine->FInRecordingState(); + } + + bool CalculatorManager::IsInputEmpty() + { + return m_currentCalculatorEngine->IsInputEmpty(); } void CalculatorManager::SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode) { m_inHistoryItemLoadMode = isHistoryItemLoadMode; } - - /// - /// Serialize Rational to vector of long - /// How Rational is serialized : - /// Serialized Rational.P(Number) + Serialized Rational.Q(Number) - /// How Number is saved : - /// [0] = Rational.P.Sign - /// [1] = Rational.P.Mantissa.size - /// [2] = Rational.P.Exp - /// [3] = Rational.P.Mantissa[0] - /// [4] = Rational.P.Mantissa[1] - /// ... - /// [2 + Rational.P.Mantissa.size] = Rational.P.Mantissa[size - 1] - /// - /// Rational number to be serialized - vector CalculatorManager::SerializeRational(Rational const& rat) - { - vector serializedRational{}; - - auto serialP = SerializeNumber(rat.P()); - serializedRational.insert(serializedRational.end(), serialP.begin(), serialP.end()); - - auto serialQ = SerializeNumber(rat.Q()); - serializedRational.insert(serializedRational.end(), serialQ.begin(), serialQ.end()); - - return serializedRational; - } - - /// - /// DeserializeRational vector and construct a Rational - /// How Rational is serialized : - /// Serialized Rational.P(Number) + Serialized Rational.Q(Number) - /// - Rational CalculatorManager::DeSerializeRational(vector::const_iterator itr) - { - auto p = DeSerializeNumber(itr); - auto q = DeSerializeNumber(itr + SERIALIZED_NUMBER_MINSIZE + p.Mantissa().size()); - - return Rational(p, q); - } - - /// - /// Serialize Number to vector of long - /// How Number is saved : - /// [0] = Number.Sign - /// [1] = Number.Mantissa.size - /// [2] = Number.Exp - /// [3] = Number.Mantissa[0] - /// [4] = Number.Mantissa[1] - /// ... - /// [2 + Number.Mantissa.size] = Number.Mantissa[size - 1] - /// - /// Number to be serialized - vector CalculatorManager::SerializeNumber(Number const& num) - { - vector serializedNumber{}; - - serializedNumber.push_back(num.Sign()); - serializedNumber.push_back(static_cast(num.Mantissa().size())); - serializedNumber.push_back(num.Exp()); - for (auto const& digit : num.Mantissa()) - { - serializedNumber.push_back(digit); - } - - return serializedNumber; - } - - /// - /// DeserializeNumber vector and construct a Number - /// How Number is saved : - /// [0] = Number.Sign - /// [1] = Number.Mantissa.size - /// [2] = Number.Exp - /// [3] = Number.Mantissa[0] - /// [4] = Number.Mantissa[1] - /// ... - /// [2 + Number.Mantissa.size] = Number.Mantissa[size - 1] - /// - Number CalculatorManager::DeSerializeNumber(vector::const_iterator itr) - { - int32_t sign = *itr; - uint32_t size = *(itr + 1); - int32_t exp = *(itr + 2); - vector mant{}; - for (size_t i = 0; i < size; ++i) - { - mant.emplace_back(*(itr + 3 + i)); - } - - return Number{ sign, exp, mant }; - } } diff --git a/src/CalcManager/CalculatorManager.h b/src/CalcManager/CalculatorManager.h index 69f4f75c..88a8457b 100644 --- a/src/CalcManager/CalculatorManager.h +++ b/src/CalcManager/CalculatorManager.h @@ -15,9 +15,8 @@ namespace CalculationManager enum class CalculatorMode { - StandardMode, - ScientificMode, - ProgrammerMode, + Standard = 0, + Scientific, }; enum class CalculatorPrecision @@ -42,9 +41,10 @@ namespace CalculationManager MemorizedNumberClear = 335 }; - class CalculatorManager sealed : public ICalcDisplay + class CalculatorManager final : public ICalcDisplay { private: + static const unsigned int m_maximumMemorySize = 100; ICalcDisplay* const m_displayCallback; CCalcEngine* m_currentCalculatorEngine; std::unique_ptr m_scientificCalculatorEngine; @@ -55,34 +55,14 @@ namespace CalculationManager std::vector m_memorizedNumbers; CalcEngine::Rational m_persistedPrimaryValue; - bool m_isExponentialFormat; - - static const unsigned int m_maximumMemorySize = 100; - - // For persistence - std::vector m_savedCommands; - std::vector m_savedPrimaryValue; - std::vector m_serializedMemory; - std::vector 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); void LoadPersistedPrimaryValue(); - static std::vector SerializeRational(CalcEngine::Rational const& rat); - static CalcEngine::Rational DeSerializeRational(std::vector::const_iterator itr); - - static std::vector SerializeNumber(CalcEngine::Number const& num); - static CalcEngine::Number DeSerializeNumber(std::vector::const_iterator itr); - std::shared_ptr m_pStdHistory; std::shared_ptr m_pSciHistory; CalculatorHistory* m_pHistory; @@ -91,34 +71,25 @@ namespace CalculationManager // ICalcDisplay void SetPrimaryDisplay(_In_ const std::wstring& displayString, _In_ bool isError) override; void SetIsInError(bool isError) override; - void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) override; + void SetExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands) override; void SetMemorizedNumbers(_In_ const std::vector& memorizedNumbers) override; void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override; - void SetParenDisplayText(const std::wstring& parenthesisCount) override; + void SetParenthesisNumber(_In_ unsigned int parenthesisCount) override; void OnNoRightParenAdded() override; void DisplayPasteError(); void MaxDigitsReached() override; void BinaryOperatorReceived() override; void MemoryItemChanged(unsigned int indexOfMemory) override; - - - CalculatorManager(ICalcDisplay* displayCallback, IResourceProvider* resourceProvider); - ~CalculatorManager(); + void InputChanged() override; + CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider); void Reset(bool clearMemory = true); void SetStandardMode(); void SetScientificMode(); void SetProgrammerMode(); void SendCommand(_In_ Command command); - std::vector SerializeCommands(); - void DeSerializeCommands(_In_ const std::vector& serializedData); - void SerializeMemory(); - std::vector GetSerializedMemory(); - void DeSerializeMemory(const std::vector &serializedMemory); - void SerializePrimaryDisplay(); - std::vector GetSerializedPrimaryDisplay(); - void DeSerializePrimaryDisplay(const std::vector &serializedPrimaryDisplay); - Command SerializeSavedDegreeMode(); void MemorizeNumber(); void MemorizedNumberLoad(_In_ unsigned int); @@ -128,22 +99,24 @@ namespace CalculationManager void MemorizedNumberClearAll(); bool IsEngineRecording(); - std::vector GetSavedCommands(){ return m_savedCommands; } - void SetRadix(RADIX_TYPE iRadixType); + bool IsInputEmpty(); + void SetRadix(RadixType iRadixType); void SetMemorizedNumbersString(); - std::wstring GetResultForRadix(uint32_t radix, int32_t precision); + std::wstring GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); void SetPrecision(int32_t precision); void UpdateMaxIntDigits(); wchar_t DecimalSeparator(); std::vector> const& GetHistoryItems(); - std::vector> const& GetHistoryItems(_In_ CalculationManager::CALCULATOR_MODE mode); + std::vector> const& GetHistoryItems(_In_ CalculatorMode mode); std::shared_ptr const& GetHistoryItem(_In_ unsigned int uIdx); bool RemoveHistoryItem(_In_ unsigned int uIdx); void ClearHistory(); - const size_t MaxHistorySize() const { return m_pHistory->MaxHistorySize(); } + size_t MaxHistorySize() const + { + return m_pHistory->MaxHistorySize(); + } CalculationManager::Command GetCurrentDegreeMode(); - void SetHistory(_In_ CALCULATOR_MODE eMode, _In_ std::vector> const& history); void SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode); }; } diff --git a/src/CalcManager/CalculatorResource.h b/src/CalcManager/CalculatorResource.h index 3493234a..8c4c049c 100644 --- a/src/CalcManager/CalculatorResource.h +++ b/src/CalcManager/CalculatorResource.h @@ -1,14 +1,18 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include + namespace CalculationManager { class IResourceProvider { public: - virtual ~IResourceProvider() { } + virtual ~IResourceProvider() + { + } // Should return a string from the resource table for strings used // by the calculation engine. The strings that must be defined @@ -17,6 +21,6 @@ namespace CalculationManager // ids "sDecimal", "sThousand" and "sGrouping". See // https://technet.microsoft.com/en-us/library/cc782655(v=ws.10).aspx // for what these values refer to. - virtual std::wstring GetCEngineString(const std::wstring& id) = 0; + virtual std::wstring GetCEngineString(std::wstring_view id) = 0; }; } diff --git a/src/CalcManager/CalculatorVector.h b/src/CalcManager/CalculatorVector.h index 7fad7e7d..4a139c8e 100644 --- a/src/CalcManager/CalculatorVector.h +++ b/src/CalcManager/CalculatorVector.h @@ -1,63 +1,66 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include +#include +#include "winerror_cross_platform.h" +#include "Ratpack/CalcErr.h" +#include // for std::out_of_range +#include "sal_cross_platform.h" // for SAL + template class CalculatorVector { public: - HRESULT GetAt(_In_opt_ unsigned int index, _Out_ TType *item) + ResultCode GetAt(_In_opt_ unsigned int index, _Out_ TType* item) { - HRESULT hr = S_OK; try { *item = m_vector.at(index); } catch (const std::out_of_range& /*ex*/) { - hr = E_BOUNDS; + return E_BOUNDS; } - return hr; + return S_OK; } - HRESULT GetSize(_Out_ unsigned int *size) + ResultCode GetSize(_Out_ unsigned int* size) { *size = static_cast(m_vector.size()); return S_OK; } - HRESULT SetAt(_In_ unsigned int index, _In_opt_ TType item) + ResultCode SetAt(_In_ unsigned int index, _In_opt_ TType item) { - HRESULT hr = S_OK; try { m_vector[index] = item; } catch (const std::out_of_range& /*ex*/) { - hr = E_BOUNDS; + return E_BOUNDS; } - return hr; + return S_OK; } - HRESULT RemoveAt(_In_ unsigned int index) + ResultCode RemoveAt(_In_ unsigned int index) { - HRESULT hr = S_OK; if (index < m_vector.size()) { m_vector.erase(m_vector.begin() + index); } else { - hr = E_BOUNDS; + return E_BOUNDS; } - return hr; + return S_OK; } - HRESULT InsertAt(_In_ unsigned int index, _In_ TType item) + ResultCode InsertAt(_In_ unsigned int index, _In_ TType item) { - HRESULT hr = S_OK; try { auto iter = m_vector.begin() + index; @@ -65,14 +68,13 @@ public: } catch (const std::bad_alloc& /*ex*/) { - hr = E_OUTOFMEMORY; + return E_OUTOFMEMORY; } - return hr; + return S_OK; } - HRESULT Truncate(_In_ unsigned int index) + ResultCode Truncate(_In_ unsigned int index) { - HRESULT hr = S_OK; if (index < m_vector.size()) { auto startIter = m_vector.begin() + index; @@ -80,45 +82,44 @@ public: } else { - hr = E_BOUNDS; + return E_BOUNDS; } - return hr; + return S_OK; } - HRESULT Append(_In_opt_ TType item) + ResultCode Append(_In_opt_ TType item) { - HRESULT hr = S_OK; try { m_vector.push_back(item); } catch (const std::bad_alloc& /*ex*/) { - hr = E_OUTOFMEMORY; + return E_OUTOFMEMORY; } - return hr; + return S_OK; } - HRESULT RemoveAtEnd() + ResultCode RemoveAtEnd() { m_vector.erase(--(m_vector.end())); return S_OK; } - HRESULT Clear() + ResultCode Clear() { m_vector.clear(); return S_OK; } - HRESULT GetString(_Out_ std::wstring* expression) + ResultCode GetString(_Out_ std::wstring* expression) { - HRESULT hr = S_OK; unsigned int nTokens = 0; - std::pair currentPair; - hr = this->GetSize(&nTokens); + ResultCode hr = this->GetSize(&nTokens); if (SUCCEEDED(hr)) { + + std::pair currentPair; for (unsigned int i = 0; i < nTokens; i++) { hr = this->GetAt(i, ¤tPair); @@ -144,7 +145,7 @@ public: return hr; } - HRESULT GetExpressionSuffix(_Out_ std::wstring* suffix) + ResultCode GetExpressionSuffix(_Out_ std::wstring* suffix) { *suffix = L" ="; return S_OK; diff --git a/src/CalcManager/Command.h b/src/CalcManager/Command.h index e6eeb8ac..ab095189 100644 --- a/src/CalcManager/Command.h +++ b/src/CalcManager/Command.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once @@ -7,9 +7,19 @@ namespace UnitConversionManager { enum class Command { - Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, + Zero, + One, + Two, + Three, + Four, + Five, + Six, + Seven, + Eight, + Nine, Decimal, - Negate, Backspace, + Negate, + Backspace, Clear, Reset, None @@ -20,9 +30,19 @@ namespace CurrencyConversionManager { enum class Command { - Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, + Zero, + One, + Two, + Three, + Four, + Five, + Six, + Seven, + Eight, + Nine, Decimal, - Negate, Backspace, + Negate, + Backspace, Clear, None }; @@ -49,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, @@ -73,10 +89,11 @@ namespace CalculationManager CommandROOT = 96, CommandPWR = 97, - CommandCHOP = 98, // Unary operators must be between CommandCHOP and CommandEQU + CommandCHOP = 98, // Unary operators must be between CommandCHOP and CommandEQU CommandROL = 99, CommandROR = 100, CommandCOM = 101, + CommandSIN = 102, CommandCOS = 103, CommandTAN = 104, @@ -85,7 +102,6 @@ namespace CalculationManager CommandCOSH = 106, CommandTANH = 107, - CommandLN = 108, CommandLOG = 109, CommandSQRT = 110, @@ -94,8 +110,8 @@ namespace CalculationManager CommandFAC = 113, CommandREC = 114, CommandDMS = 115, - CommandCUBEROOT = 116, // x ^ 1/3 - CommandPOW10 = 117, // 10 ^ x + CommandCUBEROOT = 116, // x ^ 1/3 + CommandPOW10 = 117, // 10 ^ x CommandPERCENT = 118, CommandFE = 119, @@ -113,7 +129,7 @@ namespace CalculationManager CommandOPENP = 128, CommandCLOSEP = 129, - Command0 = 130, // The controls for 0 through F must be consecutive and in order + Command0 = 130, // The controls for 0 through F must be consecutive and in order Command1 = 131, Command2 = 132, Command3 = 133, @@ -128,10 +144,38 @@ namespace CalculationManager CommandC = 142, CommandD = 143, CommandE = 144, - CommandF = 145, // this is last control ID which must match the string table + CommandF = 145, // this is last control ID which must match the string table CommandINV = 146, CommandSET_RESULT = 147, + CommandSEC = 400, + CommandASEC = 401, + CommandCSC = 402, + CommandACSC = 403, + CommandCOT = 404, + CommandACOT = 405, + + CommandSECH = 406, + CommandASECH = 407, + CommandCSCH = 408, + CommandACSCH = 409, + CommandCOTH = 410, + CommandACOTH = 411, + + CommandPOW2 = 412, // 2 ^ x + CommandAbs = 413, + CommandFloor = 414, + CommandCeil = 415, + CommandROLC = 416, + CommandRORC = 417, + CommandLogBaseY = 500, + CommandNand = 501, + CommandNor = 502, + + CommandRSHFL = 505, + CommandRand = 600, + CommandEuler = 601, + CommandAnd = 86, CommandOR = 87, CommandNot = 101, diff --git a/src/CalcManager/ExpressionCommand.cpp b/src/CalcManager/ExpressionCommand.cpp index 08ee293c..d574c9da 100644 --- a/src/CalcManager/ExpressionCommand.cpp +++ b/src/CalcManager/ExpressionCommand.cpp @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include #include "Header Files/CCommand.h" -#include "CalculatorVector.h" #include "ExpressionCommand.h" using namespace std; @@ -13,8 +12,10 @@ constexpr wchar_t chNegate = L'-'; constexpr wchar_t chExp = L'e'; constexpr wchar_t chPlus = L'+'; -CParentheses::CParentheses(_In_ int command) :m_command(command) -{} +CParentheses::CParentheses(_In_ int command) + : m_command(command) +{ +} int CParentheses::GetCommand() const { @@ -26,25 +27,25 @@ CalculationManager::CommandType CParentheses::GetCommandType() const return CalculationManager::CommandType::Parentheses; } -void CParentheses::Accept(_In_ ISerializeCommandVisitor &commandVisitor) +void CParentheses::Accept(_In_ ISerializeCommandVisitor& commandVisitor) { commandVisitor.Visit(*this); } CUnaryCommand::CUnaryCommand(int command) { - m_command = make_shared>(); - m_command->Append(command); + m_command = make_shared>(); + m_command->push_back(command); } CUnaryCommand::CUnaryCommand(int command1, int command2) { - m_command = make_shared>(); - m_command->Append(command1); - m_command->Append(command2); + m_command = make_shared>(); + m_command->push_back(command1); + m_command->push_back(command2); } -const shared_ptr> & CUnaryCommand::GetCommands() const +const shared_ptr>& CUnaryCommand::GetCommands() const { return m_command; } @@ -56,24 +57,26 @@ CalculationManager::CommandType CUnaryCommand::GetCommandType() const void CUnaryCommand::SetCommand(int command) { - m_command->Clear(); - m_command->Append(command); + m_command->clear(); + m_command->push_back(command); } void CUnaryCommand::SetCommands(int command1, int command2) { - m_command->Clear(); - m_command->Append(command1); - m_command->Append(command2); + m_command->clear(); + m_command->push_back(command1); + m_command->push_back(command2); } -void CUnaryCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor) +void CUnaryCommand::Accept(_In_ ISerializeCommandVisitor& commandVisitor) { commandVisitor.Visit(*this); } -CBinaryCommand::CBinaryCommand(int command) :m_command(command) -{} +CBinaryCommand::CBinaryCommand(int command) + : m_command(command) +{ +} void CBinaryCommand::SetCommand(int command) { @@ -90,19 +93,20 @@ CalculationManager::CommandType CBinaryCommand::GetCommandType() const return CalculationManager::CommandType::BinaryCommand; } -void CBinaryCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor) +void CBinaryCommand::Accept(_In_ ISerializeCommandVisitor& commandVisitor) { commandVisitor.Visit(*this); } -COpndCommand::COpndCommand(shared_ptr> const &commands, bool fNegative, bool fDecimal, bool fSciFmt) : - m_commands(commands), - m_fNegative(fNegative), - m_fDecimal(fDecimal), - m_fSciFmt(fSciFmt), - m_fInitialized(false), - m_value{} -{} +COpndCommand::COpndCommand(shared_ptr> const& commands, bool fNegative, bool fDecimal, bool fSciFmt) + : m_commands(commands) + , m_fNegative(fNegative) + , m_fSciFmt(fSciFmt) + , m_fDecimal(fDecimal) + , m_fInitialized(false) + , m_value{} +{ +} void COpndCommand::Initialize(Rational const& rat) { @@ -110,28 +114,27 @@ void COpndCommand::Initialize(Rational const& rat) m_fInitialized = true; } -const shared_ptr> & COpndCommand::GetCommands() const +const shared_ptr>& COpndCommand::GetCommands() const { return m_commands; } -void COpndCommand::SetCommands(shared_ptr> const& commands) +void COpndCommand::SetCommands(shared_ptr> const& commands) { m_commands = commands; } void COpndCommand::AppendCommand(int command) { - unsigned int nCommands; - m_commands->GetSize(&nCommands); if (m_fSciFmt) { ClearAllAndAppendCommand(static_cast(command)); } else { - m_commands->Append(command); + m_commands->push_back(command); } + if (command == IDC_PNT) { m_fDecimal = true; @@ -140,14 +143,8 @@ void COpndCommand::AppendCommand(int command) void COpndCommand::ToggleSign() { - unsigned int commandCount; - m_commands->GetSize(&commandCount); - - for (unsigned int i = 0; i < commandCount; i++) + for (int nOpCode : *m_commands) { - int nOpCode; - m_commands->GetAt(i, &nOpCode); - if (nOpCode != IDC_0) { m_fNegative = !m_fNegative; @@ -164,8 +161,7 @@ void COpndCommand::RemoveFromEnd() } else { - unsigned int nCommands; - m_commands->GetSize(&nCommands); + const size_t nCommands = m_commands->size(); if (nCommands == 1) { @@ -173,13 +169,14 @@ void COpndCommand::RemoveFromEnd() } else { - int nOpCode; - m_commands->GetAt(nCommands - 1, &nOpCode); + int nOpCode = m_commands->at(nCommands - 1); + if (nOpCode == IDC_PNT) { m_fDecimal = false; } - m_commands->RemoveAt(nCommands - 1); + + m_commands->pop_back(); } } } @@ -206,42 +203,40 @@ CalculationManager::CommandType COpndCommand::GetCommandType() const void COpndCommand::ClearAllAndAppendCommand(CalculationManager::Command command) { - m_commands->Clear(); - m_commands->Append(static_cast(command)); + m_commands->clear(); + m_commands->push_back(static_cast(command)); m_fSciFmt = false; m_fNegative = false; m_fDecimal = false; } -const wstring & COpndCommand::GetToken(wchar_t decimalSymbol) +const wstring& COpndCommand::GetToken(wchar_t decimalSymbol) { static const wchar_t chZero = L'0'; - unsigned int nCommands; - m_commands->GetSize(&nCommands); + const size_t nCommands = m_commands->size(); m_token.clear(); - int nOpCode; - for (unsigned int i = 0; i < nCommands; i++) + for (size_t i = 0; i < nCommands; i++) { - m_commands->GetAt(i, &nOpCode); + int nOpCode = (*m_commands)[i]; + if (nOpCode == IDC_PNT) { - m_token.append(wstring{ decimalSymbol }); + m_token += decimalSymbol; } else if (nOpCode == IDC_EXP) { - m_token.append(&chExp); - int nextOpCode; - m_commands->GetAt(i + 1, &nextOpCode); + m_token += chExp; + int nextOpCode = m_commands->at(i + 1); if (nextOpCode != IDC_SIGN) { - m_token.append(&chPlus); + m_token += chPlus; } } else if (nOpCode == IDC_SIGN) { - m_token.append(&chNegate); + m_token += chNegate; } else { @@ -251,55 +246,44 @@ const wstring & COpndCommand::GetToken(wchar_t decimalSymbol) } // Remove zeros - bool fDigitsFound = false; - int trimIdx = 0; - for (unsigned int i = 0; i < m_token.size(); i++) + for (size_t i = 0; i < m_token.size(); i++) { if (m_token.at(i) != chZero) { if (m_token.at(i) == decimalSymbol) { - trimIdx = i - 1; + m_token.erase(0, i - 1); } else { - trimIdx = i; + m_token.erase(0, i); } - fDigitsFound = true; - break; + + if (m_fNegative) + { + m_token.insert(0, 1, chNegate); + } + + return m_token; } } - if (fDigitsFound) - { - m_token.erase(0, trimIdx); - if (m_fNegative) - { - m_token.insert(0, &chNegate); - } - } - else - { - m_token.clear(); - m_token.append(&chZero); - } + m_token = chZero; return m_token; } wstring COpndCommand::GetString(uint32_t radix, int32_t precision) { - wstring result{}; - if (m_fInitialized) { - result = m_value.ToString(radix, eNUMOBJ_FMT::FMT_FLOAT, precision); + return m_value.ToString(radix, NumberFormat::Float, precision); } - return result; + return wstring{}; } -void COpndCommand::Accept(_In_ ISerializeCommandVisitor &commandVisitor) +void COpndCommand::Accept(_In_ ISerializeCommandVisitor& commandVisitor) { commandVisitor.Visit(*this); } diff --git a/src/CalcManager/ExpressionCommand.h b/src/CalcManager/ExpressionCommand.h index 2958f4f8..d8ab7ec0 100644 --- a/src/CalcManager/ExpressionCommand.h +++ b/src/CalcManager/ExpressionCommand.h @@ -12,7 +12,7 @@ public: CParentheses(_In_ int command); int GetCommand() const override; CalculationManager::CommandType GetCommandType() const override; - void Accept(_In_ ISerializeCommandVisitor &commandVisitor) override; + void Accept(_In_ ISerializeCommandVisitor& commandVisitor) override; private: int m_command; @@ -23,14 +23,14 @@ class CUnaryCommand final : public IUnaryCommand public: CUnaryCommand(int command); CUnaryCommand(int command1, int command2); - const std::shared_ptr> & GetCommands() const override; + const std::shared_ptr>& GetCommands() const override; CalculationManager::CommandType GetCommandType() const override; void SetCommand(int command) override; void SetCommands(int command1, int command2) override; - void Accept(_In_ ISerializeCommandVisitor &commandVisitor) override; + void Accept(_In_ ISerializeCommandVisitor& commandVisitor) override; private: - std::shared_ptr> m_command; + std::shared_ptr> m_command; }; class CBinaryCommand final : public IBinaryCommand @@ -40,7 +40,7 @@ public: void SetCommand(int command) override; int GetCommand() const override; CalculationManager::CommandType GetCommandType() const override; - void Accept(_In_ ISerializeCommandVisitor &commandVisitor) override; + void Accept(_In_ ISerializeCommandVisitor& commandVisitor) override; private: int m_command; @@ -49,28 +49,24 @@ private: class COpndCommand final : public IOpndCommand { public: - COpndCommand( - std::shared_ptr> const &commands, - bool fNegative, - bool fDecimal, - bool fSciFmt); + COpndCommand(std::shared_ptr> const& commands, bool fNegative, bool fDecimal, bool fSciFmt); void Initialize(CalcEngine::Rational const& rat); - const std::shared_ptr> & GetCommands() const override; - void SetCommands(std::shared_ptr> const& commands) override; + const std::shared_ptr>& GetCommands() const override; + void SetCommands(std::shared_ptr> const& commands) override; void AppendCommand(int command) override; void ToggleSign() override; void RemoveFromEnd() override; bool IsNegative() const override; bool IsSciFmt() const override; bool IsDecimalPresent() const override; - const std::wstring & GetToken(wchar_t decimalSymbol) override; + const std::wstring& GetToken(wchar_t decimalSymbol) override; CalculationManager::CommandType GetCommandType() const override; - void Accept(_In_ ISerializeCommandVisitor &commandVisitor) override; + void Accept(_In_ ISerializeCommandVisitor& commandVisitor) override; std::wstring GetString(uint32_t radix, int32_t precision); private: - std::shared_ptr> m_commands; + std::shared_ptr> m_commands; bool m_fNegative; bool m_fSciFmt; bool m_fDecimal; @@ -83,8 +79,8 @@ private: class ISerializeCommandVisitor { public: - virtual void Visit(_In_ COpndCommand &opndCmd) = 0; - virtual void Visit(_In_ CUnaryCommand &unaryCmd) = 0; - virtual void Visit(_In_ CBinaryCommand &binaryCmd) = 0; - virtual void Visit(_In_ CParentheses ¶Cmd) = 0; + virtual void Visit(_In_ COpndCommand& opndCmd) = 0; + virtual void Visit(_In_ CUnaryCommand& unaryCmd) = 0; + virtual void Visit(_In_ CBinaryCommand& binaryCmd) = 0; + virtual void Visit(_In_ CParentheses& paraCmd) = 0; }; diff --git a/src/CalcManager/ExpressionCommandInterface.h b/src/CalcManager/ExpressionCommandInterface.h index ce89dd81..4ea9b81b 100644 --- a/src/CalcManager/ExpressionCommandInterface.h +++ b/src/CalcManager/ExpressionCommandInterface.h @@ -1,8 +1,10 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once -#include "CalculatorVector.h" + +#include // for std::shared_ptr +#include #include "Command.h" class ISerializeCommandVisitor; @@ -11,7 +13,7 @@ class IExpressionCommand { public: virtual CalculationManager::CommandType GetCommandType() const = 0; - virtual void Accept(_In_ ISerializeCommandVisitor &commandVisitor) = 0; + virtual void Accept(_In_ ISerializeCommandVisitor& commandVisitor) = 0; }; class IOperatorCommand : public IExpressionCommand @@ -23,7 +25,7 @@ public: class IUnaryCommand : public IOperatorCommand { public: - virtual const std::shared_ptr> & GetCommands() const = 0; + virtual const std::shared_ptr>& GetCommands() const = 0; virtual void SetCommands(int command1, int command2) = 0; }; @@ -37,15 +39,15 @@ public: class IOpndCommand : public IExpressionCommand { public: - virtual const std::shared_ptr> & GetCommands() const= 0; + virtual const std::shared_ptr>& GetCommands() const = 0; virtual void AppendCommand(int command) = 0; virtual void ToggleSign() = 0; virtual void RemoveFromEnd() = 0; virtual bool IsNegative() const = 0; virtual bool IsSciFmt() const = 0; virtual bool IsDecimalPresent() const = 0; - virtual const std::wstring & GetToken(wchar_t decimalSymbol) = 0; - virtual void SetCommands(std::shared_ptr> const& commands) = 0; + virtual const std::wstring& GetToken(wchar_t decimalSymbol) = 0; + virtual void SetCommands(std::shared_ptr> const& commands) = 0; }; class IParenthesisCommand : public IExpressionCommand diff --git a/src/CalcManager/Header Files/CCommand.h b/src/CalcManager/Header Files/CCommand.h index 2fc08b3c..440c6283 100644 --- a/src/CalcManager/Header Files/CCommand.h +++ b/src/CalcManager/Header Files/CCommand.h @@ -13,36 +13,37 @@ * \****************************************************************************/ +#pragma once + // The following are the valid id's which can be passed to CCalcEngine::ProcessCommand -#define IDM_HEX 313 -#define IDM_DEC 314 -#define IDM_OCT 315 -#define IDM_BIN 316 -#define IDM_QWORD 317 -#define IDM_DWORD 318 -#define IDM_WORD 319 -#define IDM_BYTE 320 -#define IDM_DEG 321 -#define IDM_RAD 322 -#define IDM_GRAD 323 -#define IDM_DEGREES 324 +#define IDM_HEX 313 +#define IDM_DEC 314 +#define IDM_OCT 315 +#define IDM_BIN 316 +#define IDM_QWORD 317 +#define IDM_DWORD 318 +#define IDM_WORD 319 +#define IDM_BYTE 320 +#define IDM_DEG 321 +#define IDM_RAD 322 +#define IDM_GRAD 323 +#define IDM_DEGREES 324 -#define IDC_HEX IDM_HEX -#define IDC_DEC IDM_DEC -#define IDC_OCT IDM_OCT -#define IDC_BIN IDM_BIN +#define IDC_HEX IDM_HEX +#define IDC_DEC IDM_DEC +#define IDC_OCT IDM_OCT +#define IDC_BIN IDM_BIN -#define IDC_DEG IDM_DEG -#define IDC_RAD IDM_RAD -#define IDC_GRAD IDM_GRAD -#define IDC_DEGREES IDM_DEGREES - -#define IDC_QWORD IDM_QWORD -#define IDC_DWORD IDM_DWORD -#define IDC_WORD IDM_WORD -#define IDC_BYTE IDM_BYTE +#define IDC_DEG IDM_DEG +#define IDC_RAD IDM_RAD +#define IDC_GRAD IDM_GRAD +#define IDC_DEGREES IDM_DEGREES +#define IDC_QWORD IDM_QWORD +#define IDC_DWORD IDM_DWORD +#define IDC_WORD IDM_WORD +#define IDC_BYTE IDM_BYTE // Key IDs: // These id's must be consecutive from IDC_FIRSTCONTROL to IDC_LASTCONTROL. @@ -53,162 +54,196 @@ // Find the string id for that control from the rc file // Now define the control's id as IDC_FRISTCONTROL+stringID(IDC_ST_AVE) #define IDC_FIRSTCONTROL IDC_SIGN -#define IDC_SIGN 80 -#define IDC_CLEAR 81 -#define IDC_CENTR 82 -#define IDC_BACK 83 +#define IDC_SIGN 80 +#define IDC_CLEAR 81 +#define IDC_CENTR 82 +#define IDC_BACK 83 -#define IDC_PNT 84 +#define IDC_PNT 84 // Hole 85 -#define IDC_AND 86 // Binary operators must be between IDC_AND and IDC_PWR -#define IDC_OR 87 -#define IDC_XOR 88 -#define IDC_LSHF 89 -#define IDC_RSHF 90 -#define IDC_DIV 91 -#define IDC_MUL 92 -#define IDC_ADD 93 -#define IDC_SUB 94 -#define IDC_MOD 95 -#define IDC_ROOT 96 -#define IDC_PWR 97 +#define IDC_AND 86 // Binary operators must be between IDC_AND and IDC_PWR +#define IDC_OR 87 +#define IDC_XOR 88 +#define IDC_LSHF 89 +#define IDC_RSHF 90 +#define IDC_DIV 91 +#define IDC_MUL 92 +#define IDC_ADD 93 +#define IDC_SUB 94 +#define IDC_MOD 95 +#define IDC_ROOT 96 +#define IDC_PWR 97 +#define IDC_UNARYFIRST IDC_CHOP +#define IDC_CHOP 98 // Unary operators must be between IDC_CHOP and IDC_EQU +#define IDC_ROL 99 +#define IDC_ROR 100 +#define IDC_COM 101 -#define IDC_UNARYFIRST IDC_CHOP -#define IDC_CHOP 98 // Unary operators must be between IDC_CHOP and IDC_EQU -#define IDC_ROL 99 -#define IDC_ROR 100 -#define IDC_COM 101 -#define IDC_SIN 102 -#define IDC_COS 103 -#define IDC_TAN 104 +#define IDC_SIN 102 +#define IDC_COS 103 +#define IDC_TAN 104 -#define IDC_SINH 105 -#define IDC_COSH 106 -#define IDC_TANH 107 +#define IDC_SINH 105 +#define IDC_COSH 106 +#define IDC_TANH 107 -#define IDC_LN 108 -#define IDC_LOG 109 -#define IDC_SQRT 110 -#define IDC_SQR 111 -#define IDC_CUB 112 -#define IDC_FAC 113 -#define IDC_REC 114 -#define IDC_DMS 115 -#define IDC_CUBEROOT 116 // x ^ 1/3 -#define IDC_POW10 117 // 10 ^ x -#define IDC_PERCENT 118 +#define IDC_LN 108 +#define IDC_LOG 109 +#define IDC_SQRT 110 +#define IDC_SQR 111 +#define IDC_CUB 112 +#define IDC_FAC 113 +#define IDC_REC 114 +#define IDC_DMS 115 +#define IDC_CUBEROOT 116 // x ^ 1/3 +#define IDC_POW10 117 // 10 ^ x +#define IDC_PERCENT 118 #define IDC_UNARYLAST IDC_PERCENT -#define IDC_FE 119 -#define IDC_PI 120 -#define IDC_EQU 121 +#define IDC_FE 119 +#define IDC_PI 120 +#define IDC_EQU 121 -#define IDC_MCLEAR 122 -#define IDC_RECALL 123 -#define IDC_STORE 124 -#define IDC_MPLUS 125 -#define IDC_MMINUS 126 +#define IDC_MCLEAR 122 +#define IDC_RECALL 123 +#define IDC_STORE 124 +#define IDC_MPLUS 125 +#define IDC_MMINUS 126 -#define IDC_EXP 127 +#define IDC_EXP 127 +#define IDC_OPENP 128 +#define IDC_CLOSEP 129 -#define IDC_OPENP 128 -#define IDC_CLOSEP 129 +#define IDC_0 130 // The controls for 0 through F must be consecutive and in order +#define IDC_1 131 +#define IDC_2 132 +#define IDC_3 133 +#define IDC_4 134 +#define IDC_5 135 +#define IDC_6 136 +#define IDC_7 137 +#define IDC_8 138 +#define IDC_9 139 +#define IDC_A 140 +#define IDC_B 141 +#define IDC_C 142 +#define IDC_D 143 +#define IDC_E 144 +#define IDC_F 145 // this is last control ID which must match the string table +#define IDC_INV 146 +#define IDC_SET_RESULT 147 -#define IDC_0 130 // The controls for 0 through F must be consecutive and in order -#define IDC_1 131 -#define IDC_2 132 -#define IDC_3 133 -#define IDC_4 134 -#define IDC_5 135 -#define IDC_6 136 -#define IDC_7 137 -#define IDC_8 138 -#define IDC_9 139 -#define IDC_A 140 -#define IDC_B 141 -#define IDC_C 142 -#define IDC_D 143 -#define IDC_E 144 -#define IDC_F 145 // this is last control ID which must match the string table -#define IDC_INV 146 -#define IDC_SET_RESULT 147 +#define IDC_STRING_MAPPED_VALUES 400 +#define IDC_UNARYEXTENDEDFIRST IDC_STRING_MAPPED_VALUES +#define IDC_SEC 400 // Secant +// 401 reserved for inverse +#define IDC_CSC 402 // Cosecant +// 403 reserved for inverse +#define IDC_COT 404 // Cotangent +// 405 reserved for inverse -#define IDC_LASTCONTROL IDC_SET_RESULT +#define IDC_SECH 406 //Hyperbolic Secant +// 407 reserved for inverse +#define IDC_CSCH 408 //Hyperbolic Cosecant +// 409 reserved for inverse +#define IDC_COTH 410 //Hyperbolic Cotangent +// 411 reserved for inverse -#define IDC_BINEDITSTART 700 -#define IDC_BINPOS0 700 -#define IDC_BINPOS1 701 -#define IDC_BINPOS2 702 -#define IDC_BINPOS3 703 -#define IDC_BINPOS4 704 -#define IDC_BINPOS5 705 -#define IDC_BINPOS6 706 -#define IDC_BINPOS7 707 -#define IDC_BINPOS8 708 -#define IDC_BINPOS9 709 -#define IDC_BINPOS10 710 -#define IDC_BINPOS11 711 -#define IDC_BINPOS12 712 -#define IDC_BINPOS13 713 -#define IDC_BINPOS14 714 -#define IDC_BINPOS15 715 -#define IDC_BINPOS16 716 -#define IDC_BINPOS17 717 -#define IDC_BINPOS18 718 -#define IDC_BINPOS19 719 -#define IDC_BINPOS20 720 -#define IDC_BINPOS21 721 -#define IDC_BINPOS22 722 -#define IDC_BINPOS23 723 -#define IDC_BINPOS24 724 -#define IDC_BINPOS25 725 -#define IDC_BINPOS26 726 -#define IDC_BINPOS27 727 -#define IDC_BINPOS28 728 -#define IDC_BINPOS29 729 -#define IDC_BINPOS30 730 -#define IDC_BINPOS31 731 -#define IDC_BINPOS32 732 -#define IDC_BINPOS33 733 -#define IDC_BINPOS34 734 -#define IDC_BINPOS35 735 -#define IDC_BINPOS36 736 -#define IDC_BINPOS37 737 -#define IDC_BINPOS38 738 -#define IDC_BINPOS39 739 -#define IDC_BINPOS40 740 -#define IDC_BINPOS41 741 -#define IDC_BINPOS42 742 -#define IDC_BINPOS43 743 -#define IDC_BINPOS44 744 -#define IDC_BINPOS45 745 -#define IDC_BINPOS46 746 -#define IDC_BINPOS47 747 -#define IDC_BINPOS48 748 -#define IDC_BINPOS49 749 -#define IDC_BINPOS50 750 -#define IDC_BINPOS51 751 -#define IDC_BINPOS52 752 -#define IDC_BINPOS53 753 -#define IDC_BINPOS54 754 -#define IDC_BINPOS55 755 -#define IDC_BINPOS56 756 -#define IDC_BINPOS57 757 -#define IDC_BINPOS58 758 -#define IDC_BINPOS59 759 -#define IDC_BINPOS60 760 -#define IDC_BINPOS61 761 -#define IDC_BINPOS62 762 -#define IDC_BINPOS63 763 -#define IDC_BINEDITEND 763 +#define IDC_POW2 412 // 2 ^ x +#define IDC_ABS 413 // Absolute Value +#define IDC_FLOOR 414 // Floor +#define IDC_CEIL 415 // Ceiling +#define IDC_ROLC 416 // Rotate Left Circular +#define IDC_RORC 417 // Rotate Right Circular + +#define IDC_UNARYEXTENDEDLAST IDC_RORC + +#define IDC_LASTCONTROL IDC_CEIL + +#define IDC_BINARYEXTENDEDFIRST 500 +#define IDC_LOGBASEY 500 // logy(x) +#define IDC_NAND 501 // Nand +#define IDC_NOR 502 // Nor + +#define IDC_RSHFL 505 //Right Shift Logical +#define IDC_BINARYEXTENDEDLAST IDC_RSHFL + +#define IDC_RAND 600 // Random +#define IDC_EULER 601 // e Constant + +#define IDC_BINEDITSTART 700 +#define IDC_BINPOS0 700 +#define IDC_BINPOS1 701 +#define IDC_BINPOS2 702 +#define IDC_BINPOS3 703 +#define IDC_BINPOS4 704 +#define IDC_BINPOS5 705 +#define IDC_BINPOS6 706 +#define IDC_BINPOS7 707 +#define IDC_BINPOS8 708 +#define IDC_BINPOS9 709 +#define IDC_BINPOS10 710 +#define IDC_BINPOS11 711 +#define IDC_BINPOS12 712 +#define IDC_BINPOS13 713 +#define IDC_BINPOS14 714 +#define IDC_BINPOS15 715 +#define IDC_BINPOS16 716 +#define IDC_BINPOS17 717 +#define IDC_BINPOS18 718 +#define IDC_BINPOS19 719 +#define IDC_BINPOS20 720 +#define IDC_BINPOS21 721 +#define IDC_BINPOS22 722 +#define IDC_BINPOS23 723 +#define IDC_BINPOS24 724 +#define IDC_BINPOS25 725 +#define IDC_BINPOS26 726 +#define IDC_BINPOS27 727 +#define IDC_BINPOS28 728 +#define IDC_BINPOS29 729 +#define IDC_BINPOS30 730 +#define IDC_BINPOS31 731 +#define IDC_BINPOS32 732 +#define IDC_BINPOS33 733 +#define IDC_BINPOS34 734 +#define IDC_BINPOS35 735 +#define IDC_BINPOS36 736 +#define IDC_BINPOS37 737 +#define IDC_BINPOS38 738 +#define IDC_BINPOS39 739 +#define IDC_BINPOS40 740 +#define IDC_BINPOS41 741 +#define IDC_BINPOS42 742 +#define IDC_BINPOS43 743 +#define IDC_BINPOS44 744 +#define IDC_BINPOS45 745 +#define IDC_BINPOS46 746 +#define IDC_BINPOS47 747 +#define IDC_BINPOS48 748 +#define IDC_BINPOS49 749 +#define IDC_BINPOS50 750 +#define IDC_BINPOS51 751 +#define IDC_BINPOS52 752 +#define IDC_BINPOS53 753 +#define IDC_BINPOS54 754 +#define IDC_BINPOS55 755 +#define IDC_BINPOS56 756 +#define IDC_BINPOS57 757 +#define IDC_BINPOS58 758 +#define IDC_BINPOS59 759 +#define IDC_BINPOS60 760 +#define IDC_BINPOS61 761 +#define IDC_BINPOS62 762 +#define IDC_BINPOS63 763 +#define IDC_BINEDITEND 763 // The strings in the following range IDS_ENGINESTR_FIRST ... IDS_ENGINESTR_MAX are strings allocated in the // resource for the purpose internal to Engine and cant be used by the clients -#define IDS_ENGINESTR_FIRST 0 -#define IDS_ENGINESTR_MAX 200 - +#define IDS_ENGINESTR_FIRST 0 +#define IDS_ENGINESTR_MAX 200 diff --git a/src/CalcManager/Header Files/CalcEngine.h b/src/CalcManager/Header Files/CalcEngine.h index 3e151e2a..0158e628 100644 --- a/src/CalcManager/Header Files/CalcEngine.h +++ b/src/CalcManager/Header Files/CalcEngine.h @@ -14,14 +14,15 @@ * \****************************************************************************/ +#include #include "CCommand.h" #include "EngineStrings.h" #include "../Command.h" -#include "../CalculatorVector.h" #include "../ExpressionCommand.h" #include "RadixType.h" -#include "History.h" // for History Collector +#include "History.h" // for History Collector #include "CalcInput.h" +#include "CalcUtils.h" #include "ICalcDisplay.h" #include "Rational.h" #include "RationalMath.h" @@ -30,13 +31,13 @@ // The real exports follows later // This is expected to be in same order as IDM_QWORD, IDM_DWORD etc. -enum eNUM_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 +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 @@ -44,62 +45,94 @@ namespace CalculationManager class IResourceProvider; } -namespace CalculatorUnitTests +namespace CalculatorEngineTests { class CalcEngineTests; } -class CCalcEngine { +class CCalcEngine +{ public: - CCalcEngine(bool fPrecedence, bool fIntegerMode, CalculationManager::IResourceProvider* const pResourceProvider, __in_opt ICalcDisplay *pCalcDisplay, __in_opt std::shared_ptr pHistoryDisplay); - void ProcessCommand(WPARAM wID); - void DisplayError (DWORD nError); + CCalcEngine( + bool fPrecedence, + bool fIntegerMode, + CalculationManager::IResourceProvider* const pResourceProvider, + __in_opt ICalcDisplay* pCalcDisplay, + __in_opt std::shared_ptr pHistoryDisplay); + void ProcessCommand(OpCode wID); + void DisplayError(uint32_t nError); std::unique_ptr PersistedMemObject(); void PersistedMemObject(CalcEngine::Rational const& memObject); - bool FInErrorState() { return m_bError; } - bool FInRecordingState() { return m_bRecord; } + bool FInErrorState() + { + return m_bError; + } + bool IsInputEmpty() + { + return m_input.IsEmpty() && (m_numberString.empty() || m_numberString == L"0"); + } + bool FInRecordingState() + { + return m_bRecord; + } void SettingsChanged(); bool IsCurrentTooBigForTrig(); int GetCurrentRadix(); - std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision); - void ChangePrecision(int32_t precision) { m_precision = precision; ChangeConstants(m_radix, precision); } + std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); + void ChangePrecision(int32_t precision) + { + m_precision = precision; + ChangeConstants(m_radix, precision); + } std::wstring GroupDigitsPerRadix(std::wstring_view numberString, uint32_t radix); std::wstring GetStringForDisplay(CalcEngine::Rational const& rat, uint32_t radix); void UpdateMaxIntDigits(); wchar_t DecimalSeparator() const; // Static methods for the instance - static void InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider); // Once per load time to call to initialize all shared global variables + static void + InitialOneTimeOnlySetup(CalculationManager::IResourceProvider& resourceProvider); // Once per load time to call to initialize all shared global variables // returns the ptr to string representing the operator. Mostly same as the button, but few special cases for x^y etc. - static std::wstring_view GetString(int ids) { return s_engineStrings[ids]; } - static std::wstring_view OpCodeToString(int nOpCode) { return GetString(IdStrFromCmdId(nOpCode)); } - static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype); + static std::wstring_view GetString(int ids) + { + return s_engineStrings[std::to_wstring(ids)]; + } + static std::wstring_view GetString(std::wstring_view ids) + { + return s_engineStrings[ids]; + } + static std::wstring_view OpCodeToString(int nOpCode) + { + return GetString(IdStrFromCmdId(nOpCode)); + } + static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype); + static std::wstring_view OpCodeToBinaryString(int nOpCode, bool isIntegerMode); private: bool m_fPrecedence; bool m_fIntegerMode; /* This is true if engine is explicitly called to be in integer mode. All bases are restricted to be in integers only */ - ICalcDisplay *m_pCalcDisplay; + ICalcDisplay* m_pCalcDisplay; CalculationManager::IResourceProvider* const m_resourceProvider; - int m_nOpCode; /* ID value of operation. */ + 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_bRecord; // Global mode: recording or displaying - bool m_bSetCalcState; // Flag for setting the engine result state + 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 m_memoryValue; // Current memory value. CalcEngine::Rational m_holdVal; // For holding the second operand in repetitive calculations ( pressing "=" continuously) - CalcEngine::Rational m_currentVal; // Currently displayed number used everywhere. - CalcEngine::Rational m_lastVal; // Number before operation (left operand). - std::array m_parenVals; // Holding array for parenthesis values. + CalcEngine::Rational m_currentVal; // Currently displayed number used everywhere. + CalcEngine::Rational m_lastVal; // Number before operation (left operand). + std::array m_parenVals; // Holding array for parenthesis values. std::array m_precedenceVals; // Holding array for precedence values. - bool m_bError; // Error flag. - bool m_bInv; // Inverse on/off flag. - bool m_bNoPrevEqu; /* Flag for previous equals. */ + bool m_bError; // Error flag. + bool m_bInv; // Inverse on/off flag. + bool m_bNoPrevEqu; /* Flag for previous equals. */ uint32_t m_radix; int32_t m_precision; @@ -108,48 +141,60 @@ private: std::wstring m_numberString; - int m_nTempCom; /* Holding place for the last command. */ - int m_openParenCount; // Number of open parentheses. - std::array m_nOp; /* Holding array for parenthesis operations. */ - std::array 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 - NUM_WIDTH m_numwidth; // one of qword, dword, word or byte mode. - LONG m_dwWordBitWidth; // # of bits in currently selected word size + int m_nTempCom; /* Holding place for the last command. */ + size_t m_openParenCount; // Number of open parentheses. + std::array m_nOp; /* Holding array for parenthesis operations. */ + std::array m_nPrecOp; /* Holding array for precedence operations. */ + size_t m_precedenceOpCount; /* Current number of precedence ops in holding. */ + int m_nLastCom; // Last command entered. + 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 + + std::unique_ptr m_randomGeneratorEngine; + std::unique_ptr> m_distr; + + uint64_t m_carryBit; CHistoryCollector m_HistoryCollector; // Accumulator of each line of history as various commands are processed - std::array m_chopNumbers; // word size enforcement - std::array m_maxDecimalValueStrings; // maximum values represented by a given word width based off m_chopNumbers - static std::array s_engineStrings; // the string table shared across all instances + std::array m_chopNumbers; // word size enforcement + std::array m_maxDecimalValueStrings; // maximum values represented by a given word width based off m_chopNumbers + static std::unordered_map s_engineStrings; // the string table shared across all instances wchar_t m_decimalSeparator; wchar_t m_groupSeparator; private: - void ProcessCommandWorker(WPARAM wParam); - void HandleErrorCommand(WPARAM idc); + void ProcessCommandWorker(OpCode wParam); + void ResolveHighestPrecedenceOperation(); + void HandleErrorCommand(OpCode idc); void HandleMaxDigitsReached(); void DisplayNum(void); int IsNumberInvalid(const std::wstring& numberString, int iMaxExp, int iMaxMantissa, uint32_t radix) const; void DisplayAnnounceBinaryOperator(); void SetPrimaryDisplay(const std::wstring& szText, bool isError = false); void ClearTemporaryValues(); + void ClearDisplay(); CalcEngine::Rational TruncateNumForIntMath(CalcEngine::Rational const& rat); - CalcEngine::Rational SciCalcFunctions(CalcEngine::Rational const& rat, DWORD op); + 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); - LONG DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth); - uint32_t NRadixFromRadixType( RADIX_TYPE radixtype); + void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth); + int32_t DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth); + uint32_t NRadixFromRadixType(RadixType radixtype); + double GenerateRandomNumber(); - bool TryToggleBit(CalcEngine::Rational& rat, DWORD wbitno); + bool TryToggleBit(CalcEngine::Rational& rat, uint32_t wbitno); void CheckAndAddLastBinOpToHistory(bool addToHistory = true); - int IdcSetAngleTypeDecMode(int idc); void InitChopNumbers(); + CalcEngine::Rational GetChopNumber() const; + std::wstring GetMaxDecimalValueString() const; static void LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider); - static int IdStrFromCmdId(int id) { return id - IDC_FIRSTCONTROL + IDS_FIRSTENGSTR; } + static int IdStrFromCmdId(int id) + { + return id - IDC_FIRSTCONTROL + IDS_ENGINESTR_FIRST; + } static std::vector DigitGroupingStringToGroupingVector(std::wstring_view groupingString); std::wstring GroupDigits(std::wstring_view delimiter, std::vector const& grouping, std::wstring_view displayString, bool isNumNegative = false); @@ -158,5 +203,5 @@ private: static void ChangeBaseConstants(uint32_t radix, int maxIntDigits, int32_t precision); void BaseOrPrecisionChanged(); - friend class CalculatorUnitTests::CalcEngineTests; + friend class CalculatorEngineTests::CalcEngineTests; }; diff --git a/src/CalcManager/Header Files/CalcInput.h b/src/CalcManager/Header Files/CalcInput.h index 60d628c1..b798d061 100644 --- a/src/CalcManager/Header Files/CalcInput.h +++ b/src/CalcManager/Header Files/CalcInput.h @@ -13,16 +13,26 @@ namespace CalcEngine class CalcNumSec { public: - CalcNumSec() : - value(), - m_isNegative(false) - {} + CalcNumSec() + : value() + , m_isNegative(false) + { + } void Clear(); - bool IsEmpty() { return value.empty(); } + bool IsEmpty() + { + return value.empty(); + } - bool IsNegative() { return m_isNegative; } - void IsNegative(bool isNegative) { m_isNegative = isNegative; } + bool IsNegative() + { + return m_isNegative; + } + void IsNegative(bool isNegative) + { + m_isNegative = isNegative; + } std::wstring value; @@ -33,26 +43,30 @@ namespace CalcEngine class CalcInput { public: - CalcInput() : CalcInput(L'.') - {} + CalcInput() + : CalcInput(L'.') + { + } - CalcInput(wchar_t decSymbol) : - m_hasExponent(false), - m_hasDecimal(false), - m_decPtIndex(0), - m_decSymbol(decSymbol), - m_base(), - m_exponent() - {} + CalcInput(wchar_t decSymbol) + : m_hasExponent(false) + , m_hasDecimal(false) + , m_decPtIndex(0) + , m_decSymbol(decSymbol) + , m_base() + , m_exponent() + { + } void Clear(); bool TryToggleSign(bool isIntegerMode, std::wstring_view maxNumStr); - bool TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, std::wstring_view maxNumStr, long wordBitWidth, int maxDigits); + bool TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, std::wstring_view maxNumStr, int32_t wordBitWidth, int maxDigits); bool TryAddDecimalPt(); bool HasDecimalPt(); bool TryBeginExponent(); void Backspace(); void SetDecimalSymbol(wchar_t decSymbol); + bool IsEmpty(); std::wstring ToString(uint32_t radix); Rational ToRational(uint32_t radix, int32_t precision); diff --git a/src/CalcManager/Header Files/CalcUtils.h b/src/CalcManager/Header Files/CalcUtils.h index 1e0dcae3..bd800783 100644 --- a/src/CalcManager/Header Files/CalcUtils.h +++ b/src/CalcManager/Header Files/CalcUtils.h @@ -3,11 +3,13 @@ #pragma once -bool IsOpInRange(WPARAM op, uint32_t x, uint32_t y); -bool IsBinOpCode(WPARAM opCode); +using OpCode = uintptr_t; + +bool IsOpInRange(OpCode op, uint32_t x, uint32_t y); +bool IsBinOpCode(OpCode opCode); // WARNING: IDC_SIGN is a special unary op but still this doesn't catch this. Caller has to be aware // of it and catch it themselves or not needing this -bool IsUnaryOpCode(WPARAM opCode); -bool IsDigitOpCode(WPARAM opCode); -bool IsGuiSettingOpCode(WPARAM opCode); +bool IsUnaryOpCode(OpCode opCode); +bool IsDigitOpCode(OpCode opCode); +bool IsGuiSettingOpCode(OpCode opCode); diff --git a/src/CalcManager/Header Files/EngineStrings.h b/src/CalcManager/Header Files/EngineStrings.h index 2ada6bba..7e093237 100644 --- a/src/CalcManager/Header Files/EngineStrings.h +++ b/src/CalcManager/Header Files/EngineStrings.h @@ -13,327 +13,351 @@ * Created: 13-Feb-2008 * \****************************************************************************/ -#define IDS_FIRSTENGSTR IDS_ENGINESTR_FIRST -#define IDS_DECIMAL 4 +#pragma once -// All unary op function names for easy history reading -// This is where the first string after all the commands in order have been placed, should be placed -// keeping in consecutive helps us to allocate 1 string table and index them -#define IDS_FNSZFIRST (IDC_F -IDC_FIRSTCONTROL)+1 +#include +#include +#include +#include -#define IDS_FRAC IDS_FNSZFIRST - -#define IDS_SIND IDS_FNSZFIRST+1 -#define IDS_COSD IDS_FNSZFIRST+2 -#define IDS_TAND IDS_FNSZFIRST+3 -#define IDS_ASIND IDS_FNSZFIRST+4 -#define IDS_ACOSD IDS_FNSZFIRST+5 -#define IDS_ATAND IDS_FNSZFIRST+6 - -#define IDS_SINR IDS_FNSZFIRST+7 -#define IDS_COSR IDS_FNSZFIRST+8 -#define IDS_TANR IDS_FNSZFIRST+9 -#define IDS_ASINR IDS_FNSZFIRST+10 -#define IDS_ACOSR IDS_FNSZFIRST+11 -#define IDS_ATANR IDS_FNSZFIRST+12 - -#define IDS_SING IDS_FNSZFIRST+13 -#define IDS_COSG IDS_FNSZFIRST+14 -#define IDS_TANG IDS_FNSZFIRST+15 -#define IDS_ASING IDS_FNSZFIRST+16 -#define IDS_ACOSG IDS_FNSZFIRST+17 -#define IDS_ATANG IDS_FNSZFIRST+18 - -#define IDS_ASINH IDS_FNSZFIRST+19 -#define IDS_ACOSH IDS_FNSZFIRST+20 -#define IDS_ATANH IDS_FNSZFIRST+21 -#define IDS_POWE IDS_FNSZFIRST+22 -#define IDS_POW10 IDS_FNSZFIRST+23 -#define IDS_SQRT IDS_FNSZFIRST+24 -#define IDS_SQR IDS_FNSZFIRST+25 -#define IDS_CUBE IDS_FNSZFIRST+26 -#define IDS_CUBERT IDS_FNSZFIRST+27 -#define IDS_FACT IDS_FNSZFIRST+28 -#define IDS_REC IDS_FNSZFIRST+29 -#define IDS_DEGREES IDS_FNSZFIRST+30 -#define IDS_NEGATE IDS_FNSZFIRST+31 -#define IDS_RSH IDS_FNSZFIRST+32 - -#define IDS_FNSZLAST IDS_RSH - -#define IDS_ERRORS_FIRST IDS_FNSZLAST+1 +inline constexpr auto IDS_ERRORS_FIRST = 99; // This is the list of error strings corresponding to SCERR_DIVIDEZERO.. -#define IDS_DIVBYZERO IDS_ERRORS_FIRST -#define IDS_DOMAIN IDS_ERRORS_FIRST+1 -#define IDS_UNDEFINED IDS_ERRORS_FIRST+2 -#define IDS_POS_INFINITY IDS_ERRORS_FIRST+3 -#define IDS_NEG_INFINITY IDS_ERRORS_FIRST+4 -#define IDS_NOMEM IDS_ERRORS_FIRST+6 -#define IDS_TOOMANY IDS_ERRORS_FIRST+7 -#define IDS_OVERFLOW IDS_ERRORS_FIRST+8 -#define IDS_NORESULT IDS_ERRORS_FIRST+9 -#define IDS_INSUFFICIENT_DATA IDS_ERRORS_FIRST+10 +inline constexpr auto IDS_DIVBYZERO = IDS_ERRORS_FIRST; +inline constexpr auto IDS_DOMAIN = IDS_ERRORS_FIRST + 1; +inline constexpr auto IDS_UNDEFINED = IDS_ERRORS_FIRST + 2; +inline constexpr auto IDS_POS_INFINITY = IDS_ERRORS_FIRST + 3; +inline constexpr auto IDS_NEG_INFINITY = IDS_ERRORS_FIRST + 4; +inline constexpr auto IDS_NOMEM = IDS_ERRORS_FIRST + 6; +inline constexpr auto IDS_TOOMANY = IDS_ERRORS_FIRST + 7; +inline constexpr auto IDS_OVERFLOW = IDS_ERRORS_FIRST + 8; +inline constexpr auto IDS_NORESULT = IDS_ERRORS_FIRST + 9; +inline constexpr auto IDS_INSUFFICIENT_DATA = IDS_ERRORS_FIRST + 10; -#define CSTRINGSENGMAX IDS_INSUFFICIENT_DATA+1 +inline constexpr auto CSTRINGSENGMAX = IDS_INSUFFICIENT_DATA + 1; // Arithmetic expression evaluator error strings -#define IDS_ERR_UNK_CH CSTRINGSENGMAX+1 -#define IDS_ERR_UNK_FN CSTRINGSENGMAX+2 -#define IDS_ERR_UNEX_NUM CSTRINGSENGMAX+3 -#define IDS_ERR_UNEX_CH CSTRINGSENGMAX+4 -#define IDS_ERR_UNEX_SZ CSTRINGSENGMAX+5 -#define IDS_ERR_MISMATCH_CLOSE CSTRINGSENGMAX+6 -#define IDS_ERR_UNEX_END CSTRINGSENGMAX+7 -#define IDS_ERR_SG_INV_ERROR CSTRINGSENGMAX+8 -#define IDS_ERR_INPUT_OVERFLOW CSTRINGSENGMAX+9 -#define IDS_ERR_OUTPUT_OVERFLOW CSTRINGSENGMAX+10 +inline constexpr auto IDS_ERR_UNK_CH = CSTRINGSENGMAX + 1; +inline constexpr auto IDS_ERR_UNK_FN = CSTRINGSENGMAX + 2; +inline constexpr auto IDS_ERR_UNEX_NUM = CSTRINGSENGMAX + 3; +inline constexpr auto IDS_ERR_UNEX_CH = CSTRINGSENGMAX + 4; +inline constexpr auto IDS_ERR_UNEX_SZ = CSTRINGSENGMAX + 5; +inline constexpr auto IDS_ERR_MISMATCH_CLOSE = CSTRINGSENGMAX + 6; +inline constexpr auto IDS_ERR_UNEX_END = CSTRINGSENGMAX + 7; +inline constexpr auto IDS_ERR_SG_INV_ERROR = CSTRINGSENGMAX + 8; +inline constexpr auto IDS_ERR_INPUT_OVERFLOW = CSTRINGSENGMAX + 9; +inline constexpr auto IDS_ERR_OUTPUT_OVERFLOW = CSTRINGSENGMAX + 10; - -#define SIDS_PLUS_MINUS L"0" -#define SIDS_CLEAR L"1" -#define SIDS_CE L"2" -#define SIDS_BACKSPACE L"3" -#define SIDS_DECIMAL_SEPARATOR L"4" -#define SIDS_EMPTY_STRING L"5" -#define SIDS_AND L"6" -#define SIDS_OR L"7" -#define SIDS_XOR L"8" -#define SIDS_LSH L"9" -#define SIDS_RSH L"10" -#define SIDS_DIVIDE L"11" -#define SIDS_MULTIPLY L"12" -#define SIDS_PLUS L"13" -#define SIDS_MINUS L"14" -#define SIDS_MOD L"15" -#define SIDS_YROOT L"16" -#define SIDS_POW_HAT L"17" -#define SIDS_INT L"18" -#define SIDS_ROL L"19" -#define SIDS_ROR L"20" -#define SIDS_NOT L"21" -#define SIDS_SIN L"22" -#define SIDS_COS L"23" -#define SIDS_TAN L"24" -#define SIDS_SINH L"25" -#define SIDS_COSH L"26" -#define SIDS_TANH L"27" -#define SIDS_LN L"28" -#define SIDS_LOG L"29" -#define SIDS_SQRT L"30" -#define SIDS_XPOW2 L"31" -#define SIDS_XPOW3 L"32" -#define SIDS_NFACTORIAL L"33" -#define SIDS_RECIPROCAL L"34" -#define SIDS_DMS L"35" -#define SIDS_CUBEROOT L"36" -#define SIDS_POWTEN L"37" -#define SIDS_PERCENT L"38" -#define SIDS_SCIENTIFIC_NOTATION L"39" -#define SIDS_PI L"40" -#define SIDS_EQUAL L"41" -#define SIDS_MC L"42" -#define SIDS_MR L"43" -#define SIDS_MS L"44" -#define SIDS_MPLUS L"45" -#define SIDS_MMINUS L"46" -#define SIDS_EXP L"47" -#define SIDS_OPEN_PAREN L"48" -#define SIDS_CLOSE_PAREN L"49" -#define SIDS_0 L"50" -#define SIDS_1 L"51" -#define SIDS_2 L"52" -#define SIDS_3 L"53" -#define SIDS_4 L"54" -#define SIDS_5 L"55" -#define SIDS_6 L"56" -#define SIDS_7 L"57" -#define SIDS_8 L"58" -#define SIDS_9 L"59" -#define SIDS_A L"60" -#define SIDS_B L"61" -#define SIDS_C L"62" -#define SIDS_D L"63" -#define SIDS_E L"64" -#define SIDS_F L"65" -#define SIDS_FRAC L"66" -#define SIDS_SIND L"67" -#define SIDS_COSD L"68" -#define SIDS_TAND L"69" -#define SIDS_ASIND L"70" -#define SIDS_ACOSD L"71" -#define SIDS_ATAND L"72" -#define SIDS_SINR L"73" -#define SIDS_COSR L"74" -#define SIDS_TANR L"75" -#define SIDS_ASINR L"76" -#define SIDS_ACOSR L"77" -#define SIDS_ATANR L"78" -#define SIDS_SING L"79" -#define SIDS_COSG L"80" -#define SIDS_TANG L"81" -#define SIDS_ASING L"82" -#define SIDS_ACOSG L"83" -#define SIDS_ATANG L"84" -#define SIDS_ASINH L"85" -#define SIDS_ACOSH L"86" -#define SIDS_ATANH L"87" -#define SIDS_POWE L"88" -#define SIDS_POWTEN2 L"89" -#define SIDS_SQRT2 L"90" -#define SIDS_SQR L"91" -#define SIDS_CUBE L"92" -#define SIDS_CUBERT L"93" -#define SIDS_FACT L"94" -#define SIDS_RECIPROC L"95" -#define SIDS_DEGREES L"96" -#define SIDS_NEGATE L"97" -#define SIDS_RSH2 L"98" -#define SIDS_DIVIDEBYZERO L"99" -#define SIDS_DOMAIN L"100" -#define SIDS_UNDEFINED L"101" -#define SIDS_POS_INFINITY L"102" -#define SIDS_NEG_INFINITY L"103" -#define SIDS_ABORTED L"104" -#define SIDS_NOMEM L"105" -#define SIDS_TOOMANY L"106" -#define SIDS_OVERFLOW L"107" -#define SIDS_NORESULT L"108" -#define SIDS_INSUFFICIENT_DATA L"109" +// Resource keys for CEngineStrings.resw +inline constexpr auto SIDS_PLUS_MINUS = L"0"; +inline constexpr auto SIDS_CLEAR = L"1"; +inline constexpr auto SIDS_CE = L"2"; +inline constexpr auto SIDS_BACKSPACE = L"3"; +inline constexpr auto SIDS_DECIMAL_SEPARATOR = L"4"; +inline constexpr auto SIDS_EMPTY_STRING = L"5"; +inline constexpr auto SIDS_AND = L"6"; +inline constexpr auto SIDS_OR = L"7"; +inline constexpr auto SIDS_XOR = L"8"; +inline constexpr auto SIDS_LSH = L"9"; +inline constexpr auto SIDS_RSH = L"10"; +inline constexpr auto SIDS_DIVIDE = L"11"; +inline constexpr auto SIDS_MULTIPLY = L"12"; +inline constexpr auto SIDS_PLUS = L"13"; +inline constexpr auto SIDS_MINUS = L"14"; +inline constexpr auto SIDS_MOD = L"15"; +inline constexpr auto SIDS_YROOT = L"16"; +inline constexpr auto SIDS_POW_HAT = L"17"; +inline constexpr auto SIDS_INT = L"18"; +inline constexpr auto SIDS_ROL = L"19"; +inline constexpr auto SIDS_ROR = L"20"; +inline constexpr auto SIDS_NOT = L"21"; +inline constexpr auto SIDS_SIN = L"22"; +inline constexpr auto SIDS_COS = L"23"; +inline constexpr auto SIDS_TAN = L"24"; +inline constexpr auto SIDS_SINH = L"25"; +inline constexpr auto SIDS_COSH = L"26"; +inline constexpr auto SIDS_TANH = L"27"; +inline constexpr auto SIDS_LN = L"28"; +inline constexpr auto SIDS_LOG = L"29"; +inline constexpr auto SIDS_SQRT = L"30"; +inline constexpr auto SIDS_XPOW2 = L"31"; +inline constexpr auto SIDS_XPOW3 = L"32"; +inline constexpr auto SIDS_NFACTORIAL = L"33"; +inline constexpr auto SIDS_RECIPROCAL = L"34"; +inline constexpr auto SIDS_DMS = L"35"; +inline constexpr auto SIDS_POWTEN = L"37"; +inline constexpr auto SIDS_PERCENT = L"38"; +inline constexpr auto SIDS_SCIENTIFIC_NOTATION = L"39"; +inline constexpr auto SIDS_PI = L"40"; +inline constexpr auto SIDS_EQUAL = L"41"; +inline constexpr auto SIDS_MC = L"42"; +inline constexpr auto SIDS_MR = L"43"; +inline constexpr auto SIDS_MS = L"44"; +inline constexpr auto SIDS_MPLUS = L"45"; +inline constexpr auto SIDS_MMINUS = L"46"; +inline constexpr auto SIDS_EXP = L"47"; +inline constexpr auto SIDS_OPEN_PAREN = L"48"; +inline constexpr auto SIDS_CLOSE_PAREN = L"49"; +inline constexpr auto SIDS_0 = L"50"; +inline constexpr auto SIDS_1 = L"51"; +inline constexpr auto SIDS_2 = L"52"; +inline constexpr auto SIDS_3 = L"53"; +inline constexpr auto SIDS_4 = L"54"; +inline constexpr auto SIDS_5 = L"55"; +inline constexpr auto SIDS_6 = L"56"; +inline constexpr auto SIDS_7 = L"57"; +inline constexpr auto SIDS_8 = L"58"; +inline constexpr auto SIDS_9 = L"59"; +inline constexpr auto SIDS_A = L"60"; +inline constexpr auto SIDS_B = L"61"; +inline constexpr auto SIDS_C = L"62"; +inline constexpr auto SIDS_D = L"63"; +inline constexpr auto SIDS_E = L"64"; +inline constexpr auto SIDS_F = L"65"; +inline constexpr auto SIDS_FRAC = L"66"; +inline constexpr auto SIDS_SIND = L"67"; +inline constexpr auto SIDS_COSD = L"68"; +inline constexpr auto SIDS_TAND = L"69"; +inline constexpr auto SIDS_ASIND = L"70"; +inline constexpr auto SIDS_ACOSD = L"71"; +inline constexpr auto SIDS_ATAND = L"72"; +inline constexpr auto SIDS_SINR = L"73"; +inline constexpr auto SIDS_COSR = L"74"; +inline constexpr auto SIDS_TANR = L"75"; +inline constexpr auto SIDS_ASINR = L"76"; +inline constexpr auto SIDS_ACOSR = L"77"; +inline constexpr auto SIDS_ATANR = L"78"; +inline constexpr auto SIDS_SING = L"79"; +inline constexpr auto SIDS_COSG = L"80"; +inline constexpr auto SIDS_TANG = L"81"; +inline constexpr auto SIDS_ASING = L"82"; +inline constexpr auto SIDS_ACOSG = L"83"; +inline constexpr auto SIDS_ATANG = L"84"; +inline constexpr auto SIDS_ASINH = L"85"; +inline constexpr auto SIDS_ACOSH = L"86"; +inline constexpr auto SIDS_ATANH = L"87"; +inline constexpr auto SIDS_POWE = L"88"; +inline constexpr auto SIDS_POWTEN2 = L"89"; +inline constexpr auto SIDS_SQRT2 = L"90"; +inline constexpr auto SIDS_SQR = L"91"; +inline constexpr auto SIDS_CUBE = L"92"; +inline constexpr auto SIDS_CUBERT = L"93"; +inline constexpr auto SIDS_FACT = L"94"; +inline constexpr auto SIDS_RECIPROC = L"95"; +inline constexpr auto SIDS_DEGREES = L"96"; +inline constexpr auto SIDS_NEGATE = L"97"; +inline constexpr auto SIDS_RSH2 = L"98"; +inline constexpr auto SIDS_DIVIDEBYZERO = L"99"; +inline constexpr auto SIDS_DOMAIN = L"100"; +inline constexpr auto SIDS_UNDEFINED = L"101"; +inline constexpr auto SIDS_POS_INFINITY = L"102"; +inline constexpr auto SIDS_NEG_INFINITY = L"103"; +inline constexpr auto SIDS_ABORTED = L"104"; +inline constexpr auto SIDS_NOMEM = L"105"; +inline constexpr auto SIDS_TOOMANY = L"106"; +inline constexpr auto SIDS_OVERFLOW = L"107"; +inline constexpr auto SIDS_NORESULT = L"108"; +inline constexpr auto SIDS_INSUFFICIENT_DATA = L"109"; // 110 is skipped by CSTRINGSENGMAX -#define SIDS_ERR_UNK_CH L"111" -#define SIDS_ERR_UNK_FN L"112" -#define SIDS_ERR_UNEX_NUM L"113" -#define SIDS_ERR_UNEX_CH L"114" -#define SIDS_ERR_UNEX_SZ L"115" -#define SIDS_ERR_MISMATCH_CLOSE L"116" -#define SIDS_ERR_UNEX_END L"117" -#define SIDS_ERR_SG_INV_ERROR L"118" -#define SIDS_ERR_INPUT_OVERFLOW L"119" -#define SIDS_ERR_OUTPUT_OVERFLOW L"120" +inline constexpr auto SIDS_ERR_UNK_CH = L"111"; +inline constexpr auto SIDS_ERR_UNK_FN = L"112"; +inline constexpr auto SIDS_ERR_UNEX_NUM = L"113"; +inline constexpr auto SIDS_ERR_UNEX_CH = L"114"; +inline constexpr auto SIDS_ERR_UNEX_SZ = L"115"; +inline constexpr auto SIDS_ERR_MISMATCH_CLOSE = L"116"; +inline constexpr auto SIDS_ERR_UNEX_END = L"117"; +inline constexpr auto SIDS_ERR_SG_INV_ERROR = L"118"; +inline constexpr auto SIDS_ERR_INPUT_OVERFLOW = L"119"; +inline constexpr auto SIDS_ERR_OUTPUT_OVERFLOW = L"120"; +inline constexpr auto SIDS_SECD = L"SecDeg"; +inline constexpr auto SIDS_SECR = L"SecRad"; +inline constexpr auto SIDS_SECG = L"SecGrad"; +inline constexpr auto SIDS_ASECD = L"InverseSecDeg"; +inline constexpr auto SIDS_ASECR = L"InverseSecRad"; +inline constexpr auto SIDS_ASECG = L"InverseSecGrad"; +inline constexpr auto SIDS_CSCD = L"CscDeg"; +inline constexpr auto SIDS_CSCR = L"CscRad"; +inline constexpr auto SIDS_CSCG = L"CscGrad"; +inline constexpr auto SIDS_ACSCD = L"InverseCscDeg"; +inline constexpr auto SIDS_ACSCR = L"InverseCscRad"; +inline constexpr auto SIDS_ACSCG = L"InverseCscGrad"; +inline constexpr auto SIDS_COTD = L"CotDeg"; +inline constexpr auto SIDS_COTR = L"CotRad"; +inline constexpr auto SIDS_COTG = L"CotGrad"; +inline constexpr auto SIDS_ACOTD = L"InverseCotDeg"; +inline constexpr auto SIDS_ACOTR = L"InverseCotRad"; +inline constexpr auto SIDS_ACOTG = L"InverseCotGrad"; +inline constexpr auto SIDS_SECH = L"Sech"; +inline constexpr auto SIDS_ASECH = L"InverseSech"; +inline constexpr auto SIDS_CSCH = L"Csch"; +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_LOGBASEY = L"LogBaseY"; +inline constexpr auto SIDS_ABS = L"Abs"; +inline constexpr auto SIDS_FLOOR = L"Floor"; +inline constexpr auto SIDS_CEIL = L"Ceil"; +inline constexpr auto SIDS_NAND = L"Nand"; +inline constexpr auto SIDS_NOR = L"Nor"; +inline constexpr auto SIDS_CUBEROOT = L"CubeRoot"; +inline constexpr auto SIDS_PROGRAMMER_MOD = L"ProgrammerMod"; -__declspec(selectany) std::wstring g_sids[] = +// Include the resource key ID from above into this vector to load it into memory for the engine to use +inline constexpr std::array g_sids = { - std::wstring(SIDS_PLUS_MINUS), - std::wstring(SIDS_C), - std::wstring(SIDS_CE), - std::wstring(SIDS_BACKSPACE), - std::wstring(SIDS_DECIMAL_SEPARATOR), - std::wstring(SIDS_EMPTY_STRING), - std::wstring(SIDS_AND), - std::wstring(SIDS_OR), - std::wstring(SIDS_XOR), - std::wstring(SIDS_LSH), - std::wstring(SIDS_RSH), - std::wstring(SIDS_DIVIDE), - std::wstring(SIDS_MULTIPLY), - std::wstring(SIDS_PLUS), - std::wstring(SIDS_MINUS), - std::wstring(SIDS_MOD), - std::wstring(SIDS_YROOT), - std::wstring(SIDS_POW_HAT), - std::wstring(SIDS_INT), - std::wstring(SIDS_ROL), - std::wstring(SIDS_ROR), - std::wstring(SIDS_NOT), - std::wstring(SIDS_SIN), - std::wstring(SIDS_COS), - std::wstring(SIDS_TAN), - std::wstring(SIDS_SINH), - std::wstring(SIDS_COSH), - std::wstring(SIDS_TANH), - std::wstring(SIDS_LN), - std::wstring(SIDS_LOG), - std::wstring(SIDS_SQRT), - std::wstring(SIDS_XPOW2), - std::wstring(SIDS_XPOW3), - std::wstring(SIDS_NFACTORIAL), - std::wstring(SIDS_RECIPROCAL), - std::wstring(SIDS_DMS), - std::wstring(SIDS_CUBEROOT), - std::wstring(SIDS_POWTEN), - std::wstring(SIDS_PERCENT), - std::wstring(SIDS_SCIENTIFIC_NOTATION), - std::wstring(SIDS_PI), - std::wstring(SIDS_EQUAL), - std::wstring(SIDS_MC), - std::wstring(SIDS_MR), - std::wstring(SIDS_MS), - std::wstring(SIDS_MPLUS), - std::wstring(SIDS_MMINUS), - std::wstring(SIDS_EXP), - std::wstring(SIDS_OPEN_PAREN), - std::wstring(SIDS_CLOSE_PAREN), - std::wstring(SIDS_0), - std::wstring(SIDS_1), - std::wstring(SIDS_2), - std::wstring(SIDS_3), - std::wstring(SIDS_4), - std::wstring(SIDS_5), - std::wstring(SIDS_6), - std::wstring(SIDS_7), - std::wstring(SIDS_8), - std::wstring(SIDS_9), - std::wstring(SIDS_A), - std::wstring(SIDS_B), - std::wstring(SIDS_C), - std::wstring(SIDS_D), - std::wstring(SIDS_E), - std::wstring(SIDS_F), - std::wstring(SIDS_FRAC), - std::wstring(SIDS_SIND), - std::wstring(SIDS_COSD), - std::wstring(SIDS_TAND), - std::wstring(SIDS_ASIND), - std::wstring(SIDS_ACOSD), - std::wstring(SIDS_ATAND), - std::wstring(SIDS_SINR), - std::wstring(SIDS_COSR), - std::wstring(SIDS_TANR), - std::wstring(SIDS_ASINR), - std::wstring(SIDS_ACOSR), - std::wstring(SIDS_ATANR), - std::wstring(SIDS_SING), - std::wstring(SIDS_COSG), - std::wstring(SIDS_TANG), - std::wstring(SIDS_ASING), - std::wstring(SIDS_ACOSG), - std::wstring(SIDS_ATANG), - std::wstring(SIDS_ASINH), - std::wstring(SIDS_ACOSH), - std::wstring(SIDS_ATANH), - std::wstring(SIDS_POWE), - std::wstring(SIDS_POWTEN2), - std::wstring(SIDS_SQRT2), - std::wstring(SIDS_SQR), - std::wstring(SIDS_CUBE), - std::wstring(SIDS_CUBERT), - std::wstring(SIDS_FACT), - std::wstring(SIDS_RECIPROC), - std::wstring(SIDS_DEGREES), - std::wstring(SIDS_NEGATE), - std::wstring(SIDS_RSH), - std::wstring(SIDS_DIVIDEBYZERO), - std::wstring(SIDS_DOMAIN), - std::wstring(SIDS_UNDEFINED), - std::wstring(SIDS_POS_INFINITY), - std::wstring(SIDS_NEG_INFINITY), - std::wstring(SIDS_ABORTED), - std::wstring(SIDS_NOMEM), - std::wstring(SIDS_TOOMANY), - std::wstring(SIDS_OVERFLOW), - std::wstring(SIDS_NORESULT), - std::wstring(SIDS_INSUFFICIENT_DATA), - std::wstring(SIDS_ERR_UNK_CH), - std::wstring(SIDS_ERR_UNK_FN), - std::wstring(SIDS_ERR_UNEX_NUM), - std::wstring(SIDS_ERR_UNEX_CH), - std::wstring(SIDS_ERR_UNEX_SZ), - std::wstring(SIDS_ERR_MISMATCH_CLOSE), - std::wstring(SIDS_ERR_UNEX_END), - std::wstring(SIDS_ERR_SG_INV_ERROR), - std::wstring(SIDS_ERR_INPUT_OVERFLOW), - std::wstring(SIDS_ERR_OUTPUT_OVERFLOW) + SIDS_PLUS_MINUS, + SIDS_C, + SIDS_CE, + SIDS_BACKSPACE, + SIDS_DECIMAL_SEPARATOR, + SIDS_EMPTY_STRING, + SIDS_AND, + SIDS_OR, + SIDS_XOR, + SIDS_LSH, + SIDS_RSH, + SIDS_DIVIDE, + SIDS_MULTIPLY, + SIDS_PLUS, + SIDS_MINUS, + SIDS_MOD, + SIDS_YROOT, + SIDS_POW_HAT, + SIDS_INT, + SIDS_ROL, + SIDS_ROR, + SIDS_NOT, + SIDS_SIN, + SIDS_COS, + SIDS_TAN, + SIDS_SINH, + SIDS_COSH, + SIDS_TANH, + SIDS_LN, + SIDS_LOG, + SIDS_SQRT, + SIDS_XPOW2, + SIDS_XPOW3, + SIDS_NFACTORIAL, + SIDS_RECIPROCAL, + SIDS_DMS, + SIDS_POWTEN, + SIDS_PERCENT, + SIDS_SCIENTIFIC_NOTATION, + SIDS_PI, + SIDS_EQUAL, + SIDS_MC, + SIDS_MR, + SIDS_MS, + SIDS_MPLUS, + SIDS_MMINUS, + SIDS_EXP, + SIDS_OPEN_PAREN, + SIDS_CLOSE_PAREN, + SIDS_0, + SIDS_1, + SIDS_2, + SIDS_3, + SIDS_4, + SIDS_5, + SIDS_6, + SIDS_7, + SIDS_8, + SIDS_9, + SIDS_A, + SIDS_B, + SIDS_C, + SIDS_D, + SIDS_E, + SIDS_F, + SIDS_FRAC, + SIDS_SIND, + SIDS_COSD, + SIDS_TAND, + SIDS_ASIND, + SIDS_ACOSD, + SIDS_ATAND, + SIDS_SINR, + SIDS_COSR, + SIDS_TANR, + SIDS_ASINR, + SIDS_ACOSR, + SIDS_ATANR, + SIDS_SING, + SIDS_COSG, + SIDS_TANG, + SIDS_ASING, + SIDS_ACOSG, + SIDS_ATANG, + SIDS_ASINH, + SIDS_ACOSH, + SIDS_ATANH, + SIDS_POWE, + SIDS_POWTEN2, + SIDS_SQRT2, + SIDS_SQR, + SIDS_CUBE, + SIDS_CUBERT, + SIDS_FACT, + SIDS_RECIPROC, + SIDS_DEGREES, + SIDS_NEGATE, + SIDS_RSH, + SIDS_DIVIDEBYZERO, + SIDS_DOMAIN, + SIDS_UNDEFINED, + SIDS_POS_INFINITY, + SIDS_NEG_INFINITY, + SIDS_ABORTED, + SIDS_NOMEM, + SIDS_TOOMANY, + SIDS_OVERFLOW, + SIDS_NORESULT, + SIDS_INSUFFICIENT_DATA, + SIDS_ERR_UNK_CH, + SIDS_ERR_UNK_FN, + SIDS_ERR_UNEX_NUM, + SIDS_ERR_UNEX_CH, + SIDS_ERR_UNEX_SZ, + SIDS_ERR_MISMATCH_CLOSE, + SIDS_ERR_UNEX_END, + SIDS_ERR_SG_INV_ERROR, + SIDS_ERR_INPUT_OVERFLOW, + SIDS_ERR_OUTPUT_OVERFLOW, + SIDS_SECD, + SIDS_SECG, + SIDS_SECR, + SIDS_ASECD, + SIDS_ASECR, + SIDS_ASECG, + SIDS_CSCD, + SIDS_CSCR, + SIDS_CSCG, + SIDS_ACSCD, + SIDS_ACSCR, + SIDS_ACSCG, + SIDS_COTD, + SIDS_COTR, + SIDS_COTG, + SIDS_ACOTD, + SIDS_ACOTR, + SIDS_ACOTG, + SIDS_SECH, + SIDS_ASECH, + SIDS_CSCH, + SIDS_ACSCH, + SIDS_COTH, + SIDS_ACOTH, + SIDS_TWOPOWX, + SIDS_LOGBASEY, + SIDS_ABS, + SIDS_FLOOR, + SIDS_CEIL, + SIDS_NAND, + SIDS_NOR, + SIDS_CUBEROOT, + SIDS_PROGRAMMER_MOD, }; diff --git a/src/CalcManager/Header Files/History.h b/src/CalcManager/Header Files/History.h index 4f82088f..f88f61c3 100644 --- a/src/CalcManager/Header Files/History.h +++ b/src/CalcManager/Header Files/History.h @@ -3,6 +3,7 @@ #pragma once +#include #include "ICalcDisplay.h" #include "IHistoryDisplay.h" #include "Rational.h" @@ -13,15 +14,16 @@ static constexpr size_t MAXPRECDEPTH = 25; // Helper class really a internal class to CCalcEngine, to accumulate each history line of text by collecting the // operands, operator, unary operator etc. Since it is a separate entity, it can be unit tested on its own but does // rely on CCalcEngine calling it in appropriate order. -class CHistoryCollector { +class CHistoryCollector +{ public: - CHistoryCollector(ICalcDisplay *pCalcDisplay, std::shared_ptr pHistoryDisplay, wchar_t decimalSymbol); // Can throw errors + CHistoryCollector(ICalcDisplay* pCalcDisplay, std::shared_ptr pHistoryDisplay, wchar_t decimalSymbol); // Can throw errors ~CHistoryCollector(); void AddOpndToHistory(std::wstring_view numStr, CalcEngine::Rational const& rat, bool fRepetition = false); void RemoveLastOpndFromHistory(); - void AddBinOpToHistory(int nOpCode, bool fNoRepetition = true); - void ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher); - void AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype); + void AddBinOpToHistory(int nOpCode, bool isIntgerMode, bool fNoRepetition = true); + void ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher, bool isIntgerMode); + void AddUnaryOpToHistory(int nOpCode, bool fInv, AngleType angletype); void AddOpenBraceToHistory(); void AddCloseBraceToHistory(); void PushLastOpndStart(int ichOpndStart = -1); @@ -29,26 +31,28 @@ public: void EnclosePrecInversionBrackets(); bool FOpndAddedToHistory(); void CompleteHistoryLine(std::wstring_view numStr); + void CompleteEquation(std::wstring_view numStr); void ClearHistoryLine(std::wstring_view errStr); - int AddCommand(_In_ const std::shared_ptr & spCommand); + int AddCommand(_In_ const std::shared_ptr& spCommand); void UpdateHistoryExpression(uint32_t radix, int32_t precision); void SetDecimalSymbol(wchar_t decimalSymbol); private: std::shared_ptr m_pHistoryDisplay; - ICalcDisplay *m_pCalcDisplay; + ICalcDisplay* m_pCalcDisplay; int m_iCurLineHistStart; // index of the beginning of the current equation // a sort of state, set to the index before 2 after 2 in the expression 2 + 3 say. Useful for auto correct portion of history and for // attaching the unary op around the last operand - int m_lastOpStartIndex; // index of the beginning of the last operand added to the history + int m_lastOpStartIndex; // index of the beginning of the last operand added to the history int m_lastBinOpStartIndex; // index of the beginning of the last binary operator added to the history - std::array m_operandIndices; // Stack of index of opnd's beginning for each '('. A parallel array to m_hnoParNum, but abstracted independently of that + std::array + m_operandIndices; // Stack of index of opnd's beginning for each '('. A parallel array to m_hnoParNum, but abstracted independently of that int m_curOperandIndex; // Stack index for the above stack - bool m_bLastOpndBrace; // iff the last opnd in history is already braced so we can avoid putting another one for unary operator + bool m_bLastOpndBrace; // iff the last opnd in history is already braced so we can avoid putting another one for unary operator wchar_t m_decimalSymbol; - std::shared_ptr>> m_spTokens; - std::shared_ptr>> m_spCommands; + std::shared_ptr>> m_spTokens; + std::shared_ptr>> m_spCommands; private: void ReinitHistory(); @@ -56,5 +60,5 @@ private: void TruncateEquationSzFromIch(int ich); void SetExpressionDisplay(); void InsertSzInEquationSz(std::wstring_view str, int icommandIndex, int ich); - std::shared_ptr> GetOperandCommandsFromString(std::wstring_view numStr); + std::shared_ptr> GetOperandCommandsFromString(std::wstring_view numStr); }; diff --git a/src/CalcManager/Header Files/ICalcDisplay.h b/src/CalcManager/Header Files/ICalcDisplay.h index 2ba57e65..ee32580c 100644 --- a/src/CalcManager/Header Files/ICalcDisplay.h +++ b/src/CalcManager/Header Files/ICalcDisplay.h @@ -3,20 +3,23 @@ #pragma once -#include "../CalculatorVector.h" #include "../ExpressionCommandInterface.h" // Callback interface to be implemented by the clients of CCalcEngine -class ICalcDisplay { +class ICalcDisplay +{ public: virtual void SetPrimaryDisplay(const std::wstring& pszText, bool isError) = 0; virtual void SetIsInError(bool isInError) = 0; - virtual void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) = 0; - virtual void SetParenDisplayText(const std::wstring& pszText) = 0; + virtual void SetExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands) = 0; + virtual void SetParenthesisNumber(_In_ unsigned int count) = 0; virtual void OnNoRightParenAdded() = 0; virtual void MaxDigitsReached() = 0; // not an error but still need to inform UI layer. virtual void BinaryOperatorReceived() = 0; virtual void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) = 0; virtual void SetMemorizedNumbers(const std::vector& memorizedNumbers) = 0; virtual void MemoryItemChanged(unsigned int indexOfMemory) = 0; + virtual void InputChanged() = 0; }; diff --git a/src/CalcManager/Header Files/IHistoryDisplay.h b/src/CalcManager/Header Files/IHistoryDisplay.h index bbadbe87..80794ec5 100644 --- a/src/CalcManager/Header Files/IHistoryDisplay.h +++ b/src/CalcManager/Header Files/IHistoryDisplay.h @@ -1,11 +1,17 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include "../ExpressionCommandInterface.h" + // Callback interface to be implemented by the clients of CCalcEngine if they require equation history -class IHistoryDisplay { +class IHistoryDisplay +{ public: - virtual ~IHistoryDisplay() {}; - virtual unsigned int AddToHistory(_In_ std::shared_ptr>> const &tokens, _In_ std::shared_ptr>> const &commands, _In_ std::wstring_view result) = 0; + virtual ~IHistoryDisplay(){}; + virtual unsigned int AddToHistory( + _In_ std::shared_ptr>> const& tokens, + _In_ std::shared_ptr>> const& commands, + _In_ std::wstring_view result) = 0; }; diff --git a/src/CalcManager/Header Files/Number.h b/src/CalcManager/Header Files/Number.h index c67aa108..769e3acf 100644 --- a/src/CalcManager/Header Files/Number.h +++ b/src/CalcManager/Header Files/Number.h @@ -3,6 +3,7 @@ #pragma once +#include #include "Ratpack/ratpak.h" namespace CalcEngine diff --git a/src/CalcManager/Header Files/RadixType.h b/src/CalcManager/Header Files/RadixType.h index 3f7f2456..2ae0f90a 100644 --- a/src/CalcManager/Header Files/RadixType.h +++ b/src/CalcManager/Header Files/RadixType.h @@ -4,10 +4,10 @@ #pragma once // This is expected to be in same order as IDM_HEX, IDM_DEC, IDM_OCT, IDM_BIN -enum eRADIX_TYPE { - HEX_RADIX, - DEC_RADIX, - OCT_RADIX, - BIN_RADIX +enum class RadixType +{ + Hex, + Decimal, + Octal, + Binary }; -typedef enum eRADIX_TYPE RADIX_TYPE; diff --git a/src/CalcManager/Header Files/Rational.h b/src/CalcManager/Header Files/Rational.h index 8ded3c27..25df92bd 100644 --- a/src/CalcManager/Header Files/Rational.h +++ b/src/CalcManager/Header Files/Rational.h @@ -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: diff --git a/src/CalcManager/Header Files/RationalMath.h b/src/CalcManager/Header Files/RationalMath.h index b52c1c5f..f3ba56ec 100644 --- a/src/CalcManager/Header Files/RationalMath.h +++ b/src/CalcManager/Header Files/RationalMath.h @@ -13,6 +13,7 @@ namespace CalcEngine::RationalMath Rational Pow(Rational const& base, Rational const& pow); Rational Root(Rational const& base, Rational const& root); Rational Fact(Rational const& rat); + Rational Mod(Rational const& a, Rational const& b); Rational Exp(Rational const& rat); Rational Log(Rational const& rat); @@ -21,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); diff --git a/src/CalcManager/NumberFormattingUtils.cpp b/src/CalcManager/NumberFormattingUtils.cpp new file mode 100644 index 00000000..63563972 --- /dev/null +++ b/src/CalcManager/NumberFormattingUtils.cpp @@ -0,0 +1,80 @@ +#include "pch.h" +#include "NumberFormattingUtils.h" + +using namespace std; + +namespace CalcManager::NumberFormattingUtils +{ + /// + /// Trims out any trailing zeros or decimals in the given input string + /// + /// number to trim + void TrimTrailingZeros(_Inout_ wstring& number) + { + if (number.find(L'.') == wstring::npos) + { + return; + } + + if (auto i = number.find_last_not_of(L'0'); i != wstring::npos) + { + number.erase(number.cbegin() + i + 1, number.cend()); + } + + if (number.back() == L'.') + { + number.pop_back(); + } + } + + /// + /// Get number of digits (whole number part + decimal part) + /// the number + unsigned int GetNumberDigits(wstring value) + { + TrimTrailingZeros(value); + unsigned int numberSignificantDigits = static_cast(value.size()); + if (value.find(L'.') != wstring::npos) + { + --numberSignificantDigits; + } + if (value.find(L'-') != wstring::npos) + { + --numberSignificantDigits; + } + return numberSignificantDigits; + } + + /// + /// Get number of digits (whole number part only) + /// the number + unsigned int GetNumberDigitsWholeNumberPart(double value) + { + return value == 0 ? 1u : static_cast(1 + max(0.0, log10(abs(value)))); + } + + /// + /// Rounds the given double to the given number of significant digits + /// + /// input double + /// unsigned int number of significant digits to round to + wstring RoundSignificantDigits(double num, unsigned int numSignificant) + { + wstringstream out(wstringstream::out); + out << fixed; + out.precision(numSignificant); + out << num; + return out.str(); + } + + /// + /// Convert a Number to Scientific Notation + /// + /// number to convert + wstring ToScientificNumber(double number) + { + wstringstream out(wstringstream::out); + out << scientific << number; + return out.str(); + } +} diff --git a/src/CalcManager/NumberFormattingUtils.h b/src/CalcManager/NumberFormattingUtils.h new file mode 100644 index 00000000..2f13ebfc --- /dev/null +++ b/src/CalcManager/NumberFormattingUtils.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include + +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, unsigned int numberSignificantDigits); + std::wstring ToScientificNumber(double number); +} diff --git a/src/CalcManager/Ratpack/CalcErr.h b/src/CalcManager/Ratpack/CalcErr.h index b420a766..ebdc6730 100644 --- a/src/CalcManager/Ratpack/CalcErr.h +++ b/src/CalcManager/Ratpack/CalcErr.h @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#pragma once + // CalcErr.h // // Defines the error codes thrown by ratpak and caught by Calculator @@ -24,7 +26,7 @@ // R - Reserved - not currently used for anything // // r - reserved portion of the facility code. Reserved for internal -// use. Used to indicate HRESULT values that are not status +// use. Used to indicate int32_t values that are not status // values, but are instead message ids for display strings. // // Facility - is the facility code @@ -34,49 +36,50 @@ // This format is based loosely on an OLE HRESULT and is compatible with the // SUCCEEDED and FAILED macros as well as the HRESULT_CODE macro +typedef int32_t ResultCode; + // CALC_E_DIVIDEBYZERO // // The current operation would require a divide by zero to complete -#define CALC_E_DIVIDEBYZERO ((DWORD)0x80000000) +static constexpr uint32_t CALC_E_DIVIDEBYZERO = (uint32_t)0x80000000; // CALC_E_DOMAIN // // The given input is not within the domain of this function -#define CALC_E_DOMAIN ((DWORD)0x80000001) +static constexpr uint32_t CALC_E_DOMAIN = (uint32_t)0x80000001; // CALC_E_INDEFINITE // // The result of this function is undefined -#define CALC_E_INDEFINITE ((DWORD)0x80000002) +static constexpr uint32_t CALC_E_INDEFINITE = (uint32_t)0x80000002; // CALC_E_POSINFINITY // // The result of this function is Positive Infinity. -#define CALC_E_POSINFINITY ((DWORD)0x80000003) +static constexpr uint32_t CALC_E_POSINFINITY = (uint32_t)0x80000003; // CALC_E_NEGINFINITY // // The result of this function is Negative Infinity -#define CALC_E_NEGINFINITY ((DWORD)0x80000004) +static constexpr uint32_t CALC_E_NEGINFINITY = (uint32_t)0x80000004; // CALC_E_INVALIDRANGE // // The given input is within the domain of the function but is beyond // the range for which calc can successfully compute the answer -#define CALC_E_INVALIDRANGE ((DWORD)0x80000006) +static constexpr uint32_t CALC_E_INVALIDRANGE = (uint32_t)0x80000006; // CALC_E_OUTOFMEMORY // // There is not enough free memory to complete the requested function -#define CALC_E_OUTOFMEMORY ((DWORD)0x80000007) +static constexpr uint32_t CALC_E_OUTOFMEMORY = (uint32_t)0x80000007; // CALC_E_OVERFLOW // // The result of this operation is an overflow -#define CALC_E_OVERFLOW ((DWORD)0x80000008) +static constexpr uint32_t CALC_E_OVERFLOW = (uint32_t)0x80000008; // CALC_E_NORESULT // // The result of this operation is undefined -#define CALC_E_NORESULT ((DWORD)0x80000009) - +static constexpr uint32_t CALC_E_NORESULT = (uint32_t)0x80000009; diff --git a/src/CalcManager/Ratpack/basex.cpp b/src/CalcManager/Ratpack/basex.cpp index 1d4a7303..27e6a223 100644 --- a/src/CalcManager/Ratpack/basex.cpp +++ b/src/CalcManager/Ratpack/basex.cpp @@ -14,10 +14,10 @@ // internal base is a power of 2. // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" +#include // for memmove -void _mulnumx( PNUMBER *pa, PNUMBER b ); +void _mulnumx(PNUMBER* pa, PNUMBER b); //---------------------------------------------------------------------------- // @@ -34,30 +34,30 @@ void _mulnumx( PNUMBER *pa, PNUMBER b ); // //---------------------------------------------------------------------------- -void __inline mulnumx( PNUMBER *pa, PNUMBER b ) +void mulnumx(_Inout_ PNUMBER* pa, _In_ PNUMBER b) { - if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) - { + if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) + { // If b is not one we multiply - if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 ) - { - // pa and b are both non-one. - _mulnumx( pa, b ); - } - else - { - // if pa is one and b isn't just copy b. and adjust the sign. - long sign = (*pa)->sign; - DUPNUM(*pa,b); - (*pa)->sign *= sign; - } - } - else + if ((*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0) { + // pa and b are both non-one. + _mulnumx(pa, b); + } + else + { + // if pa is one and b isn't just copy b. and adjust the sign. + int32_t sign = (*pa)->sign; + DUPNUM(*pa, b); + (*pa)->sign *= sign; + } + } + else + { // B is +/- 1, But we do have to set the sign. (*pa)->sign *= b->sign; - } + } } //---------------------------------------------------------------------------- @@ -76,29 +76,29 @@ void __inline mulnumx( PNUMBER *pa, PNUMBER b ) // //---------------------------------------------------------------------------- -void _mulnumx( PNUMBER *pa, PNUMBER b ) +void _mulnumx(PNUMBER* pa, PNUMBER b) { - PNUMBER c= nullptr; // c will contain the result. - PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa - MANTTYPE *ptra; // ptra is a pointer to the mantissa of a. - MANTTYPE *ptrb; // ptrb is a pointer to the mantissa of b. - MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c. - MANTTYPE *ptrcoffset; // ptrcoffset, is the anchor location of the next - // single digit multiply partial result. - long iadigit=0; // Index of digit being used in the first number. - long ibdigit=0; // Index of digit being used in the second number. - MANTTYPE da=0; // da is the digit from the fist number. - TWO_MANTTYPE cy=0; // cy is the carry resulting from the addition of - // a multiplied row into the result. - TWO_MANTTYPE mcy=0; // mcy is the resultant from a single - // multiply, AND the carry of that multiply. - long icdigit=0; // Index of digit being calculated in final result. + PNUMBER c = nullptr; // c will contain the result. + PNUMBER a = nullptr; // a is the dereferenced number pointer from *pa + MANTTYPE* ptra; // ptra is a pointer to the mantissa of a. + MANTTYPE* ptrb; // ptrb is a pointer to the mantissa of b. + MANTTYPE* ptrc; // ptrc is a pointer to the mantissa of c. + MANTTYPE* ptrcoffset; // ptrcoffset, is the anchor location of the next + // single digit multiply partial result. + int32_t iadigit = 0; // Index of digit being used in the first number. + int32_t ibdigit = 0; // Index of digit being used in the second number. + MANTTYPE da = 0; // da is the digit from the fist number. + TWO_MANTTYPE cy = 0; // cy is the carry resulting from the addition of + // a multiplied row into the result. + TWO_MANTTYPE mcy = 0; // mcy is the resultant from a single + // multiply, AND the carry of that multiply. + int32_t icdigit = 0; // Index of digit being calculated in final result. - a=*pa; + a = *pa; ibdigit = a->cdigit + b->cdigit - 1; - createnum( c, ibdigit + 1 ); + createnum(c, ibdigit + 1); c->cdigit = ibdigit; c->sign = a->sign * b->sign; @@ -106,36 +106,35 @@ void _mulnumx( PNUMBER *pa, PNUMBER b ) ptra = a->mant; ptrcoffset = c->mant; - for ( iadigit = a->cdigit; iadigit > 0; iadigit-- ) + for (iadigit = a->cdigit; iadigit > 0; iadigit--) { - da = *ptra++; + da = *ptra++; ptrb = b->mant; // Shift ptrc, and ptrcoffset, one for each digit ptrc = ptrcoffset++; - for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- ) + for (ibdigit = b->cdigit; ibdigit > 0; ibdigit--) { cy = 0; - mcy = (DWORDLONG)da * (*ptrb); - if ( mcy ) + mcy = (uint64_t)da * (*ptrb); + if (mcy) { icdigit = 0; - if ( ibdigit == 1 && iadigit == 1 ) + if (ibdigit == 1 && iadigit == 1) { c->cdigit++; } } // If result is nonzero, or while result of carry is nonzero... - while ( mcy || cy ) + while (mcy || cy) { - // update carry from addition(s) and multiply. - cy += (TWO_MANTTYPE)ptrc[icdigit]+((DWORD)mcy&((DWORD)~BASEX)); + cy += (TWO_MANTTYPE)ptrc[icdigit] + ((uint32_t)mcy & ((uint32_t)~BASEX)); // update result digit from - ptrc[icdigit++]=(MANTTYPE)((DWORD)cy&((DWORD)~BASEX)); + ptrc[icdigit++] = (MANTTYPE)((uint32_t)cy & ((uint32_t)~BASEX)); // update carries from mcy >>= BASEXPWR; @@ -144,25 +143,24 @@ void _mulnumx( PNUMBER *pa, PNUMBER b ) ptrb++; ptrc++; - } } // prevent different kinds of zeros, by stripping leading duplicate zeros. // digits are in order of increasing significance. - while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 ) - { + while (c->cdigit > 1 && c->mant[c->cdigit - 1] == 0) + { c->cdigit--; - } + } - destroynum( *pa ); - *pa=c; + destroynum(*pa); + *pa = c; } //----------------------------------------------------------------------------- // -// FUNCTION: numpowlongx +// FUNCTION: numpowi32x // -// ARGUMENTS: root as number power as long +// ARGUMENTS: root as number power as int32_t // number. // // RETURN: None root is changed. @@ -174,34 +172,33 @@ void _mulnumx( PNUMBER *pa, PNUMBER b ) // //----------------------------------------------------------------------------- -void numpowlongx( _Inout_ PNUMBER *proot, _In_ long power ) +void numpowi32x(_Inout_ PNUMBER* proot, int32_t power) { - PNUMBER lret = longtonum( 1, BASEX ); + PNUMBER lret = i32tonum(1, BASEX); // Once the power remaining is zero we are done. - while ( power > 0 ) - { + while (power > 0) + { // If this bit in the power decomposition is on, multiply the result // by the root number. - if ( power & 1 ) - { - mulnumx( &lret, *proot ); - } + if (power & 1) + { + mulnumx(&lret, *proot); + } // multiply the root number by itself to scale for the next bit (i.e. // square it. - mulnumx( proot, *proot ); + mulnumx(proot, *proot); // move the next bit of the power into place. power >>= 1; - } - destroynum( *proot ); - *proot=lret; - + } + destroynum(*proot); + *proot = lret; } -void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision); +void _divnumx(PNUMBER* pa, PNUMBER b, int32_t precision); //---------------------------------------------------------------------------- // @@ -218,30 +215,30 @@ void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision); // //---------------------------------------------------------------------------- -void __inline divnumx( PNUMBER *pa, PNUMBER b, int32_t precision) +void divnumx(_Inout_ PNUMBER* pa, _In_ PNUMBER b, int32_t precision) { - if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) - { + if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) + { // b is not one. - if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 ) - { - // pa and b are both not one. - _divnumx( pa, b, precision); - } - else - { - // if pa is one and b is not one, just copy b, and adjust the sign. - long sign = (*pa)->sign; - DUPNUM(*pa,b); - (*pa)->sign *= sign; - } - } - else + if ((*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0) { + // pa and b are both not one. + _divnumx(pa, b, precision); + } + else + { + // if pa is one and b is not one, just copy b, and adjust the sign. + int32_t sign = (*pa)->sign; + DUPNUM(*pa, b); + (*pa)->sign *= sign; + } + } + else + { // b is one so don't divide, but set the sign. (*pa)->sign *= b->sign; - } + } } //---------------------------------------------------------------------------- @@ -257,111 +254,109 @@ void __inline divnumx( PNUMBER *pa, PNUMBER b, int32_t precision) // //---------------------------------------------------------------------------- -void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision) +void _divnumx(PNUMBER* pa, PNUMBER b, int32_t precision) { - PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa - PNUMBER c= nullptr; // c will contain the result. + PNUMBER a = nullptr; // a is the dereferenced number pointer from *pa + PNUMBER c = nullptr; // c will contain the result. PNUMBER lasttmp = nullptr; // lasttmp allows a backup when the algorithm - // guesses one bit too far. + // guesses one bit too far. PNUMBER tmp = nullptr; // current guess being worked on for divide. PNUMBER rem = nullptr; // remainder after applying guess. - long cdigits; // count of digits for answer. - MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c. + int32_t cdigits; // count of digits for answer. + MANTTYPE* ptrc; // ptrc is a pointer to the mantissa of c. - long thismax = precision + g_ratio; // set a maximum number of internal digits - // to shoot for in the divide. + int32_t thismax = precision + g_ratio; // set a maximum number of internal digits + // to shoot for in the divide. - a=*pa; - if ( thismax < a->cdigit ) - { + a = *pa; + if (thismax < a->cdigit) + { // a has more digits than precision specified, bump up digits to shoot // for. thismax = a->cdigit; - } + } - if ( thismax < b->cdigit ) - { + if (thismax < b->cdigit) + { // b has more digits than precision specified, bump up digits to shoot // for. thismax = b->cdigit; - } + } // Create c (the divide answer) and set up exponent and sign. - createnum( c, thismax + 1 ); - c->exp = (a->cdigit+a->exp) - (b->cdigit+b->exp) + 1; + createnum(c, thismax + 1); + c->exp = (a->cdigit + a->exp) - (b->cdigit + b->exp) + 1; c->sign = a->sign * b->sign; ptrc = c->mant + thismax; cdigits = 0; - DUPNUM( rem, a ); + DUPNUM(rem, a); rem->sign = b->sign; rem->exp = b->cdigit + b->exp - rem->cdigit; - while ( cdigits++ < thismax && !zernum(rem) ) - { - long digit = 0; + while (cdigits++ < thismax && !zernum(rem)) + { + int32_t digit = 0; *ptrc = 0; - while ( !lessnum( rem, b ) ) - { + while (!lessnum(rem, b)) + { digit = 1; - DUPNUM( tmp, b ); - destroynum( lasttmp ); - lasttmp=longtonum( 0, BASEX ); - while ( lessnum( tmp, rem ) ) - { - destroynum( lasttmp ); - DUPNUM(lasttmp,tmp); - addnum( &tmp, tmp, BASEX ); + DUPNUM(tmp, b); + destroynum(lasttmp); + lasttmp = i32tonum(0, BASEX); + while (lessnum(tmp, rem)) + { + destroynum(lasttmp); + DUPNUM(lasttmp, tmp); + addnum(&tmp, tmp, BASEX); digit *= 2; - } - if ( lessnum( rem, tmp ) ) - { + } + if (lessnum(rem, tmp)) + { // too far, back up... - destroynum( tmp ); + destroynum(tmp); digit /= 2; - tmp=lasttmp; - lasttmp= nullptr; - } + tmp = lasttmp; + lasttmp = nullptr; + } tmp->sign *= -1; - addnum( &rem, tmp, BASEX ); - destroynum( tmp ); - destroynum( lasttmp ); + addnum(&rem, tmp, BASEX); + destroynum(tmp); + destroynum(lasttmp); *ptrc |= digit; - } + } rem->exp++; ptrc--; - } + } cdigits--; - if ( c->mant != ++ptrc ) - { - memmove( c->mant, ptrc, (int)(cdigits*sizeof(MANTTYPE)) ); - } + if (c->mant != ++ptrc) + { + memmove(c->mant, ptrc, (int)(cdigits * sizeof(MANTTYPE))); + } - if ( !cdigits ) - { + if (!cdigits) + { // A zero, make sure no weird exponents creep in c->exp = 0; c->cdigit = 1; - } + } else - { + { c->cdigit = cdigits; c->exp -= cdigits; // prevent different kinds of zeros, by stripping leading duplicate // zeros. digits are in order of increasing significance. - while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 ) - { + while (c->cdigit > 1 && c->mant[c->cdigit - 1] == 0) + { c->cdigit--; - } } + } - destroynum( rem ); + destroynum(rem); - destroynum( *pa ); - *pa=c; + destroynum(*pa); + *pa = c; } - - diff --git a/src/CalcManager/Ratpack/conv.cpp b/src/CalcManager/Ratpack/conv.cpp index ff7b18d5..4f7609de 100644 --- a/src/CalcManager/Ratpack/conv.cpp +++ b/src/CalcManager/Ratpack/conv.cpp @@ -11,13 +11,16 @@ // Description // // Contains conversion, input and output routines for numbers rationals -// and longs. +// and i32s. // // // //--------------------------------------------------------------------------- -#include "pch.h" +#include +#include "winerror_cross_platform.h" +#include +#include // for memmove, memcpy #include "ratpak.h" using namespace std; @@ -29,12 +32,76 @@ static constexpr wstring_view DIGITS = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabc // ratio of internal 'digits' to output 'digits' // Calculated elsewhere as part of initialization and when base is changed -long g_ratio; // int(log(2L^BASEXPWR)/log(radix)) +int32_t g_ratio; // int(log(2L^BASEXPWR)/log(radix)) // Default decimal separator wchar_t g_decimalSeparator = L'.'; +// The following defines and Calc_ULong* functions were taken from +// https://github.com/dotnet/coreclr/blob/8b1595b74c943b33fa794e63e440e6f4c9679478/src/pal/inc/rt/intsafe.h +// under MIT License +// See also +// * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros +// * https://sourceforge.net/p/predef/wiki/Architectures/ +#if defined(MIDL_PASS) || defined(RC_INVOKED) || defined(_M_CEE_PURE) || defined(_M_AMD64) || defined(__ARM_ARCH) || defined(__x86_64__) || defined(_M_ARM64) + +#ifndef Calc_UInt32x32To64 +#define Calc_UInt32x32To64(a, b) ((uint64_t)((uint32_t)(a)) * (uint64_t)((uint32_t)(b))) +#endif + +#elif defined(_M_IX86) || defined(__i386__) || defined(_M_ARM) + +#ifndef Calc_UInt32x32To64 +#define Calc_UInt32x32To64(a, b) (uint64_t)((uint64_t)(uint32_t)(a) * (uint32_t)(b)) +#endif +#else + +#error Must define a target architecture. + +#endif + +#define CALC_INTSAFE_E_ARITHMETIC_OVERFLOW ((int32_t)0x80070216L) // 0x216 = 534 = ERROR_ARITHMETIC_OVERFLOW +#define CALC_ULONG_ERROR ((uint32_t)0xffffffffU) + +namespace +{ + int32_t Calc_ULongAdd(_In_ uint32_t ulAugend, _In_ uint32_t ulAddend, _Out_ uint32_t* pulResult) + { + int32_t hr = CALC_INTSAFE_E_ARITHMETIC_OVERFLOW; + *pulResult = CALC_ULONG_ERROR; + + if ((ulAugend + ulAddend) >= ulAugend) + { + *pulResult = (ulAugend + ulAddend); + hr = S_OK; + } + + return hr; + } + + int32_t Calc_ULongLongToULong(_In_ uint64_t ullOperand, _Out_ uint32_t* pulResult) + { + int32_t hr = CALC_INTSAFE_E_ARITHMETIC_OVERFLOW; + *pulResult = CALC_ULONG_ERROR; + + if (ullOperand <= UINT32_MAX) + { + *pulResult = (uint32_t)ullOperand; + hr = S_OK; + } + + return hr; + } + + int32_t Calc_ULongMult(_In_ uint32_t ulMultiplicand, _In_ uint32_t ulMultiplier, _Out_ uint32_t* pulResult) + { + uint64_t ull64Result = Calc_UInt32x32To64(ulMultiplicand, ulMultiplier); + + return Calc_ULongLongToULong(ull64Result, pulResult); + } +} + // Used to strip trailing zeros, and prevent combinatorial explosions -bool stripzeroesnum(_Inout_ PNUMBER pnum, long starting); +bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting); void SetDecimalSeparator(wchar_t decimalSeparator) { @@ -61,9 +128,9 @@ void* zmalloc(size_t a) // //----------------------------------------------------------------------------- -void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER * const src) +void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER* const src) { - memcpy(dest, src, (int)(sizeof(NUMBER) + ((src)->cdigit)*(sizeof(MANTTYPE)))); + memcpy(dest, src, (int)(sizeof(NUMBER) + ((src)->cdigit) * (sizeof(MANTTYPE)))); } //----------------------------------------------------------------------------- @@ -78,16 +145,15 @@ void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER * const src) // //----------------------------------------------------------------------------- -void _destroynum( _In_ PNUMBER pnum ) +void _destroynum(_Frees_ptr_opt_ PNUMBER pnum) { - if ( pnum != nullptr) - { - free( pnum ); - } + if (pnum != nullptr) + { + free(pnum); + } } - //----------------------------------------------------------------------------- // // FUNCTION: _destroyrat @@ -101,18 +167,17 @@ void _destroynum( _In_ PNUMBER pnum ) // //----------------------------------------------------------------------------- -void _destroyrat( _In_ PRAT prat ) +void _destroyrat(_Frees_ptr_opt_ PRAT prat) { - if ( prat != nullptr) - { - destroynum( prat->pp ); - destroynum( prat->pq ); - free( prat ); - } + if (prat != nullptr) + { + destroynum(prat->pp); + destroynum(prat->pq); + free(prat); + } } - //----------------------------------------------------------------------------- // // FUNCTION: _createnum @@ -125,28 +190,27 @@ void _destroyrat( _In_ PRAT prat ) // //----------------------------------------------------------------------------- -PNUMBER _createnum( _In_ ULONG size ) +PNUMBER _createnum(_In_ uint32_t size) { - PNUMBER pnumret= nullptr; - ULONG cbAlloc; + PNUMBER pnumret = nullptr; + uint32_t cbAlloc; // sizeof( MANTTYPE ) is the size of a 'digit' - if (SUCCEEDED(ULongAdd(size, 1, &cbAlloc)) && - SUCCEEDED(ULongMult(cbAlloc, sizeof(MANTTYPE), &cbAlloc)) && - SUCCEEDED(ULongAdd(cbAlloc, sizeof(NUMBER), &cbAlloc))) + if (SUCCEEDED(Calc_ULongAdd(size, 1, &cbAlloc)) && SUCCEEDED(Calc_ULongMult(cbAlloc, sizeof(MANTTYPE), &cbAlloc)) + && SUCCEEDED(Calc_ULongAdd(cbAlloc, sizeof(NUMBER), &cbAlloc))) { - pnumret = (PNUMBER)zmalloc( cbAlloc ); - if ( pnumret == nullptr) + pnumret = (PNUMBER)zmalloc(cbAlloc); + if (pnumret == nullptr) { - throw( CALC_E_OUTOFMEMORY ); + throw(CALC_E_OUTOFMEMORY); } } else { - throw( CALC_E_INVALIDRANGE ); + throw(CALC_E_INVALIDRANGE); } - return( pnumret ); + return (pnumret); } //----------------------------------------------------------------------------- @@ -163,25 +227,22 @@ PNUMBER _createnum( _In_ ULONG size ) // //----------------------------------------------------------------------------- - -PRAT _createrat( void ) +PRAT _createrat(void) { - PRAT prat= nullptr; + PRAT prat = nullptr; - prat = (PRAT)zmalloc( sizeof( RAT ) ); + prat = (PRAT)zmalloc(sizeof(RAT)); - if ( prat == nullptr) + if (prat == nullptr) { - throw( CALC_E_OUTOFMEMORY ); + throw(CALC_E_OUTOFMEMORY); } prat->pp = nullptr; prat->pq = nullptr; - return( prat ); + return (prat); } - - //----------------------------------------------------------------------------- // // FUNCTION: numtorat @@ -197,37 +258,34 @@ PRAT _createrat( void ) // //----------------------------------------------------------------------------- -PRAT numtorat( _In_ PNUMBER pin, uint32_t radix) +PRAT numtorat(_In_ PNUMBER pin, uint32_t radix) { - PNUMBER pnRadixn= nullptr; - DUPNUM( pnRadixn, pin ); + PNUMBER pnRadixn = nullptr; + DUPNUM(pnRadixn, pin); - PNUMBER qnRadixn=longtonum( 1, radix); + PNUMBER qnRadixn = i32tonum(1, radix); // Ensure p and q start out as integers. - if ( pnRadixn->exp < 0 ) - { + if (pnRadixn->exp < 0) + { qnRadixn->exp -= pnRadixn->exp; pnRadixn->exp = 0; - } + } - PRAT pout= nullptr; + PRAT pout = nullptr; createrat(pout); // There is probably a better way to do this. pout->pp = numtonRadixx(pnRadixn, radix); pout->pq = numtonRadixx(qnRadixn, radix); + destroynum(pnRadixn); + destroynum(qnRadixn); - destroynum( pnRadixn ); - destroynum( qnRadixn ); - - return( pout ); + return (pout); } - - //---------------------------------------------------------------------------- // // FUNCTION: nRadixxtonum @@ -242,49 +300,44 @@ PRAT numtorat( _In_ PNUMBER pin, uint32_t radix) // //---------------------------------------------------------------------------- -PNUMBER nRadixxtonum( _In_ PNUMBER a, uint32_t radix, int32_t precision) +PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision) { - unsigned long bitmask; - unsigned long cdigits; - MANTTYPE *ptr; - - PNUMBER sum = longtonum( 0, radix ); - PNUMBER powofnRadix = longtonum( BASEX, radix ); + 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; - if ( cdigits > (unsigned long)a->cdigit ) - { - cdigits = (unsigned long)a->cdigit; - } + uint32_t cdigits = precision + 1; + if (cdigits > (uint32_t)a->cdigit) + { + cdigits = (uint32_t)a->cdigit; + } // scale by the internal base to the internal exponent offset of the LSD - numpowlong( &powofnRadix, a->exp + (a->cdigit - cdigits), radix, 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) { - addnum( &sum, sum, radix ); - if ( *ptr & bitmask ) - { sum->mant[0] |= 1; - } } } + } // Scale answer by power of internal exponent. - mulnum( &sum, powofnRadix, radix ); + mulnum(&sum, powofnRadix, radix); - destroynum( powofnRadix ); + destroynum(powofnRadix); sum->sign = a->sign; - return( sum ); + return (sum); } //----------------------------------------------------------------------------- @@ -303,39 +356,37 @@ PNUMBER nRadixxtonum( _In_ PNUMBER a, uint32_t radix, int32_t precision) PNUMBER numtonRadixx(_In_ PNUMBER a, uint32_t radix) { - PNUMBER pnumret = longtonum(0, BASEX); // pnumret is the number in internal form. - PNUMBER num_radix = longtonum(radix, BASEX); - MANTTYPE *ptrdigit = a->mant; // pointer to digit being worked on. + PNUMBER pnumret = i32tonum(0, BASEX); // pnumret is the number in internal form. + PNUMBER num_radix = i32tonum(radix, BASEX); + MANTTYPE* ptrdigit = a->mant; // pointer to digit being worked on. // Digits are in reverse order, back over them LSD first. - ptrdigit += a->cdigit-1; + ptrdigit += a->cdigit - 1; - PNUMBER thisdigit = nullptr; // thisdigit holds the current digit of a - // being summed into result. - long idigit; // idigit is the iterate of digits in a. - for ( idigit = 0; idigit < a->cdigit; idigit++ ) - { - mulnumx( &pnumret, num_radix); + PNUMBER thisdigit = nullptr; // thisdigit holds the current digit of a + for (int32_t idigit = 0; idigit < a->cdigit; idigit++) + { + mulnumx(&pnumret, num_radix); // WARNING: // This should just smack in each digit into a 'special' thisdigit. // and not do the overhead of recreating the number type each time. - thisdigit = longtonum( *ptrdigit--, BASEX ); - addnum( &pnumret, thisdigit, BASEX ); - destroynum( thisdigit ); - } + thisdigit = i32tonum(*ptrdigit--, BASEX); + addnum(&pnumret, thisdigit, BASEX); + destroynum(thisdigit); + } // Calculate the exponent of the external base for scaling. - numpowlongx( &num_radix, a->exp ); + numpowi32x(&num_radix, a->exp); // ... and scale the result. - mulnumx( &pnumret, num_radix); + mulnumx(&pnumret, num_radix); destroynum(num_radix); // And propagate the sign. pnumret->sign = a->sign; - return( pnumret ); + return (pnumret); } //----------------------------------------------------------------------------- @@ -391,7 +442,7 @@ PRAT StringToRat(bool mantissaIsNegative, wstring_view mantissa, bool exponentIs } // Deal with exponent - long expt = 0; + int32_t expt = 0; if (!exponent.empty()) { // Exponent specified, convert to number form. @@ -404,18 +455,18 @@ PRAT StringToRat(bool mantissaIsNegative, wstring_view mantissa, bool exponentIs } // Convert exponent number form to native integral form, and cleanup. - expt = numtolong(numExp, radix); + expt = numtoi32(numExp, radix); destroynum(numExp); } // Convert native integral exponent form to rational multiplier form. - PNUMBER pnumexp = longtonum(radix, BASEX); - numpowlongx(&pnumexp, abs(expt)); + PNUMBER pnumexp = i32tonum(radix, BASEX); + numpowi32x(&pnumexp, abs(expt)); PRAT pratexp = nullptr; createrat(pratexp); DUPNUM(pratexp->pp, pnumexp); - pratexp->pq = longtonum(1, BASEX); + pratexp->pq = i32tonum(1, BASEX); destroynum(pnumexp); if (exponentIsNegative) @@ -491,80 +542,66 @@ static constexpr uint8_t EX = 4; static constexpr uint8_t START = 0; static constexpr uint8_t MANTS = 1; -static constexpr uint8_t LZ = 2; -static constexpr uint8_t LZDP = 3; -static constexpr uint8_t LD = 4; -static constexpr uint8_t DZ = 5; -static constexpr uint8_t DD = 6; -static constexpr uint8_t DDP = 7; -static constexpr uint8_t EXPB = 8; -static constexpr uint8_t EXPS = 9; -static constexpr uint8_t EXPD = 10; +static constexpr uint8_t LZ = 2; +static constexpr uint8_t LZDP = 3; +static constexpr uint8_t LD = 4; +static constexpr uint8_t DZ = 5; +static constexpr uint8_t DD = 6; +static constexpr uint8_t DDP = 7; +static constexpr uint8_t EXPB = 8; +static constexpr uint8_t EXPS = 9; +static constexpr uint8_t EXPD = 10; static constexpr uint8_t EXPBZ = 11; static constexpr uint8_t EXPSZ = 12; static constexpr uint8_t EXPDZ = 13; -static constexpr uint8_t ERR = 14; +static constexpr uint8_t ERR = 14; -#if defined( DEBUG ) -char *statestr[] = { - "START", - "MANTS", - "LZ", - "LZDP", - "LD", - "DZ", - "DD", - "DDP", - "EXPB", - "EXPS", - "EXPD", - "EXPBZ", - "EXPSZ", - "EXPDZ", - "ERR", +#if defined(DEBUG) +char* statestr[] = { + "START", "MANTS", "LZ", "LZDP", "LD", "DZ", "DD", "DDP", "EXPB", "EXPS", "EXPD", "EXPBZ", "EXPSZ", "EXPDZ", "ERR", }; #endif // New state is machine[state][terminal] -static constexpr uint8_t machine[ERR+1][EX+1]= { +static constexpr uint8_t machine[ERR + 1][EX + 1] = { // DP, ZR, NZ, SG, EX // START - { LZDP, LZ, LD, MANTS, ERR }, + { LZDP, LZ, LD, MANTS, ERR }, // MANTS - { LZDP, LZ, LD, ERR, ERR }, + { LZDP, LZ, LD, ERR, ERR }, // LZ - { LZDP, LZ, LD, ERR, EXPBZ }, + { LZDP, LZ, LD, ERR, EXPBZ }, // LZDP - { ERR, DZ, DD, ERR, EXPB }, + { ERR, DZ, DD, ERR, EXPB }, // LD - { DDP, LD, LD, ERR, EXPB }, + { DDP, LD, LD, ERR, EXPB }, // DZ - { ERR, DZ, DD, ERR, EXPBZ }, + { ERR, DZ, DD, ERR, EXPBZ }, // DD - { ERR, DD, DD, ERR, EXPB }, + { ERR, DD, DD, ERR, EXPB }, // DDP - { ERR, DD, DD, ERR, EXPB }, + { ERR, DD, DD, ERR, EXPB }, // EXPB - { ERR, EXPD, EXPD, EXPS, ERR }, + { ERR, EXPD, EXPD, EXPS, ERR }, // EXPS - { ERR, EXPD, EXPD, ERR, ERR }, + { ERR, EXPD, EXPD, ERR, ERR }, // EXPD - { ERR, EXPD, EXPD, ERR, ERR }, + { ERR, EXPD, EXPD, ERR, ERR }, // EXPBZ - { ERR, EXPDZ, EXPDZ, EXPSZ, ERR }, + { ERR, EXPDZ, EXPDZ, EXPSZ, ERR }, // EXPSZ - { ERR, EXPDZ, EXPDZ, ERR, ERR }, + { ERR, EXPDZ, EXPDZ, ERR, ERR }, // EXPDZ - { ERR, EXPDZ, EXPDZ, ERR, ERR }, + { ERR, EXPDZ, EXPDZ, ERR, ERR }, // ERR - { ERR, ERR, ERR, ERR, ERR } + { ERR, ERR, ERR, ERR, ERR } }; wchar_t NormalizeCharDigit(wchar_t c, uint32_t radix) { // Allow upper and lower case letters as equivalent, base // is in the range where this is not ambiguous. - if (size_t{ radix } >= DIGITS.find(L'A') && size_t { radix } <= DIGITS.find(L'Z')) + if (size_t{ radix } >= DIGITS.find(L'A') && size_t{ radix } <= DIGITS.find(L'Z')) { return towupper(c); } @@ -574,22 +611,21 @@ wchar_t NormalizeCharDigit(wchar_t c, uint32_t radix) PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precision) { - long expSign = 1L; // expSign is exponent sign ( +/- 1 ) - long expValue = 0L; // expValue is exponent mantissa, should be unsigned + int32_t expSign = 1L; // expSign is exponent sign ( +/- 1 ) + int32_t expValue = 0L; // expValue is exponent mantissa, should be unsigned PNUMBER pnumret = nullptr; - createnum(pnumret, static_cast(numberString.length())); + createnum(pnumret, static_cast(numberString.length())); pnumret->sign = 1L; pnumret->cdigit = 0; pnumret->exp = 0; - MANTTYPE *pmant = pnumret->mant + numberString.length() - 1; + 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) @@ -612,6 +648,7 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis break; } // Drop through in the 'e'-as-a-digit case + [[fallthrough]]; default: state = machine[state][NZ]; break; @@ -629,41 +666,41 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis break; case EXPDZ: case EXPD: - { - curChar = NormalizeCharDigit(curChar, radix); + { + curChar = NormalizeCharDigit(curChar, radix); - size_t pos = DIGITS.find(curChar); - if (pos != wstring_view::npos) - { - expValue *= radix; - expValue += static_cast(pos); - } - else - { - state = ERR; - } + size_t pos = DIGITS.find(curChar); + if (pos != wstring_view::npos) + { + expValue *= radix; + expValue += static_cast(pos); } - break; + else + { + state = ERR; + } + } + break; case LD: pnumret->exp++; - // Fall through + [[fallthrough]]; case DD: - { - curChar = NormalizeCharDigit(curChar, radix); + { + curChar = NormalizeCharDigit(curChar, radix); - size_t pos = DIGITS.find(curChar); - if (pos != wstring_view::npos && pos < static_cast(radix)) - { - *pmant-- = static_cast(pos); - pnumret->exp--; - pnumret->cdigit++; - } - else - { - state = ERR; - } + size_t pos = DIGITS.find(curChar); + if (pos != wstring_view::npos && pos < static_cast(radix)) + { + *pmant-- = static_cast(pos); + pnumret->exp--; + pnumret->cdigit++; } - break; + else + { + state = ERR; + } + } + break; case DZ: pnumret->exp--; break; @@ -682,7 +719,7 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis } else { - while (pnumret->cdigit < static_cast(numberString.length())) + while (pnumret->cdigit < static_cast(numberString.length())) { pnumret->cdigit++; pnumret->exp--; @@ -697,216 +734,217 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis destroynum(pnumret); pnumret = nullptr; } - - stripzeroesnum(pnumret, precision); + else + { + stripzeroesnum(pnumret, precision); + } return pnumret; } //----------------------------------------------------------------------------- // -// FUNCTION: longtorat +// FUNCTION: i32torat // -// ARGUMENTS: long +// ARGUMENTS: int32_t // -// RETURN: Rational representation of long input. +// RETURN: Rational representation of int32_t input. // -// DESCRIPTION: Converts long input to rational (p over q) -// form, where q is 1 and p is the long. +// DESCRIPTION: Converts int32_t input to rational (p over q) +// form, where q is 1 and p is the int32_t. // //----------------------------------------------------------------------------- -PRAT longtorat( _In_ long inlong ) +PRAT i32torat(int32_t ini32) { - PRAT pratret= nullptr; - createrat( pratret ); - pratret->pp = longtonum(inlong, BASEX ); - pratret->pq = longtonum(1L, BASEX ); - return( pratret ); + PRAT pratret = nullptr; + createrat(pratret); + pratret->pp = i32tonum(ini32, BASEX); + pratret->pq = i32tonum(1L, BASEX); + return (pratret); } //----------------------------------------------------------------------------- // -// FUNCTION: Ulongtorat +// FUNCTION: Ui32torat // -// ARGUMENTS: ulong +// ARGUMENTS: ui32 // -// RETURN: Rational representation of unsigned long input. +// RETURN: Rational representation of uint32_t input. // -// DESCRIPTION: Converts unsigned long input to rational (p over q) -// form, where q is 1 and p is the unsigned long. Being unsigned cant take negative +// DESCRIPTION: Converts uint32_t input to rational (p over q) +// form, where q is 1 and p is the uint32_t. Being unsigned cant take negative // numbers, but the full range of unsigned numbers // //----------------------------------------------------------------------------- -PRAT Ulongtorat( _In_ unsigned long inulong ) +PRAT Ui32torat(uint32_t inui32) { - PRAT pratret= nullptr; - createrat( pratret ); - pratret->pp = Ulongtonum(inulong, BASEX ); - pratret->pq = longtonum(1L, BASEX ); - return( pratret ); + PRAT pratret = nullptr; + createrat(pratret); + pratret->pp = Ui32tonum(inui32, BASEX); + pratret->pq = i32tonum(1L, BASEX); + return (pratret); } //----------------------------------------------------------------------------- // -// FUNCTION: longtonum +// FUNCTION: i32tonum // -// ARGUMENTS: long input and radix requested. +// ARGUMENTS: int32_t input and radix requested. // // RETURN: number // // DESCRIPTION: Returns a number representation in the -// base requested of the long value passed in. +// base requested of the int32_t value passed in. // //----------------------------------------------------------------------------- -PNUMBER longtonum( long inlong, uint32_t radix) +PNUMBER i32tonum(int32_t ini32, uint32_t radix) { - MANTTYPE *pmant; - PNUMBER pnumret= nullptr; + MANTTYPE* pmant; + PNUMBER pnumret = nullptr; - createnum( pnumret, MAX_LONG_SIZE ); + createnum(pnumret, MAX_LONG_SIZE); pmant = pnumret->mant; pnumret->cdigit = 0; pnumret->exp = 0; - if ( inlong < 0 ) - { + if (ini32 < 0) + { pnumret->sign = -1; - inlong *= -1; - } + ini32 *= -1; + } else - { + { pnumret->sign = 1; - } + } - do { - *pmant++ = (MANTTYPE)(inlong % radix); - inlong /= radix; + do + { + *pmant++ = (MANTTYPE)(ini32 % radix); + ini32 /= radix; pnumret->cdigit++; - } while ( inlong ); + } while (ini32); - return( pnumret ); + return (pnumret); } //----------------------------------------------------------------------------- // -// FUNCTION: Ulongtonum +// FUNCTION: Ui32tonum // -// ARGUMENTS: ULONG input and radix requested. +// ARGUMENTS: uint32_t input and radix requested. // // RETURN: number // // DESCRIPTION: Returns a number representation in the -// base requested of the unsigned long value passed in. Being unsigned number it has no +// base requested of the uint32_t value passed in. Being unsigned number it has no // negative number and takes the full range of unsigned number // //----------------------------------------------------------------------------- - -PNUMBER Ulongtonum(unsigned long inlong, uint32_t radix) +PNUMBER Ui32tonum(uint32_t ini32, uint32_t radix) { - MANTTYPE *pmant; - PNUMBER pnumret= nullptr; + MANTTYPE* pmant; + PNUMBER pnumret = nullptr; - createnum( pnumret, MAX_LONG_SIZE ); + createnum(pnumret, MAX_LONG_SIZE); pmant = pnumret->mant; pnumret->cdigit = 0; pnumret->exp = 0; pnumret->sign = 1; - do { - *pmant++ = (MANTTYPE)(inlong % radix); - inlong /= radix; + do + { + *pmant++ = (MANTTYPE)(ini32 % radix); + ini32 /= radix; pnumret->cdigit++; - } while ( inlong ); + } while (ini32); - return( pnumret ); + return (pnumret); } - //----------------------------------------------------------------------------- // -// FUNCTION: rattolong +// FUNCTION: rattoi32 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision. // -// RETURN: long +// RETURN: int32_t // -// DESCRIPTION: returns the long representation of the +// DESCRIPTION: returns the int32_t representation of the // number input. Assumes that the number is in the internal // base. // //----------------------------------------------------------------------------- -long rattolong( _In_ PRAT prat , uint32_t radix, int32_t precision) +int32_t rattoi32(_In_ PRAT prat, uint32_t radix, int32_t precision) { - if ( rat_gt( prat, rat_max_long, precision) || rat_lt( prat, rat_min_long, precision) ) + if (rat_gt(prat, rat_max_i32, precision) || rat_lt(prat, rat_min_i32, precision)) { - // Don't attempt rattolong of anything too big or small - throw( CALC_E_DOMAIN ); + // Don't attempt rattoi32 of anything too big or small + throw(CALC_E_DOMAIN); } PRAT pint = nullptr; - DUPRAT(pint,prat); + DUPRAT(pint, prat); - intrat( &pint, radix, precision); - divnumx( &(pint->pp), pint->pq, precision); - DUPNUM( pint->pq, num_one ); + intrat(&pint, radix, precision); + divnumx(&(pint->pp), pint->pq, precision); + DUPNUM(pint->pq, num_one); - long lret = numtolong( pint->pp, BASEX ); + int32_t lret = numtoi32(pint->pp, BASEX); destroyrat(pint); - return( lret ); + return (lret); } //----------------------------------------------------------------------------- // -// FUNCTION: rattoUlong +// FUNCTION: rattoUi32 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision. // -// RETURN: Ulong +// RETURN: Ui32 // -// DESCRIPTION: returns the Ulong representation of the +// DESCRIPTION: returns the Ui32 representation of the // number input. Assumes that the number is in the internal // base. // //----------------------------------------------------------------------------- -unsigned long rattoUlong( _In_ PRAT prat, uint32_t radix, int32_t precision) +uint32_t rattoUi32(_In_ PRAT prat, uint32_t radix, int32_t precision) { - if ( rat_gt( prat, rat_dword, precision) || rat_lt( prat, rat_zero, precision) ) + if (rat_gt(prat, rat_dword, precision) || rat_lt(prat, rat_zero, precision)) { - // Don't attempt rattoulong of anything too big or small - throw( CALC_E_DOMAIN ); + // Don't attempt rattoui32 of anything too big or small + throw(CALC_E_DOMAIN); } PRAT pint = nullptr; - DUPRAT(pint,prat); + DUPRAT(pint, prat); - intrat( &pint, radix, precision); - divnumx( &(pint->pp), pint->pq, precision); - DUPNUM( pint->pq, num_one ); + intrat(&pint, radix, precision); + divnumx(&(pint->pp), pint->pq, precision); + DUPNUM(pint->pq, num_one); - unsigned long lret = numtolong( pint->pp, BASEX ); // This happens to work even if it is only signed + uint32_t lret = numtoi32(pint->pp, BASEX); // This happens to work even if it is only signed destroyrat(pint); - return( lret ); + return (lret); } - //----------------------------------------------------------------------------- // -// FUNCTION: rattoUlonglong +// FUNCTION: rattoUi64 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision // -// RETURN: Ulonglong +// RETURN: Ui64 // // DESCRIPTION: returns the 64 bit (irrespective of which processor this is running in) representation of the // number input. Assumes that the number is in the internal @@ -915,50 +953,50 @@ unsigned long rattoUlong( _In_ PRAT prat, uint32_t radix, int32_t precision) // internal base chosen happens to be 2^32, this is easier. //----------------------------------------------------------------------------- -ULONGLONG rattoUlonglong( _In_ PRAT prat, uint32_t radix, int32_t precision) +uint64_t rattoUi64(_In_ PRAT prat, uint32_t radix, int32_t precision) { PRAT pint = nullptr; // first get the LO 32 bit word DUPRAT(pint, prat); - andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) - unsigned long lo = rattoUlong(pint, radix, precision); // wont throw exception because already hi-dword chopped off + andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) + uint32_t lo = rattoUi32(pint, radix, precision); // wont throw exception because already hi-dword chopped off DUPRAT(pint, prat); // previous pint will get freed by this as well - PRAT prat32 = longtorat(32); + PRAT prat32 = i32torat(32); rshrat(&pint, prat32, radix, precision); - intrat( &pint, radix, precision); - andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) - unsigned long hi = rattoUlong(pint, radix, precision); + intrat(&pint, radix, precision); + andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) + uint32_t hi = rattoUi32(pint, radix, precision); destroyrat(prat32); destroyrat(pint); - return (((ULONGLONG)hi << 32) | lo); + return (((uint64_t)hi << 32) | lo); } //----------------------------------------------------------------------------- // -// FUNCTION: numtolong +// FUNCTION: numtoi32 // // ARGUMENTS: number input and base of that number. // -// RETURN: long +// RETURN: int32_t // -// DESCRIPTION: returns the long representation of the +// DESCRIPTION: returns the int32_t representation of the // number input. Assumes that the number is really in the // base claimed. // //----------------------------------------------------------------------------- -long numtolong( _In_ PNUMBER pnum, uint32_t radix ) +int32_t numtoi32(_In_ PNUMBER pnum, uint32_t radix) { - long lret = 0; + int32_t lret = 0; - MANTTYPE *pmant = pnum->mant; + MANTTYPE* pmant = pnum->mant; pmant += pnum->cdigit - 1; - long expt = pnum->exp; - for (long length = pnum->cdigit; length > 0 && length + expt > 0; length--) + int32_t expt = pnum->exp; + for (int32_t length = pnum->cdigit; length > 0 && length + expt > 0; length--) { lret *= radix; lret += *(pmant--); @@ -985,42 +1023,39 @@ long numtolong( _In_ PNUMBER pnum, uint32_t radix ) // //----------------------------------------------------------------------------- -bool stripzeroesnum(_Inout_ PNUMBER pnum, long starting) +bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting) { - MANTTYPE *pmant; - long 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 ) - { + if (cdigits > starting) + { pmant += cdigits - starting; cdigits = starting; - } + } // Check we haven't gone too far, and we are still looking at zeros. - while ( ( cdigits > 0 ) && !(*pmant) ) - { + while ((cdigits > 0) && !(*pmant)) + { // move to next significant digit and keep track of digits we can - // ignore later. + // ignore later. pmant++; cdigits--; fstrip = true; } // If there are zeros to remove. - if ( fstrip ) - { + if (fstrip) + { // Remove them. - memmove( pnum->mant, pmant, (int)(cdigits*sizeof(MANTTYPE)) ); + memmove(pnum->mant, pmant, (int)(cdigits * sizeof(MANTTYPE))); // And adjust exponent and digit count accordingly. - pnum->exp += ( pnum->cdigit - cdigits ); + pnum->exp += (pnum->cdigit - cdigits); pnum->cdigit = cdigits; - } - return( fstrip ); + } + return (fstrip); } //----------------------------------------------------------------------------- @@ -1028,27 +1063,27 @@ bool stripzeroesnum(_Inout_ PNUMBER pnum, long 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); - long length = pnum->cdigit; - long exponent = pnum->exp + length; // Actual number of digits to the left of decimal + int32_t length = pnum->cdigit; + int32_t exponent = pnum->exp + length; // Actual number of digits to the left of decimal - long 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. @@ -1064,11 +1099,11 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ if (!zernum(pnum) && (pnum->cdigit >= precision || (length - exponent > precision && exponent >= -MAX_ZEROS_AFTER_DECIMAL))) { // Otherwise round. - round = longtonum(radix, radix); + round = i32tonum(radix, radix); 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; } @@ -1081,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)) @@ -1095,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) @@ -1109,7 +1144,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ if (round != nullptr) { addnum(&pnum, round, radix); - long offset = (pnum->cdigit + pnum->exp) - (round->cdigit + round->exp); + int32_t offset = (pnum->cdigit + pnum->exp) - (round->cdigit + round->exp); destroynum(round); if (stripzeroesnum(pnum, offset)) { @@ -1125,16 +1160,16 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ // Set up all the post rounding stuff. bool useSciForm = false; - long eout = exponent - 1; // Displayed exponent. - MANTTYPE *pmant = pnum->mant + pnum->cdigit - 1; + 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; @@ -1159,78 +1194,73 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ } // Begin building the result string - wstringstream resultStream{}; + wstring result; // Make sure negative zeros aren't allowed. if ((pnum->sign == -1) && (length > 0)) { - resultStream << L'-'; + result = L'-'; } if (exponent <= 0 && !useSciForm) { - resultStream << L'0'; - resultStream << g_decimalSeparator; + result += L'0'; + result += g_decimalSeparator; // Used up a digit unaccounted for. } while (exponent < 0) { - resultStream << L'0'; + result += L'0'; exponent++; } while (length > 0) { exponent--; - resultStream << DIGITS[*pmant--]; + result += DIGITS[*pmant--]; length--; // Be more regular in using a decimal point. if (exponent == 0) { - resultStream << g_decimalSeparator; + result += g_decimalSeparator; } } while (exponent > 0) { - resultStream << L'0'; + result += L'0'; exponent--; // Be more regular in using a decimal point. if (exponent == 0) { - resultStream << g_decimalSeparator; + result += g_decimalSeparator; } } if (useSciForm) { - resultStream << (radix == 10 ? L'e' : L'^'); - resultStream << (eout < 0 ? L'-' : L'+'); + result += (radix == 10 ? L'e' : L'^'); + result += (eout < 0 ? L'-' : L'+'); eout = abs(eout); - wstringstream exponentStream{}; + wstring expString{}; do { - exponentStream << DIGITS[eout % radix]; + expString += DIGITS[eout % radix]; eout /= radix; } while (eout > 0); - auto expString = exponentStream.str(); - for (auto ritr = expString.rbegin(); ritr != expString.rend(); ritr++) - { - resultStream << *ritr; - } + result.insert(result.end(), expString.crbegin(), expString.crend()); } // Remove trailing decimal - auto resultString = resultStream.str(); - if (!resultString.empty() && resultString.back() == g_decimalSeparator) + if (!result.empty() && result.back() == g_decimalSeparator) { - resultString.pop_back(); + result.pop_back(); } - return resultString; + return result; } //----------------------------------------------------------------------------- @@ -1239,8 +1269,8 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ // // ARGUMENTS: // PRAT *representation of a number. -// long representation of base to dump to screen. -// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or FMT_ENGINEERING +// i32 representation of base to dump to screen. +// fmt, one of NumberFormat::Float, NumberFormat::Scientific, or NumberFormat::Engineering // precision uint32_t // // RETURN: string @@ -1253,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); @@ -1269,8 +1299,8 @@ PNUMBER RatToNumber(_In_ PRAT prat, uint32_t radix, int32_t precision) DUPRAT(temprat, prat); // Convert p and q of rational form from internal base to requested base. // Scale by largest power of BASEX possible. - long scaleby = min(temprat->pp->exp, temprat->pq->exp); - scaleby = max(scaleby, 0); + int32_t scaleby = min(temprat->pp->exp, temprat->pq->exp); + scaleby = max(scaleby, 0); temprat->pp->exp -= scaleby; temprat->pq->exp -= scaleby; @@ -1318,17 +1348,17 @@ void flatrat(_Inout_ PRAT& prat, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -PNUMBER gcd( _In_ PNUMBER a, _In_ PNUMBER b) +PNUMBER gcd(_In_ PNUMBER a, _In_ PNUMBER b) { - PNUMBER r= nullptr; - PNUMBER larger= nullptr; - PNUMBER smaller= nullptr; + PNUMBER r = nullptr; + PNUMBER larger = nullptr; + PNUMBER smaller = nullptr; if (zernum(a)) { return b; } - else if(zernum(b)) + else if (zernum(b)) { return a; } @@ -1358,12 +1388,12 @@ PNUMBER gcd( _In_ PNUMBER a, _In_ PNUMBER b) //----------------------------------------------------------------------------- // -// FUNCTION: longfactnum +// FUNCTION: i32factnum // // ARGUMENTS: -// long integer to factorialize. -// long integer representing base of answer. -// unsigned long integer for radix +// int32_t integer to factorialize. +// int32_t integer representing base of answer. +// uint32_t integer for radix // // RETURN: Factorial of input in radix PNUMBER form. // @@ -1371,63 +1401,63 @@ PNUMBER gcd( _In_ PNUMBER a, _In_ PNUMBER b) // //----------------------------------------------------------------------------- -PNUMBER longfactnum(long inlong, uint32_t radix) +PNUMBER i32factnum(int32_t ini32, uint32_t radix) { - PNUMBER lret= nullptr; - PNUMBER tmp= nullptr; + PNUMBER lret = nullptr; + PNUMBER tmp = nullptr; - lret = longtonum( 1, radix); + lret = i32tonum(1, radix); - while ( inlong > 0 ) - { - tmp = longtonum( inlong--, radix); - mulnum( &lret, tmp, radix); - destroynum( tmp ); - } - return( lret ); + while (ini32 > 0) + { + tmp = i32tonum(ini32--, radix); + mulnum(&lret, tmp, radix); + destroynum(tmp); + } + return (lret); } //----------------------------------------------------------------------------- // -// FUNCTION: longprodnum +// FUNCTION: i32prodnum // // ARGUMENTS: -// long integer to factorialize. -// long integer representing base of answer. -// unsigned long integer for radix +// int32_t integer to factorialize. +// int32_t integer representing base of answer. +// uint32_t integer for radix // // RETURN: Factorial of input in base PNUMBER form. // //----------------------------------------------------------------------------- -PNUMBER longprodnum(long start, long stop, uint32_t radix) +PNUMBER i32prodnum(int32_t start, int32_t stop, uint32_t radix) { - PNUMBER lret= nullptr; - PNUMBER tmp= nullptr; + PNUMBER lret = nullptr; + PNUMBER tmp = nullptr; - lret = longtonum( 1, radix); + lret = i32tonum(1, radix); - while ( start <= stop ) + while (start <= stop) + { + if (start) { - if ( start ) - { - tmp = longtonum( start, radix); - mulnum( &lret, tmp, radix); - destroynum( tmp ); - } - start++; + tmp = i32tonum(start, radix); + mulnum(&lret, tmp, radix); + destroynum(tmp); } - return( lret ); + start++; + } + return (lret); } //----------------------------------------------------------------------------- // -// FUNCTION: numpowlong +// FUNCTION: numpowi32 // -// ARGUMENTS: root as number power as long and radix of -// number along with the precision value in long. +// ARGUMENTS: root as number power as int32_t and radix of +// number along with the precision value in int32_t. // // RETURN: None root is changed. // @@ -1436,30 +1466,29 @@ PNUMBER longprodnum(long start, long stop, uint32_t radix) // //----------------------------------------------------------------------------- -void numpowlong( _Inout_ PNUMBER *proot, long power, uint32_t radix, int32_t precision) +void numpowi32(_Inout_ PNUMBER* proot, int32_t power, uint32_t radix, int32_t precision) { - PNUMBER lret = longtonum( 1, radix ); + PNUMBER lret = i32tonum(1, radix); - while ( power > 0 ) + while (power > 0) + { + if (power & 1) { - if ( power & 1 ) - { - mulnum( &lret, *proot, radix ); - } - mulnum( proot, *proot, radix ); + mulnum(&lret, *proot, radix); + } + mulnum(proot, *proot, radix); TRIMNUM(*proot, precision); power >>= 1; - } - destroynum( *proot ); - *proot=lret; - + } + destroynum(*proot); + *proot = lret; } //----------------------------------------------------------------------------- // -// FUNCTION: ratpowlong +// FUNCTION: ratpowi32 // -// ARGUMENTS: root as rational, power as long and precision as uint32_t. +// ARGUMENTS: root as rational, power as int32_t and precision as int32_t. // // RETURN: None root is changed. // @@ -1468,37 +1497,37 @@ void numpowlong( _Inout_ PNUMBER *proot, long power, uint32_t radix, int32_t pre // //----------------------------------------------------------------------------- -void ratpowlong( _Inout_ PRAT *proot, long power, int32_t precision) +void ratpowi32(_Inout_ PRAT* proot, int32_t power, int32_t precision) { - if ( power < 0 ) - { + if (power < 0) + { // Take the positive power and invert answer. PNUMBER pnumtemp = nullptr; - ratpowlong( proot, -power, precision); + ratpowi32(proot, -power, precision); pnumtemp = (*proot)->pp; - (*proot)->pp = (*proot)->pq; + (*proot)->pp = (*proot)->pq; (*proot)->pq = pnumtemp; - } + } else + { + PRAT lret = nullptr; + + lret = i32torat(1); + + while (power > 0) { - PRAT lret= nullptr; - - lret = longtorat( 1 ); - - while ( power > 0 ) + if (power & 1) { - if ( power & 1 ) - { - mulnumx( &(lret->pp), (*proot)->pp ); - mulnumx( &(lret->pq), (*proot)->pq ); - } - mulrat( proot, *proot, precision); + mulnumx(&(lret->pp), (*proot)->pp); + mulnumx(&(lret->pq), (*proot)->pq); + } + mulrat(proot, *proot, precision); trimit(&lret, precision); trimit(proot, precision); power >>= 1; - } - destroyrat( *proot ); - *proot=lret; } + destroyrat(*proot); + *proot = lret; + } } diff --git a/src/CalcManager/Ratpack/exp.cpp b/src/CalcManager/Ratpack/exp.cpp index af191638..61be7083 100644 --- a/src/CalcManager/Ratpack/exp.cpp +++ b/src/CalcManager/Ratpack/exp.cpp @@ -14,10 +14,8 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" - //----------------------------------------------------------------------------- // // FUNCTION: exprat @@ -41,63 +39,62 @@ // //----------------------------------------------------------------------------- -void _exprat( PRAT *px, int32_t precision) +void _exprat(_Inout_ PRAT* px, int32_t precision) { CREATETAYLOR(); - addnum(&(pret->pp),num_one, BASEX); - addnum(&(pret->pq),num_one, BASEX); - DUPRAT(thisterm,pret); + addnum(&(pret->pp), num_one, BASEX); + addnum(&(pret->pq), num_one, BASEX); + DUPRAT(thisterm, pret); - n2=longtonum(0L, BASEX); + n2 = i32tonum(0L, BASEX); - do { + do + { NEXTTERM(*px, INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } -void exprat( PRAT *px, uint32_t radix, int32_t precision) +void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - PRAT pwr= nullptr; - PRAT pint= nullptr; - long intpwr; + PRAT pwr = nullptr; + PRAT pint = nullptr; - if ( rat_gt( *px, rat_max_exp, precision) || rat_lt( *px, rat_min_exp, precision) ) - { + if (rat_gt(*px, rat_max_exp, precision) || rat_lt(*px, rat_min_exp, precision)) + { // Don't attempt exp of anything large. - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } - DUPRAT(pwr,rat_exp); - DUPRAT(pint,*px); + DUPRAT(pwr, rat_exp); + DUPRAT(pint, *px); intrat(&pint, radix, precision); - intpwr = rattolong(pint, radix, precision); - ratpowlong( &pwr, intpwr, precision); + const int32_t intpwr = rattoi32(pint, radix, precision); + ratpowi32(&pwr, intpwr, precision); subrat(px, pint, precision); // It just so happens to be an integral power of e. - if ( rat_gt( *px, rat_negsmallest, precision) && rat_lt( *px, rat_smallest, precision) ) - { - DUPRAT(*px,pwr); - } + if (rat_gt(*px, rat_negsmallest, precision) && rat_lt(*px, rat_smallest, precision)) + { + DUPRAT(*px, pwr); + } else - { + { _exprat(px, precision); mulrat(px, pwr, precision); - } + } - destroyrat( pwr ); - destroyrat( pint ); + destroyrat(pwr); + destroyrat(pint); } - //----------------------------------------------------------------------------- // // FUNCTION: lograt, _lograt @@ -125,7 +122,7 @@ void exprat( PRAT *px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void _lograt( PRAT *px, int32_t precision) +void _lograt(PRAT* px, int32_t precision) { CREATETAYLOR(); @@ -134,76 +131,69 @@ void _lograt( PRAT *px, int32_t precision) // sub one from x (*px)->pq->sign *= -1; - addnum(&((*px)->pp),(*px)->pq, BASEX); + addnum(&((*px)->pp), (*px)->pq, BASEX); (*px)->pq->sign *= -1; - DUPRAT(pret,*px); - DUPRAT(thisterm,*px); + DUPRAT(pret, *px); + DUPRAT(thisterm, *px); - n2=longtonum(1L, BASEX); + n2 = i32tonum(1L, BASEX); (*px)->pp->sign *= -1; - do { + do + { NEXTTERM(*px, MULNUM(n2) INC(n2) DIVNUM(n2), precision); TRIMTOP(*px, precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } - -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. - + PRAT pwr = nullptr; // pwr is the large scaling factor. + PRAT offset = nullptr; // offset is the incremental scaling factor. // Check for someone taking the log of zero or a negative number. - if ( rat_le( *px, rat_zero, precision) ) - { - throw( CALC_E_DOMAIN ); - } + if (rat_le(*px, rat_zero, precision)) + { + throw(CALC_E_DOMAIN); + } // Get number > 1, for scaling - fneglog = rat_lt( *px, rat_one, precision); - if ( fneglog ) - { - // WARNING: This is equivalent to doing *px = 1 / *px - PNUMBER pnumtemp= nullptr; - pnumtemp = (*px)->pp; + bool fneglog = rat_lt(*px, rat_one, precision); + if (fneglog) + { + PNUMBER pnumtemp = (*px)->pp; (*px)->pp = (*px)->pq; (*px)->pq = pnumtemp; - } + } // Scale the number within BASEX factor of 1, for the large scale. // 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. - long intpwr; - intpwr=LOGRAT2(*px)-1; + if (LOGRAT2(*px) > 1) + { + const int32_t intpwr = LOGRAT2(*px) - 1; (*px)->pq->exp += intpwr; - pwr=longtorat(intpwr*BASEXPWR); + pwr = i32torat(intpwr * BASEXPWR); mulrat(&pwr, ln_two, precision); // ln(x+e)-ln(x) looks close to e when x is close to one using some // expansions. This means we can trim past precision digits+1. TRIMTOP(*px, precision); - } + } else - { - DUPRAT(pwr,rat_zero); - } + { + DUPRAT(pwr, rat_zero); + } - DUPRAT(offset,rat_zero); + DUPRAT(offset, rat_zero); // Scale the number between 1 and e_to_one_half, for the small scale. - while ( rat_gt( *px, e_to_one_half, precision) ) - { - divrat( px, e_to_one_half, precision); - addrat( &offset, rat_one, precision); - } + while (rat_gt(*px, e_to_one_half, precision)) + { + divrat(px, e_to_one_half, precision); + addrat(&offset, rat_one, precision); + } _lograt(px, precision); @@ -218,16 +208,16 @@ void lograt( PRAT *px, int32_t precision) trimit(px, precision); // If number started out < 1 rescale answer to negative. - if ( fneglog ) - { + if (fneglog) + { (*px)->pp->sign *= -1; - } + } destroyrat(offset); destroyrat(pwr); } -void log10rat( PRAT *px, int32_t precision) +void log10rat(_Inout_ PRAT* px, int32_t precision) { lograt(px, precision); @@ -247,7 +237,7 @@ bool IsEven(PRAT x, uint32_t radix, int32_t precision) fracrat(&tmp, radix, precision); addrat(&tmp, tmp, precision); subrat(&tmp, rat_one, precision); - if ( rat_lt( tmp, rat_zero, precision)) + if (rat_lt(tmp, rat_zero, precision)) { bRet = true; } @@ -270,7 +260,7 @@ bool IsEven(PRAT x, uint32_t radix, int32_t precision) // // //--------------------------------------------------------------------------- -void powrat(PRAT *px, PRAT y, uint32_t radix, int32_t precision) +void powrat(_Inout_ PRAT* px, _In_ PRAT y, uint32_t radix, int32_t precision) { // Handle cases where px or y is 0 by calling powratcomp directly if (zerrat(*px) || zerrat(y)) @@ -302,14 +292,14 @@ void powratPowerOfPowers(_Inout_ PRAT* px, PRAT y, uint32_t radix, int32_t preci // Prepare rationals PRAT yNumerator = nullptr; PRAT yDenominator = nullptr; - DUPRAT(yNumerator, rat_zero); // yNumerator->pq is 1 one + DUPRAT(yNumerator, rat_zero); // yNumerator->pq is 1 one DUPRAT(yDenominator, rat_zero); // yDenominator->pq is 1 one DUPNUM(yNumerator->pp, y->pp); DUPNUM(yDenominator->pp, y->pq); // Calculate the following use the Powers of Powers rule: // px ^ (yNum/yDenom) == px ^ yNum ^ (1/yDenom) - // 1. For px ^ yNum, we call powratcomp directly which will call ratpowlong + // 1. For px ^ yNum, we call powratcomp directly which will call ratpowi32 // and store the result in pxPowNum // 2. For pxPowNum ^ (1/yDenom), we call powratcomp // 3. Validate the result of 2 by adding/subtracting 0.5, flooring and call powratcomp with yDenom @@ -438,84 +428,81 @@ void powratNumeratorDenominator(_Inout_ PRAT* px, PRAT y, uint32_t radix, int32_ // // //--------------------------------------------------------------------------- -void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision) +void powratcomp(_Inout_ PRAT* px, _In_ PRAT y, uint32_t radix, int32_t precision) { - long sign = ((*px)->pp->sign * (*px)->pq->sign); + int32_t sign = SIGN(*px); // Take the absolute value (*px)->pp->sign = 1; (*px)->pq->sign = 1; - if ( zerrat( *px ) ) - { + if (zerrat(*px)) + { // *px is zero. - if ( rat_lt( y, rat_zero, precision) ) - { - throw( CALC_E_DOMAIN ); - } - else if ( zerrat( y ) ) - { - // *px and y are both zero, special case a 1 return. - DUPRAT(*px,rat_one); - // Ensure sign is positive. - sign = 1; - } - } - else + if (rat_lt(y, rat_zero, precision)) { - PRAT pxint= nullptr; - DUPRAT(pxint,*px); - subrat(&pxint, rat_one, precision); - if ( rat_gt( pxint, rat_negsmallest, precision) && - rat_lt( pxint, rat_smallest, precision) && ( sign == 1 ) ) - { - // *px is one, special case a 1 return. - DUPRAT(*px,rat_one); + throw(CALC_E_DOMAIN); + } + else if (zerrat(y)) + { + // *px and y are both zero, special case a 1 return. + DUPRAT(*px, rat_one); // Ensure sign is positive. sign = 1; - } + } + } + else + { + PRAT pxint = nullptr; + DUPRAT(pxint, *px); + subrat(&pxint, rat_one, precision); + if (rat_gt(pxint, rat_negsmallest, precision) && rat_lt(pxint, rat_smallest, precision) && (sign == 1)) + { + // *px is one, special case a 1 return. + DUPRAT(*px, rat_one); + // Ensure sign is positive. + sign = 1; + } else - { - + { // Only do the exp if the number isn't zero or one PRAT podd = nullptr; - DUPRAT(podd,y); + DUPRAT(podd, y); fracrat(&podd, radix, precision); - if ( rat_gt( podd, rat_negsmallest, precision) && rat_lt( podd, rat_smallest, precision) ) - { - // If power is an integer let ratpowlong deal with it. + if (rat_gt(podd, rat_negsmallest, precision) && rat_lt(podd, rat_smallest, precision)) + { + // If power is an integer let ratpowi32 deal with it. PRAT iy = nullptr; - long inty; - DUPRAT(iy,y); + DUPRAT(iy, y); subrat(&iy, podd, precision); - inty = rattolong(iy, radix, precision); + int32_t inty = rattoi32(iy, radix, precision); PRAT plnx = nullptr; - DUPRAT(plnx,*px); + DUPRAT(plnx, *px); lograt(&plnx, precision); mulrat(&plnx, iy, precision); - if ( rat_gt( plnx, rat_max_exp, precision) || rat_lt( plnx, rat_min_exp, precision) ) - { + if (rat_gt(plnx, rat_max_exp, precision) || rat_lt(plnx, rat_min_exp, precision)) + { // Don't attempt exp of anything large or small.A destroyrat(plnx); destroyrat(iy); destroyrat(pxint); destroyrat(podd); - throw( CALC_E_DOMAIN ); - } - destroyrat(plnx); - ratpowlong(px, inty, precision); - if ( ( inty & 1 ) == 0 ) - { - sign=1; - } - destroyrat(iy); + throw(CALC_E_DOMAIN); } - else + destroyrat(plnx); + ratpowi32(px, inty, precision); + if ((inty & 1) == 0) { + sign = 1; + } + destroyrat(iy); + } + else + { // power is a fraction - if ( sign == -1 ) - { + if (sign == -1) + { // Need to throw an error if the exponent has an even denominator. // As a first step, the numerator and denominator must be divided by 2 as many times as // possible, so that 2/6 is allowed. @@ -525,7 +512,7 @@ void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision) bool fBadExponent = false; // Get the numbers in arbitrary precision rational number format - DUPRAT(pNumerator, rat_zero); // pNumerator->pq is 1 one + DUPRAT(pNumerator, rat_zero); // pNumerator->pq is 1 one DUPRAT(pDenominator, rat_zero); // pDenominator->pq is 1 one DUPNUM(pNumerator->pp, y->pp); @@ -534,39 +521,39 @@ void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision) pDenominator->pp->sign = 1; while (IsEven(pNumerator, radix, precision) && IsEven(pDenominator, radix, precision)) // both Numerator & denominator is even - { + { divrat(&pNumerator, rat_two, precision); divrat(&pDenominator, rat_two, precision); - } + } if (IsEven(pDenominator, radix, precision)) // denominator is still even - { - fBadExponent = true; - } + { + fBadExponent = true; + } if (IsEven(pNumerator, radix, precision)) // numerator is still even - { + { sign = 1; - } + } destroyrat(pNumerator); destroyrat(pDenominator); if (fBadExponent) - { - throw( CALC_E_DOMAIN ); - } - } - else { + throw(CALC_E_DOMAIN); + } + } + else + { // If the exponent is not odd disregard the sign. sign = 1; - } + } lograt(px, precision); mulrat(px, y, precision); exprat(px, radix, precision); - } - destroyrat(podd); - } - destroyrat(pxint); + } + destroyrat(podd); } + destroyrat(pxint); + } (*px)->pp->sign *= sign; } diff --git a/src/CalcManager/Ratpack/fact.cpp b/src/CalcManager/Ratpack/fact.cpp index 1a992bc0..f0905e4f 100644 --- a/src/CalcManager/Ratpack/fact.cpp +++ b/src/CalcManager/Ratpack/fact.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -13,11 +13,9 @@ // Contains fact(orial) and supporting _gamma functions. // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" - -#define ABSRAT(x) (((x)->pp->sign=1),((x)->pq->sign=1)) +#define ABSRAT(x) (((x)->pp->sign = 1), ((x)->pq->sign = 1)) #define NEGATE(x) ((x)->pp->sign *= -1) //----------------------------------------------------------------------------- @@ -56,37 +54,32 @@ // //----------------------------------------------------------------------------- - -void _gamma( PRAT *pn, uint32_t radix, int32_t precision) +void _gamma(PRAT* pn, uint32_t radix, int32_t precision) { - PRAT factorial= nullptr; - PNUMBER count= nullptr; - PRAT tmp= nullptr; - PRAT one_pt_five= nullptr; - PRAT a= nullptr; - PRAT a2= nullptr; - PRAT term= nullptr; - PRAT sum= nullptr; - PRAT err= nullptr; - PRAT mpy= nullptr; - PRAT ratprec = nullptr; - PRAT ratRadix = nullptr; - long oldprec; + PRAT factorial = nullptr; + PNUMBER count = nullptr; + PRAT tmp = nullptr; + PRAT one_pt_five = nullptr; + PRAT a = nullptr; + PRAT a2 = nullptr; + PRAT term = nullptr; + PRAT sum = nullptr; + PRAT err = nullptr; + PRAT mpy = nullptr; // Set up constants and initial conditions - oldprec = precision; - ratprec = longtorat( oldprec ); + PRAT ratprec = i32torat(precision); // Find the best 'A' for convergence to the required precision. - a=longtorat( radix ); + a = i32torat(radix); lograt(&a, precision); mulrat(&a, ratprec, precision); // Really is -ln(n)+1, but -ln(n) will be < 1 // if we scale n between 0.5 and 1.5 addrat(&a, rat_two, precision); - DUPRAT(tmp,a); + DUPRAT(tmp, a); lograt(&tmp, precision); mulrat(&tmp, *pn, precision); addrat(&a, tmp, precision); @@ -95,88 +88,87 @@ void _gamma( PRAT *pn, uint32_t radix, int32_t precision) // Calculate the necessary bump in precision and up the precision. // The following code is equivalent to // precision += ln(exp(a)*pow(a,n+1.5))-ln(radix)); - DUPRAT(tmp,*pn); - one_pt_five=longtorat( 3L ); - divrat( &one_pt_five, rat_two, precision); - addrat( &tmp, one_pt_five, precision); - DUPRAT(term,a); - powratcomp( &term, tmp, radix, precision); - DUPRAT( tmp, a ); - exprat( &tmp, radix, precision); - mulrat( &term, tmp, precision); - lograt( &term, precision); - ratRadix = longtorat(radix); - DUPRAT(tmp,ratRadix); - lograt( &tmp, precision); - subrat( &term, tmp, precision); - precision += rattolong( term, radix, precision); + DUPRAT(tmp, *pn); + one_pt_five = i32torat(3L); + divrat(&one_pt_five, rat_two, precision); + addrat(&tmp, one_pt_five, precision); + DUPRAT(term, a); + powratcomp(&term, tmp, radix, precision); + DUPRAT(tmp, a); + exprat(&tmp, radix, precision); + mulrat(&term, tmp, precision); + lograt(&term, precision); + const auto ratRadix = i32torat(radix); + DUPRAT(tmp, ratRadix); + lograt(&tmp, precision); + subrat(&term, tmp, precision); + precision += rattoi32(term, radix, precision); // Set up initial terms for series, refer to series in above comment block. - DUPRAT(factorial,rat_one); // Start factorial out with one - count = longtonum( 0L, BASEX ); + DUPRAT(factorial, rat_one); // Start factorial out with one + count = i32tonum(0L, BASEX); - DUPRAT(mpy,a); - powratcomp(&mpy,*pn, radix, precision); + DUPRAT(mpy, a); + powratcomp(&mpy, *pn, radix, precision); // a2=a^2 - DUPRAT(a2,a); + DUPRAT(a2, a); mulrat(&a2, a, precision); // sum=(1/n)-(a/(n+1)) - DUPRAT(sum,rat_one); + DUPRAT(sum, rat_one); divrat(&sum, *pn, precision); - DUPRAT(tmp,*pn); + DUPRAT(tmp, *pn); addrat(&tmp, rat_one, precision); - DUPRAT(term,a); + DUPRAT(term, a); divrat(&term, tmp, precision); subrat(&sum, term, precision); - DUPRAT(err,ratRadix); + DUPRAT(err, ratRadix); NEGATE(ratprec); - powratcomp(&err,ratprec, radix, precision); + powratcomp(&err, ratprec, radix, precision); divrat(&err, ratRadix, precision); // Just get something not tiny in term - DUPRAT(term, rat_two ); + DUPRAT(term, rat_two); // Loop until precision is reached, or asked to halt. - while ( !zerrat( term ) && rat_gt( term, err, precision) ) - { + while (!zerrat(term) && rat_gt(term, err, precision)) + { addrat(pn, rat_two, precision); // WARNING: mixing numbers and rationals here. // for speed and efficiency. INC(count); - mulnumx(&(factorial->pp),count); + mulnumx(&(factorial->pp), count); INC(count) - mulnumx(&(factorial->pp),count); + mulnumx(&(factorial->pp), count); divrat(&factorial, a2, precision); - DUPRAT(tmp,*pn); - addrat( &tmp, rat_one, precision); + DUPRAT(tmp, *pn); + addrat(&tmp, rat_one, precision); destroyrat(term); createrat(term); - DUPNUM(term->pp,count); - DUPNUM(term->pq,num_one); - addrat( &term, rat_one, precision); - mulrat( &term, tmp, precision); - DUPRAT(tmp,a); - divrat( &tmp, term, precision); + DUPNUM(term->pp, count); + DUPNUM(term->pq, num_one); + addrat(&term, rat_one, precision); + mulrat(&term, tmp, precision); + DUPRAT(tmp, a); + divrat(&tmp, term, precision); - DUPRAT(term,rat_one); - divrat( &term, *pn, precision); - subrat( &term, tmp, precision); + DUPRAT(term, rat_one); + divrat(&term, *pn, precision); + subrat(&term, tmp, precision); - divrat (&term, factorial, precision); - addrat( &sum, term, precision); + divrat(&term, factorial, precision); + addrat(&sum, term, precision); ABSRAT(term); - } + } // Multiply by factor. - mulrat( &sum, mpy, precision); + mulrat(&sum, mpy, precision); // And cleanup - precision = oldprec; destroyrat(ratprec); destroyrat(err); destroyrat(term); @@ -189,70 +181,67 @@ void _gamma( PRAT *pn, uint32_t radix, int32_t precision) destroyrat(factorial); destroyrat(*pn); - DUPRAT(*pn,sum); + DUPRAT(*pn, sum); destroyrat(sum); } -void factrat( PRAT *px, uint32_t radix, int32_t precision) +void factrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { PRAT fact = nullptr; PRAT frac = nullptr; PRAT neg_rat_one = nullptr; - if ( rat_gt( *px, rat_max_fact, precision) || rat_lt( *px, rat_min_fact, precision) ) - { + if (rat_gt(*px, rat_max_fact, precision) || rat_lt(*px, rat_min_fact, precision)) + { // Don't attempt factorial of anything too large or small. throw CALC_E_OVERFLOW; - } + } - DUPRAT(fact,rat_one); + DUPRAT(fact, rat_one); - DUPRAT(neg_rat_one,rat_one); + DUPRAT(neg_rat_one, rat_one); neg_rat_one->pp->sign *= -1; - DUPRAT( frac, *px ); - fracrat( &frac, radix, precision); + DUPRAT(frac, *px); + fracrat(&frac, radix, precision); // Check for negative integers and throw an error. - if ( ( zerrat(frac) || ( LOGRATRADIX(frac) <= -precision) ) && - ( (*px)->pp->sign * (*px)->pq->sign == -1 ) ) - { + if ((zerrat(frac) || (LOGRATRADIX(frac) <= -precision)) && (SIGN(*px) == -1)) + { throw CALC_E_DOMAIN; - } - while ( rat_gt( *px, rat_zero, precision) && - ( LOGRATRADIX(*px) > -precision) ) - { - mulrat( &fact, *px, precision); - subrat( px, rat_one, precision); - } + } + while (rat_gt(*px, rat_zero, precision) && (LOGRATRADIX(*px) > -precision)) + { + mulrat(&fact, *px, precision); + subrat(px, rat_one, precision); + } // Added to make numbers 'close enough' to integers use integer factorial. - if ( LOGRATRADIX(*px) <= -precision) - { - DUPRAT((*px),rat_zero); + if (LOGRATRADIX(*px) <= -precision) + { + DUPRAT((*px), rat_zero); intrat(&fact, radix, precision); - } + } - while ( rat_lt( *px, neg_rat_one, precision) ) - { - addrat( px, rat_one, precision); - divrat( &fact, *px, precision); - } + while (rat_lt(*px, neg_rat_one, precision)) + { + addrat(px, rat_one, precision); + divrat(&fact, *px, precision); + } - if ( rat_neq( *px, rat_zero, precision) ) - { - addrat( px, rat_one, precision); - _gamma( px, radix, precision); - mulrat( px, fact, precision); - } + if (rat_neq(*px, rat_zero, precision)) + { + addrat(px, rat_one, precision); + _gamma(px, radix, precision); + mulrat(px, fact, precision); + } else - { - DUPRAT(*px,fact); - } + { + DUPRAT(*px, fact); + } destroyrat(fact); destroyrat(frac); destroyrat(neg_rat_one); } - diff --git a/src/CalcManager/Ratpack/itrans.cpp b/src/CalcManager/Ratpack/itrans.cpp index ef687b93..75e74785 100644 --- a/src/CalcManager/Ratpack/itrans.cpp +++ b/src/CalcManager/Ratpack/itrans.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -15,29 +15,25 @@ // Special Information // //----------------------------------------------------------------------------- -#include "pch.h" #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: + switch (angletype) + { + case AngleType::Radians: break; - case ANGLE_DEG: - divrat( pa, two_pi, precision); - mulrat( pa, rat_360, precision); + case AngleType::Degrees: + divrat(pa, two_pi, precision); + mulrat(pa, rat_360, precision); break; - case ANGLE_GRAD: - divrat( pa, two_pi, precision); - mulrat( pa, rat_400, precision); + case AngleType::Gradians: + divrat(pa, two_pi, precision); + mulrat(pa, rat_400, precision); break; - } + } } - //----------------------------------------------------------------------------- // // FUNCTION: asinrat, _asinrat @@ -65,88 +61,83 @@ void ascalerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, int32_t precision) // //----------------------------------------------------------------------------- -void _asinrat( PRAT *px, int32_t precision) +void _asinrat(PRAT* px, int32_t precision) { CREATETAYLOR(); - DUPRAT(pret,*px); - DUPRAT(thisterm,*px); - DUPNUM(n2,num_one); + DUPRAT(pret, *px); + DUPRAT(thisterm, *px); + DUPNUM(n2, num_one); do - { - NEXTTERM(xx,MULNUM(n2) MULNUM(n2) - INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } - while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + { + NEXTTERM(xx, MULNUM(n2) MULNUM(n2) INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, 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); - ascalerat( pa, angletype, precision); + asinrat(pa, radix, precision); + ascalerat(pa, angletype, precision); } -void asinrat( PRAT *px, uint32_t radix, int32_t precision) +void asinrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - long sgn; - PRAT pret= nullptr; - PRAT phack= nullptr; - - sgn = (*px)->pp->sign* (*px)->pq->sign; + PRAT pret = nullptr; + PRAT phack = nullptr; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; // Avoid the really bad part of the asin curve near +/-1. - DUPRAT(phack,*px); + DUPRAT(phack, *px); subrat(&phack, rat_one, precision); // Since *px might be epsilon near zero we must set it to zero. - if ( rat_le(phack, rat_smallest, precision) && rat_ge(phack, rat_negsmallest, precision) ) - { + if (rat_le(phack, rat_smallest, precision) && rat_ge(phack, rat_negsmallest, precision)) + { destroyrat(phack); - DUPRAT( *px, pi_over_two ); - } + DUPRAT(*px, pi_over_two); + } else - { + { destroyrat(phack); - if ( rat_gt( *px, pt_eight_five, precision) ) + if (rat_gt(*px, pt_eight_five, precision)) + { + if (rat_gt(*px, rat_one, precision)) { - if ( rat_gt( *px, rat_one, precision) ) + subrat(px, rat_one, precision); + if (rat_gt(*px, rat_smallest, precision)) { - subrat( px, rat_one, precision); - if ( rat_gt( *px, rat_smallest, precision) ) - { - throw( CALC_E_DOMAIN ); - } - else - { - DUPRAT(*px,rat_one); - } + throw(CALC_E_DOMAIN); } - DUPRAT(pret,*px); - mulrat( px, pret, precision); + else + { + DUPRAT(*px, rat_one); + } + } + DUPRAT(pret, *px); + mulrat(px, pret, precision); (*px)->pp->sign *= -1; - addrat( px, rat_one, precision); - rootrat( px, rat_two, radix, precision); - _asinrat( px, precision); + addrat(px, rat_one, precision); + rootrat(px, rat_two, radix, precision); + _asinrat(px, precision); (*px)->pp->sign *= -1; - addrat( px, pi_over_two, precision); + addrat(px, pi_over_two, precision); destroyrat(pret); - } - else - { - _asinrat( px, precision); - } } + else + { + _asinrat(px, precision); + } + } (*px)->pp->sign = sgn; (*px)->pq->sign = 1; } - //----------------------------------------------------------------------------- // // FUNCTION: acosrat, _acosrat @@ -173,62 +164,58 @@ void asinrat( 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); - ascalerat( pa, angletype, precision); + acosrat(pa, radix, precision); + ascalerat(pa, angletype, precision); } -void _acosrat( PRAT *px, int32_t precision) +void _acosrat(PRAT* px, int32_t precision) { CREATETAYLOR(); createrat(thisterm); - thisterm->pp=longtonum( 1L, BASEX ); - thisterm->pq=longtonum( 1L, BASEX ); + thisterm->pp = i32tonum(1L, BASEX); + thisterm->pq = i32tonum(1L, BASEX); - DUPNUM(n2,num_one); + DUPNUM(n2, num_one); do - { - NEXTTERM(xx,MULNUM(n2) MULNUM(n2) - INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } - while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + { + NEXTTERM(xx, MULNUM(n2) MULNUM(n2) INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } -void acosrat( PRAT *px, uint32_t radix, int32_t precision) +void acosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - long sgn; - - sgn = (*px)->pp->sign*(*px)->pq->sign; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; - if ( rat_equ( *px, rat_one, precision) ) + if (rat_equ(*px, rat_one, precision)) + { + if (sgn == -1) { - if ( sgn == -1 ) - { - DUPRAT(*px,pi); - } - else - { - DUPRAT( *px, rat_zero ); - } + DUPRAT(*px, pi); } - else + else { + DUPRAT(*px, rat_zero); + } + } + else + { (*px)->pp->sign = sgn; - asinrat( px, radix, precision); + asinrat(px, radix, precision); (*px)->pp->sign *= -1; addrat(px, pi_over_two, precision); - } + } } //----------------------------------------------------------------------------- @@ -262,81 +249,79 @@ void acosrat( 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); - ascalerat( pa, angletype, precision); + atanrat(pa, radix, precision); + ascalerat(pa, angletype, precision); } -void _atanrat( PRAT *px, int32_t precision) +void _atanrat(PRAT* px, int32_t precision) { CREATETAYLOR(); - DUPRAT(pret,*px); - DUPRAT(thisterm,*px); + DUPRAT(pret, *px); + DUPRAT(thisterm, *px); - DUPNUM(n2,num_one); + DUPNUM(n2, num_one); xx->pp->sign *= -1; - do { - NEXTTERM(xx,MULNUM(n2) INC(n2) INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + do + { + NEXTTERM(xx, MULNUM(n2) INC(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } -void atanrat( PRAT *px, uint32_t radix, int32_t precision) +void atanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - long sgn; - PRAT tmpx= nullptr; - - sgn = (*px)->pp->sign * (*px)->pq->sign; + PRAT tmpx = nullptr; + int32_t sgn = SIGN(*px); (*px)->pp->sign = 1; (*px)->pq->sign = 1; - if ( rat_gt( (*px), pt_eight_five, precision) ) + if (rat_gt((*px), pt_eight_five, precision)) + { + if (rat_gt((*px), rat_two, precision)) { - if ( rat_gt( (*px), rat_two, precision) ) - { (*px)->pp->sign = sgn; (*px)->pq->sign = 1; - DUPRAT(tmpx,rat_one); + DUPRAT(tmpx, rat_one); divrat(&tmpx, (*px), precision); _atanrat(&tmpx, precision); tmpx->pp->sign = sgn; tmpx->pq->sign = 1; - DUPRAT(*px,pi_over_two); + DUPRAT(*px, pi_over_two); subrat(px, tmpx, precision); - destroyrat( tmpx ); - } + destroyrat(tmpx); + } else - { + { (*px)->pp->sign = sgn; - DUPRAT(tmpx,*px); - mulrat( &tmpx, *px, precision); - addrat( &tmpx, rat_one, precision); - rootrat( &tmpx, rat_two, radix, precision); - divrat( px, tmpx, precision); - destroyrat( tmpx ); - asinrat( px, radix, precision); + DUPRAT(tmpx, *px); + mulrat(&tmpx, *px, precision); + addrat(&tmpx, rat_one, precision); + rootrat(&tmpx, rat_two, radix, precision); + divrat(px, tmpx, precision); + destroyrat(tmpx); + asinrat(px, radix, precision); (*px)->pp->sign = sgn; (*px)->pq->sign = 1; - } } + } else - { + { (*px)->pp->sign = sgn; (*px)->pq->sign = 1; - _atanrat( px, precision); - } - if ( rat_gt( *px, pi_over_two, precision) ) - { - subrat( px, pi, precision); - } + _atanrat(px, precision); + } + if (rat_gt(*px, pi_over_two, precision)) + { + subrat(px, pi, precision); + } } - diff --git a/src/CalcManager/Ratpack/itransh.cpp b/src/CalcManager/Ratpack/itransh.cpp index d6734a1f..36c3c48f 100644 --- a/src/CalcManager/Ratpack/itransh.cpp +++ b/src/CalcManager/Ratpack/itransh.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -16,11 +16,8 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" - - //----------------------------------------------------------------------------- // // FUNCTION: asinhrat @@ -50,47 +47,44 @@ // //----------------------------------------------------------------------------- -void asinhrat( PRAT *px, uint32_t radix, int32_t precision) +void asinhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { PRAT neg_pt_eight_five = nullptr; - DUPRAT(neg_pt_eight_five,pt_eight_five); + DUPRAT(neg_pt_eight_five, pt_eight_five); neg_pt_eight_five->pp->sign *= -1; - if ( rat_gt( *px, pt_eight_five, precision) || rat_lt( *px, neg_pt_eight_five, precision) ) - { + if (rat_gt(*px, pt_eight_five, precision) || rat_lt(*px, neg_pt_eight_five, precision)) + { PRAT ptmp = nullptr; - DUPRAT(ptmp,(*px)); + DUPRAT(ptmp, (*px)); mulrat(&ptmp, *px, precision); addrat(&ptmp, rat_one, precision); rootrat(&ptmp, rat_two, radix, precision); addrat(px, ptmp, precision); lograt(px, precision); destroyrat(ptmp); - } + } else - { + { CREATETAYLOR(); xx->pp->sign *= -1; - DUPRAT(pret,(*px)); - DUPRAT(thisterm,(*px)); + DUPRAT(pret, (*px)); + DUPRAT(thisterm, (*px)); - DUPNUM(n2,num_one); + DUPNUM(n2, num_one); do - { - NEXTTERM(xx,MULNUM(n2) MULNUM(n2) - INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } - while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + { + NEXTTERM(xx, MULNUM(n2) MULNUM(n2) INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); - } + } destroyrat(neg_pt_eight_five); } - //----------------------------------------------------------------------------- // // FUNCTION: acoshrat @@ -107,24 +101,24 @@ void asinhrat( PRAT *px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void acoshrat( PRAT *px, uint32_t radix, int32_t precision) +void acoshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - if ( rat_lt( *px, rat_one, precision) ) - { + if (rat_lt(*px, rat_one, precision)) + { throw CALC_E_DOMAIN; - } + } else - { + { PRAT ptmp = nullptr; - DUPRAT(ptmp,(*px)); + DUPRAT(ptmp, (*px)); mulrat(&ptmp, *px, precision); subrat(&ptmp, rat_one, precision); - rootrat(&ptmp,rat_two, radix, precision); + rootrat(&ptmp, rat_two, radix, precision); addrat(px, ptmp, precision); lograt(px, precision); destroyrat(ptmp); - } + } } //----------------------------------------------------------------------------- @@ -144,11 +138,11 @@ void acoshrat( PRAT *px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void atanhrat( PRAT *px, int32_t precision) +void atanhrat(_Inout_ PRAT* px, int32_t precision) { PRAT ptmp = nullptr; - DUPRAT(ptmp,(*px)); + DUPRAT(ptmp, (*px)); subrat(&ptmp, rat_one, precision); addrat(px, rat_one, precision); divrat(px, ptmp, precision); @@ -157,4 +151,3 @@ void atanhrat( PRAT *px, int32_t precision) divrat(px, rat_two, precision); destroyrat(ptmp); } - diff --git a/src/CalcManager/Ratpack/logic.cpp b/src/CalcManager/Ratpack/logic.cpp index ab5a63ed..197bda90 100644 --- a/src/CalcManager/Ratpack/logic.cpp +++ b/src/CalcManager/Ratpack/logic.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //--------------------------------------------------------------------------- @@ -13,83 +13,80 @@ // Contains routines for and, or, xor, not and other support // //--------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" +using namespace std; - -void lshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { - PRAT pwr= nullptr; - long intb; + PRAT pwr = nullptr; intrat(pa, radix, precision); - if ( !zernum( (*pa)->pp ) ) - { + if (!zernum((*pa)->pp)) + { // If input is zero we're done. - if ( rat_gt( b, rat_max_exp, precision) ) - { + if (rat_gt(b, rat_max_exp, precision)) + { // Don't attempt lsh of anything big - throw( CALC_E_DOMAIN ); - } - intb = rattolong(b, radix, precision); - DUPRAT(pwr,rat_two); - ratpowlong(&pwr, intb, precision); + throw(CALC_E_DOMAIN); + } + const int32_t intb = rattoi32(b, radix, precision); + DUPRAT(pwr, rat_two); + ratpowi32(&pwr, intb, precision); mulrat(pa, pwr, precision); destroyrat(pwr); - } + } } -void rshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { - PRAT pwr= nullptr; - long intb; + PRAT pwr = nullptr; intrat(pa, radix, precision); - if ( !zernum( (*pa)->pp ) ) - { + if (!zernum((*pa)->pp)) + { // If input is zero we're done. - if ( rat_lt( b, rat_min_exp, precision) ) - { + if (rat_lt(b, rat_min_exp, precision)) + { // Don't attempt rsh of anything big and negative. - throw( CALC_E_DOMAIN ); - } - intb = rattolong(b, radix, precision); - DUPRAT(pwr,rat_two); - ratpowlong(&pwr, intb, precision); + throw(CALC_E_DOMAIN); + } + const int32_t intb = rattoi32(b, radix, precision); + DUPRAT(pwr, rat_two); + ratpowi32(&pwr, intb, precision); divrat(pa, pwr, precision); destroyrat(pwr); - } + } } -void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision); -void boolnum( PNUMBER *pa, PNUMBER b, int func ); +void boolrat(PRAT* pa, PRAT b, int func, uint32_t radix, int32_t precision); +void boolnum(PNUMBER* pa, PNUMBER b, int func); - -enum { +enum +{ FUNC_AND, FUNC_OR, FUNC_XOR } BOOL_FUNCS; -void andrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void andrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_AND, radix, precision); + boolrat(pa, b, FUNC_AND, radix, precision); } -void orrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void orrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_OR, radix, precision); + boolrat(pa, b, FUNC_OR, radix, precision); } -void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) +void xorrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { - boolrat( pa, b, FUNC_XOR, radix, precision); + boolrat(pa, b, FUNC_XOR, radix, precision); } //--------------------------------------------------------------------------- @@ -104,15 +101,15 @@ void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision) +void boolrat(PRAT* pa, PRAT b, int func, uint32_t radix, int32_t precision) { - PRAT tmp= nullptr; - intrat( pa, radix, precision); - DUPRAT(tmp,b); - intrat( &tmp, radix, precision); + PRAT tmp = nullptr; + intrat(pa, radix, precision); + DUPRAT(tmp, b); + intrat(&tmp, radix, precision); - boolnum( &((*pa)->pp), tmp->pp, func ); + boolnum(&((*pa)->pp), tmp->pp, func); destroyrat(tmp); } @@ -130,39 +127,34 @@ void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -void boolnum( PNUMBER *pa, PNUMBER b, int func ) +void boolnum(PNUMBER* pa, PNUMBER b, int func) { - PNUMBER c= nullptr; - PNUMBER a= nullptr; - MANTTYPE *pcha; - MANTTYPE *pchb; - MANTTYPE *pchc; - long cdigits; - long mexp; + PNUMBER c = nullptr; + PNUMBER a = nullptr; + MANTTYPE* pcha; + MANTTYPE* pchb; + MANTTYPE* pchc; + int32_t cdigits; + int32_t mexp; MANTTYPE da; MANTTYPE db; - a=*pa; - cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) - - min( a->exp, b->exp ); - createnum( c, cdigits ); - c->exp = min( a->exp, b->exp ); + a = *pa; + cdigits = max(a->cdigit + a->exp, b->cdigit + b->exp) - min(a->exp, b->exp); + createnum(c, cdigits); + c->exp = min(a->exp, b->exp); mexp = c->exp; c->cdigit = cdigits; pcha = a->mant; pchb = b->mant; pchc = c->mant; - for ( ;cdigits > 0; cdigits--, mexp++ ) + for (; cdigits > 0; cdigits--, mexp++) + { + da = (((mexp >= a->exp) && (cdigits + a->exp - c->exp > (c->cdigit - a->cdigit))) ? *pcha++ : 0); + db = (((mexp >= b->exp) && (cdigits + b->exp - c->exp > (c->cdigit - b->cdigit))) ? *pchb++ : 0); + switch (func) { - da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp > - (c->cdigit - a->cdigit) ) ) ? - *pcha++ : 0 ); - db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp > - (c->cdigit - b->cdigit) ) ) ? - *pchb++ : 0 ); - switch ( func ) - { case FUNC_AND: *pchc++ = da & db; break; @@ -172,15 +164,51 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) case FUNC_XOR: *pchc++ = da ^ db; break; - } } + } c->sign = a->sign; - while ( c->cdigit > 1 && *(--pchc) == 0 ) - { + while (c->cdigit > 1 && *(--pchc) == 0) + { c->cdigit--; - } - destroynum( *pa ); - *pa=c; + } + destroynum(*pa); + *pa = c; +} + +//----------------------------------------------------------------------------- +// +// FUNCTION: remrat +// +// ARGUMENTS: pointer to a rational a second rational. +// +// RETURN: None, changes pointer. +// +// DESCRIPTION: Calculate the remainder of *pa / b, +// equivalent of 'pa % b' in C/C++ and produces a result +// that is either zero or has the same sign as the dividend. +// +//----------------------------------------------------------------------------- + +void remrat(_Inout_ PRAT* pa, _In_ PRAT b) + +{ + if (zerrat(b)) + { + throw CALC_E_INDEFINITE; + } + + PRAT tmp = nullptr; + DUPRAT(tmp, b); + + mulnumx(&((*pa)->pp), tmp->pq); + mulnumx(&(tmp->pp), (*pa)->pq); + remnum(&((*pa)->pp), tmp->pp, BASEX); + mulnumx(&((*pa)->pq), tmp->pq); + + // Get *pa back in the integer over integer form. + RENORMALIZE(*pa); + + destroyrat(tmp); } //----------------------------------------------------------------------------- @@ -191,28 +219,38 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func ) // // RETURN: None, changes pointer. // -// DESCRIPTION: Does the rational equivalent of frac(*pa); +// DESCRIPTION: Calculate the remainder of *pa / b, with the sign of the result +// either zero or has the same sign as the divisor. +// NOTE: When *pa or b are negative, the result won't be the same as +// the C/C++ operator %, use remrat if it's the behavior you expect. // //----------------------------------------------------------------------------- -void modrat( PRAT *pa, PRAT b ) - +void modrat(_Inout_ PRAT* pa, _In_ PRAT b) { + // contrary to remrat(X, 0) returning 0, modrat(X, 0) must return X + if (zerrat(b)) + { + return; + } + PRAT tmp = nullptr; + DUPRAT(tmp, b); - if ( zerrat( b ) ) - { - throw CALC_E_INDEFINITE; - } - DUPRAT(tmp,b); + auto needAdjust = (SIGN(*pa) == -1 ? (SIGN(b) == 1) : (SIGN(b) == -1)); - mulnumx( &((*pa)->pp), tmp->pq ); - mulnumx( &(tmp->pp), (*pa)->pq ); - remnum( &((*pa)->pp), tmp->pp, BASEX ); - mulnumx( &((*pa)->pq), tmp->pq ); + mulnumx(&((*pa)->pp), tmp->pq); + mulnumx(&(tmp->pp), (*pa)->pq); + remnum(&((*pa)->pp), tmp->pp, BASEX); + mulnumx(&((*pa)->pq), tmp->pq); + + if (needAdjust && !zerrat(*pa)) + { + addrat(pa, b, BASEX); + } // Get *pa back in the integer over integer form. RENORMALIZE(*pa); - destroyrat( tmp ); + destroyrat(tmp); } diff --git a/src/CalcManager/Ratpack/num.cpp b/src/CalcManager/Ratpack/num.cpp index 817fe02c..6055e41d 100644 --- a/src/CalcManager/Ratpack/num.cpp +++ b/src/CalcManager/Ratpack/num.cpp @@ -17,7 +17,8 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" +#include +#include // for memmove #include "ratpak.h" using namespace std; @@ -40,51 +41,48 @@ using namespace std; // //---------------------------------------------------------------------------- -void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix); +void _addnum(PNUMBER* pa, PNUMBER b, uint32_t radix); -void __inline addnum( PNUMBER *pa, PNUMBER b, uint32_t radix) +void addnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix) { - if ( b->cdigit > 1 || b->mant[0] != 0 ) - { // If b is zero we are done. - if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 0 ) - { // pa and b are both nonzero. - _addnum( pa, b, radix); - } - else - { // if pa is zero and b isn't just copy b. - DUPNUM(*pa,b); - } + if (b->cdigit > 1 || b->mant[0] != 0) + { // If b is zero we are done. + if ((*pa)->cdigit > 1 || (*pa)->mant[0] != 0) + { // pa and b are both nonzero. + _addnum(pa, b, radix); } + else + { // if pa is zero and b isn't just copy b. + DUPNUM(*pa, b); + } + } } -void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix) +void _addnum(PNUMBER* pa, PNUMBER b, uint32_t radix) { - PNUMBER c= nullptr; // c will contain the result. - PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa - 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. - long cdigits; // cdigits is the max count of the digits results - // used as a counter. - long 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. - MANTTYPE cy=0; // cy is the value of a carry after adding two 'digits' - long fcompla = 0; // fcompla is a flag to signal a is negative. - long fcomplb = 0; // fcomplb is a flag to signal b is negative. - - a=*pa; + PNUMBER c = nullptr; // c will contain the result. + PNUMBER a = nullptr; // a is the dereferenced number pointer from *pa + 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 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. + MANTTYPE cy = 0; // cy is the value of a carry after adding two 'digits' + int32_t fcompla = 0; // fcompla is a flag to signal a is negative. + int32_t fcomplb = 0; // fcomplb is a flag to signal b is negative. + a = *pa; // Calculate the overlap of the numbers after alignment, this includes // necessary padding 0's - cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) - - min( a->exp, b->exp ); + cdigits = max(a->cdigit + a->exp, b->cdigit + b->exp) - min(a->exp, b->exp); - createnum( c, cdigits + 1 ); - c->exp = min( a->exp, b->exp ); + createnum(c, cdigits + 1); + c->exp = min(a->exp, b->exp); mexp = c->exp; c->cdigit = cdigits; pcha = a->mant; @@ -92,89 +90,82 @@ void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix) pchc = c->mant; // Figure out the sign of the numbers - if ( a->sign != b->sign ) - { + if (a->sign != b->sign) + { cy = 1; - fcompla = ( a->sign == -1 ); - fcomplb = ( b->sign == -1 ); - } + fcompla = (a->sign == -1); + fcomplb = (b->sign == -1); + } // Loop over all the digits, real and 0 padded. Here we know a and b are // aligned - for ( ;cdigits > 0; cdigits--, mexp++ ) - { - + for (; cdigits > 0; cdigits--, mexp++) + { // Get digit from a, taking padding into account. - da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp > - (c->cdigit - a->cdigit) ) ) ? - *pcha++ : 0 ); + da = (((mexp >= a->exp) && (cdigits + a->exp - c->exp > (c->cdigit - a->cdigit))) ? *pcha++ : 0); // Get digit from b, taking padding into account. - db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp > - (c->cdigit - b->cdigit) ) ) ? - *pchb++ : 0 ); + db = (((mexp >= b->exp) && (cdigits + b->exp - c->exp > (c->cdigit - b->cdigit))) ? *pchb++ : 0); // Handle complementing for a and b digit. Might be a better way, but // haven't found it yet. - if ( fcompla ) - { - da = (MANTTYPE)(radix) - 1 - da; - } - if ( fcomplb ) - { - db = (MANTTYPE)(radix) - 1 - db; - } + if (fcompla) + { + da = (MANTTYPE)(radix)-1 - da; + } + if (fcomplb) + { + db = (MANTTYPE)(radix)-1 - db; + } // Update carry as necessary cy = da + db + cy; *pchc++ = (MANTTYPE)(cy % (MANTTYPE)radix); cy /= (MANTTYPE)radix; - } + } // Handle carry from last sum as extra digit - if ( cy && !(fcompla || fcomplb) ) - { + if (cy && !(fcompla || fcomplb)) + { *pchc++ = cy; c->cdigit++; - } + } // Compute sign of result - if ( !(fcompla || fcomplb) ) - { + if (!(fcompla || fcomplb)) + { c->sign = a->sign; - } + } else + { + if (cy) { - if ( cy ) - { c->sign = 1; - } + } else - { + { // In this particular case an overflow or underflow has occurred // and all the digits need to be complemented, at one time an // attempt to handle this above was made, it turned out to be much // slower on average. c->sign = -1; cy = 1; - for ( ( cdigits = c->cdigit ), (pchc = c->mant); - cdigits > 0; - cdigits-- ) - { + for ((cdigits = c->cdigit), (pchc = c->mant); cdigits > 0; cdigits--) + { cy = (MANTTYPE)radix - (MANTTYPE)1 - *pchc + cy; - *pchc++ = (MANTTYPE)( cy % (MANTTYPE)radix); + *pchc++ = (MANTTYPE)(cy % (MANTTYPE)radix); cy /= (MANTTYPE)radix; - } } } + } // Remove leading zeros, remember digits are in order of // increasing significance. i.e. 100 would be 0,0,1 - while ( c->cdigit > 1 && *(--pchc) == 0 ) - { + while (c->cdigit > 1 && *(--pchc) == 0) + { c->cdigit--; - } - destroynum( *pa ); - *pa=c; + } + destroynum(*pa); + *pa = c; } //---------------------------------------------------------------------------- @@ -192,52 +183,52 @@ void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix) // //---------------------------------------------------------------------------- -void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix); +void _mulnum(PNUMBER* pa, PNUMBER b, uint32_t radix); -void __inline mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix) +void mulnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix) { - if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) - { // If b is one we don't multiply exactly. - if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 ) - { // pa and b are both non-one. - _mulnum( pa, b, radix); - } + if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) + { // If b is one we don't multiply exactly. + if ((*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0) + { // pa and b are both non-one. + _mulnum(pa, b, radix); + } else - { // if pa is one and b isn't just copy b, and adjust the sign. - long sign = (*pa)->sign; - DUPNUM(*pa,b); + { // if pa is one and b isn't just copy b, and adjust the sign. + int32_t sign = (*pa)->sign; + DUPNUM(*pa, b); (*pa)->sign *= sign; - } } + } else - { // But we do have to set the sign. + { // But we do have to set the sign. (*pa)->sign *= b->sign; - } + } } -void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix) +void _mulnum(PNUMBER* pa, PNUMBER b, uint32_t radix) { - PNUMBER c= nullptr; // c will contain the result. - PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa - 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. - MANTTYPE *pchcoffset; // pchcoffset, is the anchor location of the next - // single digit multiply partial result. - long iadigit = 0; // Index of digit being used in the first number. - long ibdigit = 0; // Index of digit being used in the second number. - MANTTYPE da = 0; // da is the digit from the fist number. - TWO_MANTTYPE cy = 0; // cy is the carry resulting from the addition of - // a multiplied row into the result. - TWO_MANTTYPE mcy = 0; // mcy is the resultant from a single - // multiply, AND the carry of that multiply. - long icdigit = 0; // Index of digit being calculated in final result. + PNUMBER c = nullptr; // c will contain the result. + PNUMBER a = nullptr; // a is the dereferenced number pointer from *pa + 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. + MANTTYPE* pchcoffset; // pchcoffset, is the anchor location of the next + // single digit multiply partial result. + int32_t iadigit = 0; // Index of digit being used in the first number. + int32_t ibdigit = 0; // Index of digit being used in the second number. + MANTTYPE da = 0; // da is the digit from the fist number. + TWO_MANTTYPE cy = 0; // cy is the carry resulting from the addition of + // a multiplied row into the result. + TWO_MANTTYPE mcy = 0; // mcy is the resultant from a single + // multiply, AND the carry of that multiply. + int32_t icdigit = 0; // Index of digit being calculated in final result. - a=*pa; + a = *pa; ibdigit = a->cdigit + b->cdigit - 1; - createnum( c, ibdigit + 1 ); + createnum(c, ibdigit + 1); c->cdigit = ibdigit; c->sign = a->sign * b->sign; @@ -245,58 +236,56 @@ void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix) pcha = a->mant; pchcoffset = c->mant; - for ( iadigit = a->cdigit; iadigit > 0; iadigit-- ) - { - da = *pcha++; + for (iadigit = a->cdigit; iadigit > 0; iadigit--) + { + da = *pcha++; pchb = b->mant; // Shift pchc, and pchcoffset, one for each digit pchc = pchcoffset++; - for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- ) - { + for (ibdigit = b->cdigit; ibdigit > 0; ibdigit--) + { cy = 0; mcy = (TWO_MANTTYPE)da * *pchb; - if ( mcy ) - { + if (mcy) + { icdigit = 0; - if ( ibdigit == 1 && iadigit == 1 ) - { - c->cdigit++; - } - } - // If result is nonzero, or while result of carry is nonzero... - while ( mcy || cy ) + if (ibdigit == 1 && iadigit == 1) { - + c->cdigit++; + } + } + // If result is nonzero, or while result of carry is nonzero... + while (mcy || cy) + { // update carry from addition(s) and multiply. - cy += (TWO_MANTTYPE)pchc[icdigit]+(mcy%(TWO_MANTTYPE)radix); + cy += (TWO_MANTTYPE)pchc[icdigit] + (mcy % (TWO_MANTTYPE)radix); // update result digit from - pchc[icdigit++]=(MANTTYPE)(cy%(TWO_MANTTYPE)radix); + pchc[icdigit++] = (MANTTYPE)(cy % (TWO_MANTTYPE)radix); // update carries from mcy /= (TWO_MANTTYPE)radix; cy /= (TWO_MANTTYPE)radix; - } + } pchb++; pchc++; - } } + } // prevent different kinds of zeros, by stripping leading duplicate zeros. // digits are in order of increasing significance. - while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 ) - { + while (c->cdigit > 1 && c->mant[c->cdigit - 1] == 0) + { c->cdigit--; - } + } - destroynum( *pa ); - *pa=c; + destroynum(*pa); + *pa = c; } - //---------------------------------------------------------------------------- // // FUNCTION: remnum @@ -312,55 +301,53 @@ void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix) // //---------------------------------------------------------------------------- -void remnum( PNUMBER *pa, PNUMBER b, uint32_t radix) +void remnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix) { PNUMBER tmp = nullptr; // tmp is the working remainder. PNUMBER lasttmp = nullptr; // lasttmp is the last remainder which worked. // Once *pa is less than b, *pa is the remainder. - while ( !lessnum( *pa, b ) ) + while (!lessnum(*pa, b)) + { + DUPNUM(tmp, b); + if (lessnum(tmp, *pa)) { - DUPNUM( tmp, b ); - if ( lessnum( tmp, *pa ) ) - { // Start off close to the right answer for subtraction. - tmp->exp = (*pa)->cdigit+(*pa)->exp - tmp->cdigit; - if ( MSD(*pa) <= MSD(tmp) ) - { + tmp->exp = (*pa)->cdigit + (*pa)->exp - tmp->cdigit; + if (MSD(*pa) <= MSD(tmp)) + { // Don't take the chance that the numbers are equal. tmp->exp--; - } } + } - destroynum( lasttmp ); - lasttmp=longtonum( 0, radix); + destroynum(lasttmp); + lasttmp = i32tonum(0, radix); - while ( lessnum( tmp, *pa ) ) - { - DUPNUM( lasttmp, tmp ); - addnum( &tmp, tmp, radix); - } + while (lessnum(tmp, *pa)) + { + DUPNUM(lasttmp, tmp); + addnum(&tmp, tmp, radix); + } - if ( lessnum( *pa, tmp ) ) - { + if (lessnum(*pa, tmp)) + { // too far, back up... - destroynum( tmp ); - tmp=lasttmp; - lasttmp= nullptr; - } + destroynum(tmp); + tmp = lasttmp; + lasttmp = nullptr; + } // Subtract the working remainder from the remainder holder. - tmp->sign = -1*(*pa)->sign; - addnum( pa, tmp, radix); + tmp->sign = -1 * (*pa)->sign; + addnum(pa, tmp, radix); - destroynum( tmp ); - destroynum( lasttmp ); - - } + destroynum(tmp); + destroynum(lasttmp); + } } - //--------------------------------------------------------------------------- // // FUNCTION: divnum @@ -375,26 +362,26 @@ void remnum( PNUMBER *pa, PNUMBER b, uint32_t radix) // //--------------------------------------------------------------------------- -void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision); +void _divnum(PNUMBER* pa, PNUMBER b, uint32_t radix, int32_t precision); -void __inline divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) +void divnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix, int32_t precision) { - if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) - { + if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) + { // b is not one - _divnum( pa, b, radix, precision); - } + _divnum(pa, b, radix, precision); + } else - { // But we do have to set the sign. + { // But we do have to set the sign. (*pa)->sign *= b->sign; - } + } } -void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) +void _divnum(PNUMBER* pa, PNUMBER b, uint32_t radix, int32_t precision) { PNUMBER a = *pa; - long thismax = precision + 2; + int32_t thismax = precision + 2; if (thismax < a->cdigit) { thismax = a->cdigit; @@ -410,7 +397,7 @@ void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) c->exp = (a->cdigit + a->exp) - (b->cdigit + b->exp) + 1; c->sign = a->sign * b->sign; - MANTTYPE *ptrc = c->mant + thismax; + MANTTYPE* ptrc = c->mant + thismax; PNUMBER rem = nullptr; PNUMBER tmp = nullptr; DUPNUM(rem, a); @@ -420,8 +407,8 @@ void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) // Build a table of multiplications of the divisor, this is quicker for // more than radix 'digits' - list numberList{ longtonum(0L, radix) }; - for (unsigned long i = 1; i < radix; i++) + list numberList{ i32tonum(0L, radix) }; + for (uint32_t i = 1; i < radix; i++) { PNUMBER newValue = nullptr; DUPNUM(newValue, numberList.front()); @@ -431,8 +418,8 @@ void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) } destroynum(tmp); - long digit; - long cdigits = 0; + int32_t digit; + int32_t cdigits = 0; while (cdigits++ < thismax && !zernum(rem)) { digit = radix - 1; @@ -488,7 +475,6 @@ void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) *pa = c; } - //--------------------------------------------------------------------------- // // FUNCTION: equnum @@ -502,58 +488,56 @@ void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -bool equnum( PNUMBER a, PNUMBER b ) +bool equnum(_In_ PNUMBER a, _In_ PNUMBER b) { - long diff; - MANTTYPE *pa; - MANTTYPE *pb; - long cdigits; - long ccdigits; - MANTTYPE da; - MANTTYPE db; + 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 ); - if ( diff < 0 ) - { + diff = (a->cdigit + a->exp) - (b->cdigit + b->exp); + if (diff < 0) + { // If the exponents are different, these are different numbers. return false; - } + } else + { + if (diff > 0) { - if ( diff > 0 ) - { // If the exponents are different, these are different numbers. return false; - } + } else - { + { // OK the exponents match. pa = a->mant; pb = b->mant; pa += a->cdigit - 1; pb += b->cdigit - 1; - cdigits = max( a->cdigit, b->cdigit ); + cdigits = max(a->cdigit, b->cdigit); ccdigits = cdigits; // Loop over all digits until we run out of digits or there is a // difference in the digits. - for ( ;cdigits > 0; cdigits-- ) + for (; cdigits > 0; cdigits--) + { + da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0); + db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0); + if (da != db) { - da = ( (cdigits > (ccdigits - a->cdigit) ) ? - *pa-- : 0 ); - db = ( (cdigits > (ccdigits - b->cdigit) ) ? - *pb-- : 0 ); - if ( da != db ) - { return false; - } } + } // In this case, they are equal. return true; - } } + } } //--------------------------------------------------------------------------- @@ -570,54 +554,37 @@ bool equnum( PNUMBER a, PNUMBER b ) // //--------------------------------------------------------------------------- -bool lessnum( PNUMBER a, PNUMBER b ) +bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b) { - long diff; - MANTTYPE *pa; - MANTTYPE *pb; - long cdigits; - long ccdigits; - MANTTYPE da; - MANTTYPE db; - - - diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp ); - if ( diff < 0 ) - { + 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; + } + MANTTYPE* pa = a->mant; + MANTTYPE* pb = b->mant; + pa += a->cdigit - 1; + pb += b->cdigit - 1; + int32_t cdigits = max(a->cdigit, b->cdigit); + int32_t ccdigits = cdigits; + for (; cdigits > 0; cdigits--) + { + MANTTYPE da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0); + MANTTYPE db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0); + diff = da - db; + if (diff) { - if ( diff > 0 ) - { - return false; - } - else - { - pa = a->mant; - pb = b->mant; - pa += a->cdigit - 1; - pb += b->cdigit - 1; - cdigits = max( a->cdigit, b->cdigit ); - ccdigits = cdigits; - for ( ;cdigits > 0; cdigits-- ) - { - da = ( (cdigits > (ccdigits - a->cdigit) ) ? - *pa-- : 0 ); - db = ( (cdigits > (ccdigits - b->cdigit) ) ? - *pb-- : 0 ); - diff = da-db; - if ( diff ) - { - return( diff < 0 ); - } - } - // In this case, they are equal. - return false; - } + return (diff < 0); } + } + // In this case, they are equal. + return false; } //---------------------------------------------------------------------------- @@ -632,24 +599,24 @@ bool lessnum( PNUMBER a, PNUMBER b ) // //---------------------------------------------------------------------------- -bool zernum( PNUMBER a ) +bool zernum(_In_ PNUMBER a) { - long length; - MANTTYPE *pcha; + int32_t length; + MANTTYPE* pcha; length = a->cdigit; pcha = a->mant; // loop over all the digits until you find a nonzero or until you run // out of digits - while ( length-- > 0 ) + while (length-- > 0) + { + if (*pcha++) { - if ( *pcha++ ) - { // One of the digits isn't zero, therefore the number isn't zero return false; - } } + } // All of the digits are zero, therefore the number is zero return true; } diff --git a/src/CalcManager/Ratpack/rat.cpp b/src/CalcManager/Ratpack/rat.cpp index acc9fc65..101d814f 100644 --- a/src/CalcManager/Ratpack/rat.cpp +++ b/src/CalcManager/Ratpack/rat.cpp @@ -16,7 +16,6 @@ // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" using namespace std; @@ -38,7 +37,7 @@ using namespace std; // //----------------------------------------------------------------------------- -void gcdrat(PRAT* pa, int32_t precision) +void gcdrat(_Inout_ PRAT* pa, int32_t precision) { PNUMBER pgcd = nullptr; @@ -76,16 +75,16 @@ void gcdrat(PRAT* pa, int32_t precision) // //----------------------------------------------------------------------------- -void fracrat( PRAT *pa , uint32_t radix, int32_t precision) +void fracrat(_Inout_ PRAT* pa, uint32_t radix, int32_t precision) { // Only do the flatrat operation if number is nonzero. // and only if the bottom part is not one. - if ( !zernum( (*pa)->pp ) && !equnum( (*pa)->pq, num_one ) ) + if (!zernum((*pa)->pp) && !equnum((*pa)->pq, num_one)) { flatrat(*pa, radix, precision); } - remnum( &((*pa)->pp), (*pa)->pq, BASEX ); + remnum(&((*pa)->pp), (*pa)->pq, BASEX); // Get *pa back in the integer over integer form. RENORMALIZE(*pa); @@ -104,26 +103,25 @@ void fracrat( PRAT *pa , uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void mulrat( PRAT *pa, PRAT b, int32_t precision) +void mulrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision) - { +{ // Only do the multiply if it isn't zero. - if ( !zernum( (*pa)->pp ) ) - { - mulnumx( &((*pa)->pp), b->pp ); - mulnumx( &((*pa)->pq), b->pq ); + if (!zernum((*pa)->pp)) + { + mulnumx(&((*pa)->pp), b->pp); + mulnumx(&((*pa)->pq), b->pq); trimit(pa, precision); - } + } else - { + { // If it is zero, blast a one in the denominator. - DUPNUM( ((*pa)->pq), num_one ); - } + DUPNUM(((*pa)->pq), num_one); + } #ifdef MULGCD - gcdrat( pa ); + gcdrat(pa); #endif - } //----------------------------------------------------------------------------- @@ -139,44 +137,41 @@ void mulrat( PRAT *pa, PRAT b, int32_t precision) // //----------------------------------------------------------------------------- - -void divrat( PRAT *pa, PRAT b, int32_t precision) +void divrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision) { - - if ( !zernum( (*pa)->pp ) ) - { + if (!zernum((*pa)->pp)) + { // Only do the divide if the top isn't zero. - mulnumx( &((*pa)->pp), b->pq ); - mulnumx( &((*pa)->pq), b->pp ); + mulnumx(&((*pa)->pp), b->pq); + mulnumx(&((*pa)->pq), b->pp); - if ( zernum( (*pa)->pq ) ) - { - // raise an exception if the bottom is 0. - throw( CALC_E_DIVIDEBYZERO ); - } - trimit(pa, precision); - } - else + if (zernum((*pa)->pq)) { + // raise an exception if the bottom is 0. + throw(CALC_E_DIVIDEBYZERO); + } + trimit(pa, precision); + } + else + { // Top is zero. - if ( zerrat( b ) ) - { + if (zerrat(b)) + { // If bottom is zero // 0 / 0 is indefinite, raise an exception. - throw( CALC_E_INDEFINITE ); - } - else - { - // 0/x make a unique 0. - DUPNUM( ((*pa)->pq), num_one ); - } + throw(CALC_E_INDEFINITE); } + else + { + // 0/x make a unique 0. + DUPNUM(((*pa)->pq), num_one); + } + } #ifdef DIVGCD - gcdrat( pa ); + gcdrat(pa); #endif - } //----------------------------------------------------------------------------- @@ -192,11 +187,11 @@ void divrat( PRAT *pa, PRAT b, int32_t precision) // //----------------------------------------------------------------------------- -void subrat( PRAT *pa, PRAT b, int32_t precision) +void subrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision) { b->pp->sign *= -1; - addrat( pa, b, precision); + addrat(pa, b, precision); b->pp->sign *= -1; } @@ -213,13 +208,13 @@ void subrat( PRAT *pa, PRAT b, int32_t precision) // //----------------------------------------------------------------------------- -void addrat( PRAT *pa, PRAT b, int32_t precision) +void addrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision) { - PNUMBER bot= nullptr; + PNUMBER bot = nullptr; - if ( equnum( (*pa)->pq, b->pq ) ) - { + if (equnum((*pa)->pq, b->pq)) + { // Very special case, q's match., // make sure signs are involved in the calculation // we have to do this since the optimization here is only @@ -228,33 +223,30 @@ void addrat( PRAT *pa, PRAT b, int32_t precision) (*pa)->pq->sign = 1; b->pp->sign *= b->pq->sign; b->pq->sign = 1; - addnum( &((*pa)->pp), b->pp, BASEX ); - } + addnum(&((*pa)->pp), b->pp, BASEX); + } else - { + { // Usual case q's aren't the same. - DUPNUM( bot, (*pa)->pq ); - mulnumx( &bot, b->pq ); - mulnumx( &((*pa)->pp), b->pq ); - mulnumx( &((*pa)->pq), b->pp ); - addnum( &((*pa)->pp), (*pa)->pq, BASEX ); - destroynum( (*pa)->pq ); + DUPNUM(bot, (*pa)->pq); + mulnumx(&bot, b->pq); + mulnumx(&((*pa)->pp), b->pq); + mulnumx(&((*pa)->pq), b->pp); + addnum(&((*pa)->pp), (*pa)->pq, BASEX); + destroynum((*pa)->pq); (*pa)->pq = bot; trimit(pa, precision); // Get rid of negative zeros here. (*pa)->pp->sign *= (*pa)->pq->sign; (*pa)->pq->sign = 1; - } + } #ifdef ADDGCD - gcdrat( pa ); + gcdrat(pa); #endif - } - - //----------------------------------------------------------------------------- // // FUNCTION: rootrat @@ -268,11 +260,11 @@ void addrat( PRAT *pa, PRAT b, int32_t precision) // //----------------------------------------------------------------------------- -void rootrat( PRAT *py, PRAT n, uint32_t radix, int32_t precision) +void rootrat(_Inout_ PRAT* py, _In_ PRAT n, uint32_t radix, int32_t precision) { // Initialize 1/n - PRAT oneovern= nullptr; - DUPRAT(oneovern,rat_one); + PRAT oneovern = nullptr; + DUPRAT(oneovern, rat_one); divrat(&oneovern, n, precision); powrat(py, oneovern, radix, precision); @@ -280,7 +272,6 @@ void rootrat( PRAT *py, PRAT n, uint32_t radix, int32_t precision) destroyrat(oneovern); } - //----------------------------------------------------------------------------- // // FUNCTION: zerrat @@ -294,10 +285,8 @@ void rootrat( PRAT *py, PRAT n, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -bool zerrat( PRAT a ) +bool zerrat(_In_ PRAT a) { - return( zernum(a->pp) ); + return (zernum(a->pp)); } - - diff --git a/src/CalcManager/Ratpack/ratconst.h b/src/CalcManager/Ratpack/ratconst.h index be24b002..23efaabb 100644 --- a/src/CalcManager/Ratpack/ratconst.h +++ b/src/CalcManager/Ratpack/ratconst.h @@ -4,481 +4,591 @@ #pragma once // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_one= { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_num_one = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_two= { - 1, - 1, - 0, - { 2,} -}; +inline const NUMBER init_num_two = { 1, + 1, + 0, + { + 2, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_five= { - 1, - 1, - 0, - { 5,} -}; +inline const NUMBER init_num_five = { 1, + 1, + 0, + { + 5, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_six= { - 1, - 1, - 0, - { 6,} -}; +inline const NUMBER init_num_six = { 1, + 1, + 0, + { + 6, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_ten= { - 1, - 1, - 0, - { 10,} -}; +inline const NUMBER init_num_ten = { 1, + 1, + 0, + { + 10, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_smallest = { - 1, - 1, - 0, - { 1,} -}; -inline const NUMBER init_q_rat_smallest = { - 1, - 4, - 0, - { 0, 190439170, 901055854, 10097,} -}; +inline const NUMBER init_p_rat_smallest = { 1, + 1, + 0, + { + 1, + } }; +inline const NUMBER init_q_rat_smallest = { 1, + 4, + 0, + { + 0, + 190439170, + 901055854, + 10097, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_negsmallest = { - -1, - 1, - 0, - { 1,} -}; -inline const NUMBER init_q_rat_negsmallest = { - 1, - 4, - 0, - { 0, 190439170, 901055854, 10097,} -}; +inline const NUMBER init_p_rat_negsmallest = { -1, + 1, + 0, + { + 1, + } }; +inline const NUMBER init_q_rat_negsmallest = { 1, + 4, + 0, + { + 0, + 190439170, + 901055854, + 10097, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_pt_eight_five = { - 1, - 1, - 0, - { 85,} -}; -inline const NUMBER init_q_pt_eight_five = { - 1, - 1, - 0, - { 100,} -}; +inline const NUMBER init_p_pt_eight_five = { 1, + 1, + 0, + { + 85, + } }; +inline const NUMBER init_q_pt_eight_five = { 1, + 1, + 0, + { + 100, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_six = { - 1, - 1, - 0, - { 6,} -}; -inline const NUMBER init_q_rat_six = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_six = { 1, + 1, + 0, + { + 6, + } }; +inline const NUMBER init_q_rat_six = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_two = { - 1, - 1, - 0, - { 2,} -}; -inline const NUMBER init_q_rat_two = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_two = { 1, + 1, + 0, + { + 2, + } }; +inline const NUMBER init_q_rat_two = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_zero = { - 1, - 1, - 0, - { 0,} -}; -inline const NUMBER init_q_rat_zero = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_zero = { 1, + 1, + 0, + { + 0, + } }; +inline const NUMBER init_q_rat_zero = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_one = { - 1, - 1, - 0, - { 1,} -}; -inline const NUMBER init_q_rat_one = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_one = { 1, + 1, + 0, + { + 1, + } }; +inline const NUMBER init_q_rat_one = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_neg_one = { - -1, - 1, - 0, - { 1,} -}; -inline const NUMBER init_q_rat_neg_one = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_neg_one = { -1, + 1, + 0, + { + 1, + } }; +inline const NUMBER init_q_rat_neg_one = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_half = { - 1, - 1, - 0, - { 1,} -}; -inline const NUMBER init_q_rat_half = { - 1, - 1, - 0, - { 2,} -}; +inline const NUMBER init_p_rat_half = { 1, + 1, + 0, + { + 1, + } }; +inline const NUMBER init_q_rat_half = { 1, + 1, + 0, + { + 2, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_ten = { - 1, - 1, - 0, - { 10,} -}; -inline const NUMBER init_q_rat_ten = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_ten = { 1, + 1, + 0, + { + 10, + } }; +inline const NUMBER init_q_rat_ten = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_pi = { - 1, - 6, - 0, - { 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,} -}; -inline const NUMBER init_q_pi = { - 1, - 6, - 0, - { 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2,} -}; +inline const NUMBER init_p_pi = { 1, + 6, + 0, + { + 125527896, + 283898350, + 1960493936, + 1672850762, + 1288168272, + 8, + } }; +inline const NUMBER init_q_pi = { 1, + 6, + 0, + { + 1288380402, + 1120116153, + 1860424692, + 1944118326, + 1583591604, + 2, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_two_pi = { - 1, - 6, - 0, - { 251055792, 567796700, 1773504224, 1198217877, 428852897, 17,} -}; -inline const NUMBER init_q_two_pi = { - 1, - 6, - 0, - { 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2,} -}; +inline const NUMBER init_p_two_pi = { 1, + 6, + 0, + { + 251055792, + 567796700, + 1773504224, + 1198217877, + 428852897, + 17, + } }; +inline const NUMBER init_q_two_pi = { 1, + 6, + 0, + { + 1288380402, + 1120116153, + 1860424692, + 1944118326, + 1583591604, + 2, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_pi_over_two = { - 1, - 6, - 0, - { 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,} -}; -inline const NUMBER init_q_pi_over_two = { - 1, - 6, - 0, - { 429277156, 92748659, 1573365737, 1740753005, 1019699561, 5,} -}; +inline const NUMBER init_p_pi_over_two = { 1, + 6, + 0, + { + 125527896, + 283898350, + 1960493936, + 1672850762, + 1288168272, + 8, + } }; +inline const NUMBER init_q_pi_over_two = { 1, + 6, + 0, + { + 429277156, + 92748659, + 1573365737, + 1740753005, + 1019699561, + 5, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_one_pt_five_pi = { - 1, - 6, - 0, - { 1241201312, 270061909, 1051574664, 1924965045, 1340320627, 70,} -}; -inline const NUMBER init_q_one_pt_five_pi = { - 1, - 6, - 0, - { 1579671539, 1837970263, 1067644340, 523549916, 2119366659, 14,} -}; +inline const NUMBER init_p_one_pt_five_pi = { 1, + 6, + 0, + { + 1241201312, + 270061909, + 1051574664, + 1924965045, + 1340320627, + 70, + } }; +inline const NUMBER init_q_one_pt_five_pi = { 1, + 6, + 0, + { + 1579671539, + 1837970263, + 1067644340, + 523549916, + 2119366659, + 14, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_e_to_one_half = { - 1, - 6, - 0, - { 256945612, 216219427, 223516738, 477442596, 581063757, 23,} -}; -inline const NUMBER init_q_e_to_one_half = { - 1, - 6, - 0, - { 1536828363, 698484484, 1127331835, 224219346, 245499408, 14,} -}; +inline const NUMBER init_p_e_to_one_half = { 1, + 6, + 0, + { + 256945612, + 216219427, + 223516738, + 477442596, + 581063757, + 23, + } }; +inline const NUMBER init_q_e_to_one_half = { 1, + 6, + 0, + { + 1536828363, + 698484484, + 1127331835, + 224219346, + 245499408, + 14, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_exp = { - 1, - 6, - 0, - { 943665199, 1606559160, 1094967530, 1759391384, 1671799163, 1123581,} -}; -inline const NUMBER init_q_rat_exp = { - 1, - 6, - 0, - { 879242208, 2022880100, 617392930, 1374929092, 1367479163, 413342,} -}; +inline const NUMBER init_p_rat_exp = { 1, + 6, + 0, + { + 943665199, + 1606559160, + 1094967530, + 1759391384, + 1671799163, + 1123581, + } }; +inline const NUMBER init_q_rat_exp = { 1, + 6, + 0, + { + 879242208, + 2022880100, + 617392930, + 1374929092, + 1367479163, + 413342, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_ln_ten = { - 1, - 6, - 0, - { 2086268922, 165794492, 1416063951, 1851428830, 1893239400, 65366841,} -}; -inline const NUMBER init_q_ln_ten = { - 1, - 6, - 0, - { 26790652, 564532679, 783998273, 216030448, 1564709968, 28388458,} -}; +inline const NUMBER init_p_ln_ten = { 1, + 6, + 0, + { + 2086268922, + 165794492, + 1416063951, + 1851428830, + 1893239400, + 65366841, + } }; +inline const NUMBER init_q_ln_ten = { 1, + 6, + 0, + { + 26790652, + 564532679, + 783998273, + 216030448, + 1564709968, + 28388458, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_ln_two = { - 1, - 6, - 0, - { 1789230241, 1057927868, 715399197, 908801241, 1411265331, 3,} -}; -inline const NUMBER init_q_ln_two = { - 1, - 6, - 0, - { 1559869847, 1930657510, 1228561531, 219003871, 593099283, 5,} -}; +inline const NUMBER init_p_ln_two = { 1, + 6, + 0, + { + 1789230241, + 1057927868, + 715399197, + 908801241, + 1411265331, + 3, + } }; +inline const NUMBER init_q_ln_two = { 1, + 6, + 0, + { + 1559869847, + 1930657510, + 1228561531, + 219003871, + 593099283, + 5, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rad_to_deg = { - 1, - 6, - 0, - { 2127722024, 1904928383, 2016479213, 2048947859, 1578647346, 492,} -}; -inline const NUMBER init_q_rad_to_deg = { - 1, - 6, - 0, - { 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,} -}; +inline const NUMBER init_p_rad_to_deg = { 1, + 6, + 0, + { + 2127722024, + 1904928383, + 2016479213, + 2048947859, + 1578647346, + 492, + } }; +inline const NUMBER init_q_rad_to_deg = { 1, + 6, + 0, + { + 125527896, + 283898350, + 1960493936, + 1672850762, + 1288168272, + 8, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rad_to_grad = { - 1, - 6, - 0, - { 2125526288, 684931327, 570267400, 129125085, 1038224725, 547,} -}; -inline const NUMBER init_q_rad_to_grad = { - 1, - 6, - 0, - { 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8,} -}; +inline const NUMBER init_p_rad_to_grad = { 1, + 6, + 0, + { + 2125526288, + 684931327, + 570267400, + 129125085, + 1038224725, + 547, + } }; +inline const NUMBER init_q_rad_to_grad = { 1, + 6, + 0, + { + 125527896, + 283898350, + 1960493936, + 1672850762, + 1288168272, + 8, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_qword = { - 1, - 3, - 0, - { 2147483647, 2147483647, 3,} -}; -inline const NUMBER init_q_rat_qword = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_qword = { 1, + 3, + 0, + { + 2147483647, + 2147483647, + 3, + } }; +inline const NUMBER init_q_rat_qword = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_dword = { - 1, - 2, - 0, - { 2147483647, 1,} -}; -inline const NUMBER init_q_rat_dword = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_dword = { 1, + 2, + 0, + { + 2147483647, + 1, + } }; +inline const NUMBER init_q_rat_dword = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_max_long = { - 1, - 1, - 0, - { 2147483647,} -}; -inline const NUMBER init_q_rat_max_long = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_max_i32 = { 1, + 1, + 0, + { + 2147483647, + } }; +inline const NUMBER init_q_rat_max_i32 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_min_long = { - -1, - 2, - 0, - { 0, 1,} -}; -inline const NUMBER init_q_rat_min_long = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_min_i32 = { -1, + 2, + 0, + { + 0, + 1, + } }; +inline const NUMBER init_q_rat_min_i32 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_word = { - 1, - 1, - 0, - { 65535,} -}; -inline const NUMBER init_q_rat_word = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_word = { 1, + 1, + 0, + { + 65535, + } }; +inline const NUMBER init_q_rat_word = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_byte = { - 1, - 1, - 0, - { 255,} -}; -inline const NUMBER init_q_rat_byte = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_byte = { 1, + 1, + 0, + { + 255, + } }; +inline const NUMBER init_q_rat_byte = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_400 = { - 1, - 1, - 0, - { 400,} -}; -inline const NUMBER init_q_rat_400 = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_400 = { 1, + 1, + 0, + { + 400, + } }; +inline const NUMBER init_q_rat_400 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_360 = { - 1, - 1, - 0, - { 360,} -}; -inline const NUMBER init_q_rat_360 = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_360 = { 1, + 1, + 0, + { + 360, + } }; +inline const NUMBER init_q_rat_360 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_200 = { - 1, - 1, - 0, - { 200,} -}; -inline const NUMBER init_q_rat_200 = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_200 = { 1, + 1, + 0, + { + 200, + } }; +inline const NUMBER init_q_rat_200 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_180 = { - 1, - 1, - 0, - { 180,} -}; -inline const NUMBER init_q_rat_180 = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_180 = { 1, + 1, + 0, + { + 180, + } }; +inline const NUMBER init_q_rat_180 = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_max_exp = { - 1, - 1, - 0, - { 100000,} -}; -inline const NUMBER init_q_rat_max_exp = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_max_exp = { 1, + 1, + 0, + { + 100000, + } }; +inline const NUMBER init_q_rat_max_exp = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_min_exp = { - -1, - 1, - 0, - { 100000,} -}; -inline const NUMBER init_q_rat_min_exp = { - 1, - 1, - 0, - { 1,} -}; +inline const NUMBER init_p_rat_min_exp = { -1, + 1, + 0, + { + 100000, + } }; +inline const NUMBER init_q_rat_min_exp = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_max_fact = { - 1, - 1, - 0, - { 3249, } -}; -inline const NUMBER init_q_rat_max_fact = { - 1, - 1, - 0, - { 1, } -}; +inline const NUMBER init_p_rat_max_fact = { 1, + 1, + 0, + { + 3249, + } }; +inline const NUMBER init_q_rat_max_fact = { 1, + 1, + 0, + { + 1, + } }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_min_fact = { - -1, - 1, - 0, - { 1000, } -}; -inline const NUMBER init_q_rat_min_fact = { - 1, - 1, - 0, - { 1, } -}; +inline const NUMBER init_p_rat_min_fact = { -1, + 1, + 0, + { + 1000, + } }; +inline const NUMBER init_q_rat_min_fact = { 1, + 1, + 0, + { + 1, + } }; diff --git a/src/CalcManager/Ratpack/ratpak.h b/src/CalcManager/Ratpack/ratpak.h index 9f72f873..7cb16327 100644 --- a/src/CalcManager/Ratpack/ratpak.h +++ b/src/CalcManager/Ratpack/ratpak.h @@ -17,33 +17,34 @@ // //----------------------------------------------------------------------------- +#include +#include #include "CalcErr.h" +#include // for memmove +#include "sal_cross_platform.h" // for SAL -static constexpr uint32_t BASEXPWR = 31L;// Internal log2(BASEX) +static constexpr uint32_t BASEXPWR = 31L; // Internal log2(BASEX) static constexpr uint32_t BASEX = 0x80000000; // Internal radix used in calculations, hope to raise - // this to 2^32 after solving scaling problems with - // overflow detection esp. in mul + // this to 2^32 after solving scaling problems with + // overflow detection esp. in mul -typedef unsigned long MANTTYPE; -typedef unsigned __int64 TWO_MANTTYPE; - -enum eNUMOBJ_FMT { - 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 +typedef uint32_t MANTTYPE; +typedef uint64_t TWO_MANTTYPE; +enum class NumberFormat +{ + 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 { - 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 - +enum class AngleType +{ + 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 @@ -51,21 +52,20 @@ typedef enum eANGLE_TYPE ANGLE_TYPE; //----------------------------------------------------------------------------- #pragma warning(push) -#pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union +#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union typedef struct _number { - long sign; // The sign of the mantissa, +1, or -1 - long cdigit; // The number of digits, or what passes for digits in the + int32_t sign; // The sign of the mantissa, +1, or -1 + int32_t cdigit; // The number of digits, or what passes for digits in the // radix being used. - long exp; // The offset of digits from the radix point + int32_t exp; // The offset of digits from the radix point // (decimal point in radix 10) MANTTYPE mant[]; - // This is actually allocated as a continuation of the - // NUMBER structure. + // This is actually allocated as a continuation of the + // NUMBER structure. } NUMBER, *PNUMBER, **PPNUMBER; #pragma warning(pop) - //----------------------------------------------------------------------------- // // RAT type is a representation radix on 2 NUMBER types. @@ -74,10 +74,10 @@ typedef struct _number //----------------------------------------------------------------------------- typedef struct _rat - { +{ PNUMBER pp; PNUMBER pq; - } RAT, *PRAT; +} RAT, *PRAT; static constexpr uint32_t MAX_LONG_SIZE = 33; // Base 2 requires 32 'digits' @@ -127,28 +127,38 @@ extern PRAT rat_max_exp; extern PRAT rat_min_exp; extern PRAT rat_max_fact; extern PRAT rat_min_fact; -extern PRAT rat_max_long; -extern PRAT rat_min_long; +extern PRAT rat_max_i32; +extern PRAT rat_min_i32; // DUPNUM Duplicates a number taking care of allocation and internals -#define DUPNUM(a,b) destroynum(a);createnum( a, (b)->cdigit );_dupnum(a, b); +#define DUPNUM(a, b) \ + destroynum(a); \ + createnum(a, (b)->cdigit); \ + _dupnum(a, b); // DUPRAT Duplicates a rational taking care of allocation and internals -#define DUPRAT(a,b) destroyrat(a);createrat(a);DUPNUM((a)->pp,(b)->pp);DUPNUM((a)->pq,(b)->pq); +#define DUPRAT(a, b) \ + destroyrat(a); \ + createrat(a); \ + DUPNUM((a)->pp, (b)->pp); \ + DUPNUM((a)->pq, (b)->pq); // LOG*RADIX calculates the integral portion of the log of a number in // the base currently being used, only accurate to within g_ratio -#define LOGNUMRADIX(pnum) (((pnum)->cdigit+(pnum)->exp)*g_ratio) -#define LOGRATRADIX(prat) (LOGNUMRADIX((prat)->pp)-LOGNUMRADIX((prat)->pq)) +#define LOGNUMRADIX(pnum) (((pnum)->cdigit + (pnum)->exp) * g_ratio) +#define LOGRATRADIX(prat) (LOGNUMRADIX((prat)->pp) - LOGNUMRADIX((prat)->pq)) // LOG*2 calculates the integral portion of the log of a number in // the internal base being used, only accurate to within g_ratio -#define LOGNUM2(pnum) ((pnum)->cdigit+(pnum)->exp) -#define LOGRAT2(prat) (LOGNUM2((prat)->pp)-LOGNUM2((prat)->pq)) +#define LOGNUM2(pnum) ((pnum)->cdigit + (pnum)->exp) +#define LOGRAT2(prat) (LOGNUM2((prat)->pp) - LOGNUM2((prat)->pq)) -#if defined( DEBUG_RATPAK ) +// SIGN returns the sign of the rational +#define SIGN(prat) ((prat)->pp->sign * (prat)->pq->sign) + +#if defined(DEBUG_RATPAK) //----------------------------------------------------------------------------- // // Debug versions of rational number creation and destruction routines. @@ -156,37 +166,39 @@ extern PRAT rat_min_long; // //----------------------------------------------------------------------------- -#define createrat(y) (y)=_createrat(); \ -{ \ - std::wstringstream outputString; \ - outputString << "createrat " << y << " " << # y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ - OutputDebugString(outputString.str().c_str()); \ -} -#define destroyrat(x) \ -{ \ - std::wstringstream outputString; \ - outputString << "destroyrat " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ - OutputDebugString(outputString.str().c_str()); \ -} \ -_destroyrat(x),(x)=nullptr -#define createnum(y,x) (y)=_createnum(x); \ -{ \ - std::wstringstream outputString; \ - outputString << "createnum " << y << " " << # y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ - OutputDebugString(outputString.str().c_str()); \ -} -#define destroynum(x) \ -{ \ - std::wstringstream outputString; \ - outputString << "destroynum " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ - OutputDebugString(outputString.str().c_str()); \ -} \ -_destroynum(x),(x)=nullptr +#define createrat(y) \ + (y) = _createrat(); \ + { \ + std::wstringstream outputString; \ + outputString << "createrat " << y << " " << #y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ + OutputDebugString(outputString.str().c_str()); \ + } +#define destroyrat(x) \ + { \ + std::wstringstream outputString; \ + outputString << "destroyrat " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ + OutputDebugString(outputString.str().c_str()); \ + } \ + _destroyrat(x), (x) = nullptr +#define createnum(y, x) \ + (y) = _createnum(x); \ + { \ + std::wstringstream outputString; \ + outputString << "createnum " << y << " " << #y << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ + OutputDebugString(outputString.str().c_str()); \ + } +#define destroynum(x) \ + { \ + std::wstringstream outputString; \ + outputString << "destroynum " << x << " file= " << __FILE__ << ", line= " << __LINE__ << "\n"; \ + OutputDebugString(outputString.str().c_str()); \ + } \ + _destroynum(x), (x) = nullptr #else -#define createrat(y) (y)=_createrat() -#define destroyrat(x) _destroyrat(x),(x)=nullptr -#define createnum(y,x) (y)=_createnum(x) -#define destroynum(x) _destroynum(x),(x)=nullptr +#define createrat(y) (y) = _createrat() +#define destroyrat(x) _destroyrat(x), (x) = nullptr +#define createnum(y, x) (y) = _createnum(x) +#define destroynum(x) _destroynum(x), (x) = nullptr #endif //----------------------------------------------------------------------------- @@ -197,40 +209,47 @@ _destroynum(x),(x)=nullptr //----------------------------------------------------------------------------- // RENORMALIZE, gets the exponents non-negative. -#define RENORMALIZE(x) if ( (x)->pp->exp < 0 ) { \ - (x)->pq->exp -= (x)->pp->exp; \ - (x)->pp->exp = 0; \ - } \ - if ( (x)->pq->exp < 0 ) { \ - (x)->pp->exp -= (x)->pq->exp; \ - (x)->pq->exp = 0; \ +#define RENORMALIZE(x) \ + if ((x)->pp->exp < 0) \ + { \ + (x)->pq->exp -= (x)->pp->exp; \ + (x)->pp->exp = 0; \ + } \ + if ((x)->pq->exp < 0) \ + { \ + (x)->pp->exp -= (x)->pq->exp; \ + (x)->pq->exp = 0; \ } // TRIMNUM ASSUMES the number is in radix form NOT INTERNAL BASEX!!! -#define TRIMNUM(x, precision) if ( !g_ftrueinfinite ) { \ - long trim = (x)->cdigit - precision-g_ratio;\ - if ( trim > 1 ) \ - { \ -memmove( (x)->mant, &((x)->mant[trim]), sizeof(MANTTYPE)*((x)->cdigit-trim) ); \ - (x)->cdigit -= trim; \ - (x)->exp += trim; \ - } \ - } +#define TRIMNUM(x, precision) \ + if (!g_ftrueinfinite) \ + { \ + int32_t trim = (x)->cdigit - precision - g_ratio; \ + if (trim > 1) \ + { \ + memmove((x)->mant, &((x)->mant[trim]), sizeof(MANTTYPE) * ((x)->cdigit - trim)); \ + (x)->cdigit -= trim; \ + (x)->exp += trim; \ + } \ + } // TRIMTOP ASSUMES the number is in INTERNAL BASEX!!! -#define TRIMTOP(x, precision) if ( !g_ftrueinfinite ) { \ - long trim = (x)->pp->cdigit - (precision/g_ratio) - 2;\ - if ( trim > 1 ) \ - { \ -memmove( (x)->pp->mant, &((x)->pp->mant[trim]), sizeof(MANTTYPE)*((x)->pp->cdigit-trim) ); \ - (x)->pp->cdigit -= trim; \ - (x)->pp->exp += trim; \ - } \ - trim = min((x)->pp->exp,(x)->pq->exp);\ - (x)->pp->exp -= trim;\ - (x)->pq->exp -= trim;\ - } +#define TRIMTOP(x, precision) \ + if (!g_ftrueinfinite) \ + { \ + int32_t trim = (x)->pp->cdigit - (precision / g_ratio) - 2; \ + if (trim > 1) \ + { \ + memmove((x)->pp->mant, &((x)->pp->mant[trim]), sizeof(MANTTYPE) * ((x)->pp->cdigit - trim)); \ + (x)->pp->cdigit -= trim; \ + (x)->pp->exp += trim; \ + } \ + trim = std::min((x)->pp->exp, (x)->pq->exp); \ + (x)->pp->exp -= trim; \ + (x)->pq->exp -= trim; \ + } -#define SMALL_ENOUGH_RAT(a, precision) (zernum((a)->pp) || ( ( ( (a)->pq->cdigit + (a)->pq->exp ) - ( (a)->pp->cdigit + (a)->pp->exp ) - 1 ) * g_ratio > precision ) ) +#define SMALL_ENOUGH_RAT(a, precision) (zernum((a)->pp) || ((((a)->pq->cdigit + (a)->pq->exp) - ((a)->pp->cdigit + (a)->pp->exp) - 1) * g_ratio > precision)) //----------------------------------------------------------------------------- // @@ -239,50 +258,55 @@ memmove( (x)->pp->mant, &((x)->pp->mant[trim]), sizeof(MANTTYPE)*((x)->pp->cdigi // //----------------------------------------------------------------------------- -#define CREATETAYLOR() PRAT xx=nullptr;\ - PNUMBER n2=nullptr; \ - PRAT pret=nullptr; \ - PRAT thisterm=nullptr; \ - DUPRAT(xx,*px); \ - mulrat(&xx,*px, precision); \ - createrat(pret); \ - pret->pp=longtonum( 0L, BASEX ); \ - pret->pq=longtonum( 0L, BASEX ); +#define CREATETAYLOR() \ + PRAT xx = nullptr; \ + PNUMBER n2 = nullptr; \ + PRAT pret = nullptr; \ + PRAT thisterm = nullptr; \ + DUPRAT(xx, *px); \ + mulrat(&xx, *px, precision); \ + createrat(pret); \ + pret->pp = i32tonum(0L, BASEX); \ + pret->pq = i32tonum(0L, BASEX); -#define DESTROYTAYLOR() destroynum( n2 ); \ - destroyrat( xx );\ - destroyrat( thisterm );\ - destroyrat( *px );\ - trimit(&pret, precision);\ - *px=pret; +#define DESTROYTAYLOR() \ + destroynum(n2); \ + destroyrat(xx); \ + destroyrat(thisterm); \ + destroyrat(*px); \ + trimit(&pret, precision); \ + *px = pret; // INC(a) is the rational equivalent of a++ // Check to see if we can avoid doing this the hard way. -#define INC(a) if ( (a)->mant[0] < BASEX - 1 ) \ - { \ - (a)->mant[0]++; \ - } \ - else \ - { \ - addnum( &(a), num_one, BASEX); \ +#define INC(a) \ + if ((a)->mant[0] < BASEX - 1) \ + { \ + (a)->mant[0]++; \ + } \ + else \ + { \ + addnum(&(a), num_one, BASEX); \ } -#define MSD(x) ((x)->mant[(x)->cdigit-1]) +#define MSD(x) ((x)->mant[(x)->cdigit - 1]) // MULNUM(b) is the rational equivalent of thisterm *= b where thisterm is // a rational and b is a number, NOTE this is a mixed type operation for // efficiency reasons. -#define MULNUM(b) mulnumx( &(thisterm->pp), b); +#define MULNUM(b) mulnumx(&(thisterm->pp), b); // DIVNUM(b) is the rational equivalent of thisterm /= b where thisterm is // a rational and b is a number, NOTE this is a mixed type operation for // efficiency reasons. -#define DIVNUM(b) mulnumx( &(thisterm->pq), b); +#define DIVNUM(b) mulnumx(&(thisterm->pq), b); // NEXTTERM(p,d) is the rational equivalent of // thisterm *= p // d // pret += thisterm -#define NEXTTERM(p,d,precision) mulrat(&thisterm,p,precision);d addrat( &pret, thisterm, precision ) +#define NEXTTERM(p, d, precision) \ + mulrat(&thisterm, p, precision); \ + d addrat(&pret, thisterm, precision) //----------------------------------------------------------------------------- // @@ -292,9 +316,9 @@ memmove( (x)->pp->mant, &((x)->pp->mant[trim]), sizeof(MANTTYPE)*((x)->pp->cdigi extern bool g_ftrueinfinite; // set to true to allow infinite precision // don't use unless you know what you are doing - // used to help decide when to stop calculating. + // used to help decide when to stop calculating. -extern long g_ratio; // Internally calculated ratio of internal radix +extern int32_t g_ratio; // Internally calculated ratio of internal radix //----------------------------------------------------------------------------- // @@ -308,150 +332,155 @@ extern void SetDecimalSeparator(wchar_t decimalSeparator); // Call whenever either radix or precision changes, is smarter about recalculating constants. extern void ChangeConstants(uint32_t radix, int32_t precision); -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 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, 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 extern void flatrat(_Inout_ PRAT& prat, uint32_t radix, int32_t precision); -extern long numtolong(_In_ PNUMBER pnum, uint32_t radix ); -extern long rattolong(_In_ PRAT prat, uint32_t radix, int32_t precision); -ULONGLONG rattoUlonglong(_In_ PRAT prat, uint32_t radix, int32_t precision); -extern PNUMBER _createnum(_In_ ULONG size ); // returns an empty number structure with size digits +extern int32_t numtoi32(_In_ PNUMBER pnum, uint32_t radix); +extern int32_t rattoi32(_In_ PRAT prat, uint32_t radix, int32_t precision); +uint64_t rattoUi64(_In_ PRAT prat, uint32_t radix, int32_t precision); +extern PNUMBER _createnum(_In_ uint32_t size); // returns an empty number structure with size digits extern PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision); -extern PNUMBER gcd(_In_ PNUMBER a, _In_ PNUMBER b ); -extern PNUMBER StringToNumber(std::wstring_view numberString, uint32_t radix, int32_t precision); // takes a text representation of a number and returns a number. +extern PNUMBER gcd(_In_ PNUMBER a, _In_ PNUMBER b); +extern PNUMBER StringToNumber( + std::wstring_view numberString, + uint32_t radix, + int32_t precision); // takes a text representation of a number and returns a number. // takes a text representation of a number as a mantissa with sign and an exponent with sign. -extern PRAT StringToRat(bool mantissaIsNegative, std::wstring_view mantissa, bool exponentIsNegative, std::wstring_view exponent, uint32_t radix, int32_t precision); +extern PRAT +StringToRat(bool mantissaIsNegative, std::wstring_view mantissa, bool exponentIsNegative, std::wstring_view exponent, uint32_t radix, int32_t precision); -extern PNUMBER longfactnum(long inlong, uint32_t radix); -extern PNUMBER longprodnum(long start, long stop, uint32_t radix); -extern PNUMBER longtonum(long inlong, uint32_t radix); -extern PNUMBER Ulongtonum(unsigned long inlong, uint32_t radix); -extern PNUMBER numtonRadixx(PNUMBER a, uint32_t radix); +extern PNUMBER i32factnum(int32_t ini32, uint32_t radix); +extern PNUMBER i32prodnum(int32_t start, int32_t stop, uint32_t radix); +extern PNUMBER i32tonum(int32_t ini32, uint32_t radix); +extern PNUMBER Ui32tonum(uint32_t ini32, uint32_t radix); +extern PNUMBER numtonRadixx(_In_ PNUMBER a, uint32_t radix); // creates a empty/undefined rational representation (p/q) -extern PRAT _createrat( void ); +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); +extern void acoshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the acos of x->p/x->q -extern void acosrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +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); +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 // returns a new rat structure with the asin of x->p/x->q -extern void asinrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +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); +extern void atanhrat(_Inout_ PRAT* px, int32_t precision); // returns a new rat structure with the atan of x->p/x->q -extern void atanrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +extern void atanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the cosh of x->p/x->q -extern void coshrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +extern void coshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the cos of x->p/x->q -extern void cosrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +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); +extern void _exprat(_Inout_ PRAT* px, int32_t precision); // returns a new rat structure with the exp of x->p/x->q -extern void exprat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); +extern void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the log base 10 of x->p/x->q -extern void log10rat( _Inout_ PRAT *px, int32_t precision); +extern void log10rat(_Inout_ PRAT* px, int32_t precision); // returns a new rat structure with the natural log of x->p/x->q -extern void lograt( _Inout_ PRAT *px, int32_t precision); +extern void lograt(_Inout_ PRAT* px, int32_t precision); -extern PRAT longtorat( long inlong ); -extern PRAT Ulongtorat( unsigned long inulong ); -extern PRAT numtorat( _In_ PNUMBER pin, uint32_t radix); +extern PRAT i32torat(int32_t ini32); +extern PRAT Ui32torat(uint32_t inui32); +extern PRAT numtorat(_In_ PNUMBER pin, uint32_t radix); -extern void sinhrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); -extern void sinrat( _Inout_ PRAT *px ); +extern void sinhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); +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); +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); +extern void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER* const src); -extern void _destroynum( _In_ PNUMBER pnum ); -extern void _destroyrat( _In_ PRAT prat ); -extern void addnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix); -extern void addrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision); -extern void andrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern void divnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix, int32_t precision); -extern void divnumx( _Inout_ PNUMBER *pa, _In_ PNUMBER b, int32_t precision); -extern void divrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision); -extern void fracrat( _Inout_ PRAT *pa , uint32_t radix, int32_t precision); -extern void factrat( _Inout_ PRAT *pa, uint32_t radix, int32_t precision); -extern void modrat( _Inout_ PRAT *pa, _In_ PRAT b ); -extern void gcdrat( _Inout_ PRAT *pa, int32_t precision); -extern void intrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision); -extern void mulnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix); -extern void mulnumx( _Inout_ PNUMBER *pa, _In_ PNUMBER b ); -extern void mulrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision); -extern void numpowlong( _Inout_ PNUMBER *proot, long power, uint32_t radix, int32_t precision); -extern void numpowlongx( _Inout_ PNUMBER *proot, long power ); -extern void orrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern void powrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision); -extern void powratNumeratorDenominator(_Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void _destroynum(_Frees_ptr_opt_ PNUMBER pnum); +extern void _destroyrat(_Frees_ptr_opt_ PRAT prat); +extern void addnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix); +extern void addrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision); +extern void andrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void divnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix, int32_t precision); +extern void divnumx(_Inout_ PNUMBER* pa, _In_ PNUMBER b, int32_t precision); +extern void divrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision); +extern void fracrat(_Inout_ PRAT* pa, uint32_t radix, int32_t precision); +extern void factrat(_Inout_ PRAT* pa, uint32_t radix, int32_t precision); +extern void remrat(_Inout_ PRAT* pa, _In_ PRAT b); +extern void modrat(_Inout_ PRAT* pa, _In_ PRAT b); +extern void gcdrat(_Inout_ PRAT* pa, int32_t precision); +extern void intrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); +extern void mulnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix); +extern void mulnumx(_Inout_ PNUMBER* pa, _In_ PNUMBER b); +extern void mulrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision); +extern void numpowi32(_Inout_ PNUMBER* proot, int32_t power, uint32_t radix, int32_t precision); +extern void numpowi32x(_Inout_ PNUMBER* proot, int32_t power); +extern void orrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void powrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void powratNumeratorDenominator(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); extern void powratPowerOfPowers(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern void powratcomp(_Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern void ratpowlong( _Inout_ PRAT *proot, long power, int32_t precision); -extern void remnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix); -extern void rootrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision); -extern void scale2pi( _Inout_ PRAT *px, uint32_t radix, int32_t precision); -extern void scale( _Inout_ PRAT *px, _In_ PRAT scalefact, uint32_t radix, int32_t precision); -extern void subrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision); -extern void xorrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern void lshrat( _Inout_ PRAT *pa, _In_ PRAT b , uint32_t radix, int32_t precision); -extern void rshrat( _Inout_ PRAT *pa, _In_ PRAT b, uint32_t radix, int32_t precision); -extern bool rat_equ( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern bool rat_neq( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern bool rat_gt( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern bool rat_ge( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern bool rat_lt( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern bool rat_le( _In_ PRAT a, _In_ PRAT b, int32_t precision); -extern void inbetween( _In_ PRAT *px, _In_ PRAT range, int32_t precision); -extern void trimit( _Inout_ PRAT *px, int32_t precision); -extern void _dumprawrat(_In_ const wchar_t *varname, _In_ PRAT rat, std::wostream& out); -extern void _dumprawnum(_In_ const wchar_t *varname, _In_ PNUMBER num, std::wostream& out); +extern void powratcomp(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void ratpowi32(_Inout_ PRAT* proot, int32_t power, int32_t precision); +extern void remnum(_Inout_ PNUMBER* pa, _In_ PNUMBER b, uint32_t radix); +extern void rootrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void scale2pi(_Inout_ PRAT* px, uint32_t radix, int32_t precision); +extern void scale(_Inout_ PRAT* px, _In_ PRAT scalefact, uint32_t radix, int32_t precision); +extern void subrat(_Inout_ PRAT* pa, _In_ PRAT b, int32_t precision); +extern void xorrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision); +extern bool rat_equ(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern bool rat_neq(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern bool rat_gt(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern bool rat_ge(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern bool rat_lt(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern bool rat_le(_In_ PRAT a, _In_ PRAT b, int32_t precision); +extern void inbetween(_In_ PRAT* px, _In_ PRAT range, int32_t precision); +extern void trimit(_Inout_ PRAT* px, int32_t precision); +extern void _dumprawrat(_In_ const wchar_t* varname, _In_ PRAT rat, std::wostream& out); +extern void _dumprawnum(_In_ const wchar_t* varname, _In_ PNUMBER num, std::wostream& out); diff --git a/src/CalcManager/Ratpack/support.cpp b/src/CalcManager/Ratpack/support.cpp index f3b00d2f..ef9e4106 100644 --- a/src/CalcManager/Ratpack/support.cpp +++ b/src/CalcManager/Ratpack/support.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //---------------------------------------------------------------------------- @@ -18,35 +18,48 @@ // //---------------------------------------------------------------------------- -#include "pch.h" +#include +#include // for memmove +#include // for wostream #include "ratpak.h" using namespace std; -void _readconstants( void ); +void _readconstants(void); -#if defined( GEN_CONST ) +#if defined(GEN_CONST) static int cbitsofprecision = 0; #define READRAWRAT(v) #define READRAWNUM(v) -#define DUMPRAWRAT(v) _dumprawrat(#v,v, wcout) -#define DUMPRAWNUM(v) fprintf( stderr, \ -"// Autogenerated by _dumprawrat in support.cpp\n" ); \ - fprintf( stderr, "inline const NUMBER init_" #v "= {\n" ); \ - _dumprawnum(v, wcout); \ - fprintf( stderr, "};\n" ) - +#define DUMPRAWRAT(v) _dumprawrat(#v, v, wcout) +#define DUMPRAWNUM(v) \ + fprintf(stderr, "// Autogenerated by _dumprawrat in support.cpp\n"); \ + fprintf(stderr, "inline const NUMBER init_" #v "= {\n"); \ + _dumprawnum(v, wcout); \ + fprintf(stderr, "};\n") #else #define DUMPRAWRAT(v) #define DUMPRAWNUM(v) -#define READRAWRAT(v) createrat(v); DUPNUM((v)->pp,(&(init_p_##v))); \ -DUPNUM((v)->pq,(&(init_q_##v))); -#define READRAWNUM(v) DUPNUM(v,(&(init_##v))) +#define READRAWRAT(v) \ + createrat(v); \ + DUPNUM((v)->pp, (&(init_p_##v))); \ + DUPNUM((v)->pq, (&(init_q_##v))); +#define READRAWNUM(v) DUPNUM(v, (&(init_##v))) -#define INIT_AND_DUMP_RAW_NUM_IF_NULL(r, v) if (r == nullptr) { r = longtonum(v, BASEX); DUMPRAWNUM(v); } -#define INIT_AND_DUMP_RAW_RAT_IF_NULL(r, v) if (r == nullptr) { r = longtorat(v); DUMPRAWRAT(v); } +#define INIT_AND_DUMP_RAW_NUM_IF_NULL(r, v) \ + if (r == nullptr) \ + { \ + r = i32tonum(v, BASEX); \ + DUMPRAWNUM(v); \ + } +#define INIT_AND_DUMP_RAW_RAT_IF_NULL(r, v) \ + if (r == nullptr) \ + { \ + r = i32torat(v); \ + DUMPRAWRAT(v); \ + } static constexpr int RATIO_FOR_DECIMAL = 9; static constexpr int DECIMAL = 10; @@ -58,51 +71,51 @@ static int cbitsofprecision = RATIO_FOR_DECIMAL * DECIMAL * CALC_DECIMAL_DIGITS_ #endif -bool g_ftrueinfinite = false; // Set to true if you don't want - // chopping internally - // precision used internally +bool g_ftrueinfinite = false; // Set to true if you don't want + // chopping internally + // precision used internally -PNUMBER num_one= nullptr; -PNUMBER num_two= nullptr; -PNUMBER num_five= nullptr; -PNUMBER num_six= nullptr; -PNUMBER num_ten= nullptr; +PNUMBER num_one = nullptr; +PNUMBER num_two = nullptr; +PNUMBER num_five = nullptr; +PNUMBER num_six = nullptr; +PNUMBER num_ten = nullptr; -PRAT ln_ten= nullptr; -PRAT ln_two= nullptr; -PRAT rat_zero= nullptr; -PRAT rat_one= nullptr; -PRAT rat_neg_one= nullptr; -PRAT rat_two= nullptr; -PRAT rat_six= nullptr; -PRAT rat_half= nullptr; -PRAT rat_ten= nullptr; -PRAT pt_eight_five= nullptr; -PRAT pi= nullptr; -PRAT pi_over_two= nullptr; -PRAT two_pi= nullptr; -PRAT one_pt_five_pi= nullptr; -PRAT e_to_one_half= nullptr; -PRAT rat_exp= nullptr; -PRAT rad_to_deg= nullptr; -PRAT rad_to_grad= nullptr; -PRAT rat_qword= nullptr; -PRAT rat_dword= nullptr; // unsigned max ulong -PRAT rat_word= nullptr; -PRAT rat_byte= nullptr; -PRAT rat_360= nullptr; -PRAT rat_400= nullptr; -PRAT rat_180= nullptr; -PRAT rat_200= nullptr; -PRAT rat_nRadix= nullptr; -PRAT rat_smallest= nullptr; -PRAT rat_negsmallest= nullptr; -PRAT rat_max_exp= nullptr; -PRAT rat_min_exp= nullptr; +PRAT ln_ten = nullptr; +PRAT ln_two = nullptr; +PRAT rat_zero = nullptr; +PRAT rat_one = nullptr; +PRAT rat_neg_one = nullptr; +PRAT rat_two = nullptr; +PRAT rat_six = nullptr; +PRAT rat_half = nullptr; +PRAT rat_ten = nullptr; +PRAT pt_eight_five = nullptr; +PRAT pi = nullptr; +PRAT pi_over_two = nullptr; +PRAT two_pi = nullptr; +PRAT one_pt_five_pi = nullptr; +PRAT e_to_one_half = nullptr; +PRAT rat_exp = nullptr; +PRAT rad_to_deg = nullptr; +PRAT rad_to_grad = nullptr; +PRAT rat_qword = nullptr; +PRAT rat_dword = nullptr; // unsigned max ui32 +PRAT rat_word = nullptr; +PRAT rat_byte = nullptr; +PRAT rat_360 = nullptr; +PRAT rat_400 = nullptr; +PRAT rat_180 = nullptr; +PRAT rat_200 = nullptr; +PRAT rat_nRadix = nullptr; +PRAT rat_smallest = nullptr; +PRAT rat_negsmallest = nullptr; +PRAT rat_max_exp = nullptr; +PRAT rat_min_exp = nullptr; PRAT rat_max_fact = nullptr; PRAT rat_min_fact = nullptr; -PRAT rat_min_long= nullptr; // min signed long -PRAT rat_max_long= nullptr; // max signed long +PRAT rat_min_i32 = nullptr; // min signed i32 +PRAT rat_max_i32 = nullptr; // max signed i32 //---------------------------------------------------------------------------- // @@ -125,14 +138,14 @@ void ChangeConstants(uint32_t radix, int32_t precision) uint64_t limit = static_cast(BASEX) / static_cast(radix); g_ratio = 0; - for (uint32_t digit = 1; digit < limit; digit *= radix ) + for (uint32_t digit = 1; digit < limit; digit *= radix) { g_ratio++; } g_ratio += !g_ratio; destroyrat(rat_nRadix); - rat_nRadix=longtorat( radix ); + rat_nRadix = i32torat(radix); // Check to see what we have to recalculate and what we don't if (cbitsofprecision < (g_ratio * static_cast(radix) * precision)) @@ -166,7 +179,7 @@ void ChangeConstants(uint32_t radix, int32_t precision) INIT_AND_DUMP_RAW_RAT_IF_NULL(rat_min_fact, -1000); DUPRAT(rat_smallest, rat_nRadix); - ratpowlong(&rat_smallest, -precision, precision); + ratpowi32(&rat_smallest, -precision, precision); DUPRAT(rat_negsmallest, rat_smallest); rat_negsmallest->pp->sign = -1; DUMPRAWRAT(rat_smallest); @@ -183,29 +196,29 @@ void ChangeConstants(uint32_t radix, int32_t precision) if (pt_eight_five == nullptr) { createrat(pt_eight_five); - pt_eight_five->pp = longtonum(85L, BASEX); - pt_eight_five->pq = longtonum(100L, BASEX); + pt_eight_five->pp = i32tonum(85L, BASEX); + pt_eight_five->pq = i32tonum(100L, BASEX); DUMPRAWRAT(pt_eight_five); } DUPRAT(rat_qword, rat_two); - numpowlong(&(rat_qword->pp), 64, BASEX, precision); + numpowi32(&(rat_qword->pp), 64, BASEX, precision); subrat(&rat_qword, rat_one, precision); DUMPRAWRAT(rat_qword); DUPRAT(rat_dword, rat_two); - numpowlong(&(rat_dword->pp), 32, BASEX, precision); + numpowi32(&(rat_dword->pp), 32, BASEX, precision); subrat(&rat_dword, rat_one, precision); DUMPRAWRAT(rat_dword); - DUPRAT(rat_max_long, rat_two); - numpowlong(&(rat_max_long->pp), 31, BASEX, precision); - DUPRAT(rat_min_long, rat_max_long); - subrat(&rat_max_long, rat_one, precision); // rat_max_long = 2^31 -1 - DUMPRAWRAT(rat_max_long); + DUPRAT(rat_max_i32, rat_two); + numpowi32(&(rat_max_i32->pp), 31, BASEX, precision); + DUPRAT(rat_min_i32, rat_max_i32); + subrat(&rat_max_i32, rat_one, precision); // rat_max_i32 = 2^31 -1 + DUMPRAWRAT(rat_max_i32); - rat_min_long->pp->sign *= -1; // rat_min_long = -2^31 - DUMPRAWRAT(rat_min_long); + rat_min_i32->pp->sign *= -1; // rat_min_i32 = -2^31 + DUMPRAWRAT(rat_min_i32); DUPRAT(rat_min_exp, rat_max_exp); rat_min_exp->pp->sign *= -1; @@ -215,7 +228,7 @@ void ChangeConstants(uint32_t radix, int32_t precision) // Apparently when dividing 180 by pi, another (internal) digit of // precision is needed. - long extraPrecision = precision + g_ratio; + int32_t extraPrecision = precision + g_ratio; DUPRAT(pi, rat_half); asinrat(&pi, radix, extraPrecision); mulrat(&pi, rat_six, extraPrecision); @@ -251,14 +264,13 @@ void ChangeConstants(uint32_t radix, int32_t precision) lograt(&ln_two, extraPrecision); DUMPRAWRAT(ln_two); - destroyrat(rad_to_deg); - rad_to_deg = longtorat(180L); + rad_to_deg = i32torat(180L); divrat(&rad_to_deg, pi, extraPrecision); DUMPRAWRAT(rad_to_deg); destroyrat(rad_to_grad); - rad_to_grad = longtorat(200L); + rad_to_grad = i32torat(200L); divrat(&rad_to_grad, pi, extraPrecision); DUMPRAWRAT(rad_to_grad); } @@ -267,11 +279,10 @@ void ChangeConstants(uint32_t radix, int32_t precision) _readconstants(); DUPRAT(rat_smallest, rat_nRadix); - ratpowlong(&rat_smallest, -precision, precision); + ratpowi32(&rat_smallest, -precision, precision); DUPRAT(rat_negsmallest, rat_smallest); rat_negsmallest->pp->sign = -1; } - } //---------------------------------------------------------------------------- @@ -285,21 +296,21 @@ void ChangeConstants(uint32_t radix, int32_t precision) // //---------------------------------------------------------------------------- -void intrat( PRAT *px, uint32_t radix, int32_t precision) +void intrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { // Only do the intrat operation if number is nonzero. // and only if the bottom part is not one. - if ( !zernum( (*px)->pp ) && !equnum( (*px)->pq, num_one ) ) + if (!zernum((*px)->pp) && !equnum((*px)->pq, num_one)) { flatrat(*px, radix, precision); // Subtract the fractional part of the rational PRAT pret = nullptr; - DUPRAT(pret,*px); - modrat( &pret, rat_one ); + DUPRAT(pret, *px); + remrat(&pret, rat_one); - subrat( px, pret, precision); - destroyrat( pret ); + subrat(px, pret, precision); + destroyrat(pret); // Simplify the value if possible to resolve rounding errors flatrat(*px, radix, precision); @@ -317,44 +328,42 @@ void intrat( PRAT *px, uint32_t radix, int32_t precision) // //--------------------------------------------------------------------------- -bool rat_equ( PRAT a, PRAT b, int32_t precision) +bool rat_equ(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); rattmp->pp->sign *= -1; - addrat( &rattmp, b, precision); - bool bret = zernum( rattmp->pp ); - destroyrat( rattmp ); - return( bret ); + addrat(&rattmp, b, precision); + bool bret = zernum(rattmp->pp); + destroyrat(rattmp); + return (bret); } //--------------------------------------------------------------------------- // // FUNCTION: rat_ge // -// ARGUMENTS: PRAT a, PRAT b and long precision +// ARGUMENTS: PRAT a, PRAT b and int32_t precision // // RETURN: true if a is greater than or equal to b // // //--------------------------------------------------------------------------- -bool rat_ge( PRAT a, PRAT b, int32_t precision) +bool rat_ge(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); b->pp->sign *= -1; - addrat( &rattmp, b, precision); + addrat(&rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( zernum( rattmp->pp ) || - rattmp->pp->sign * rattmp->pq->sign == 1 ); - destroyrat( rattmp ); - return( bret ); + bool bret = (zernum(rattmp->pp) || SIGN(rattmp) == 1); + destroyrat(rattmp); + return (bret); } - //--------------------------------------------------------------------------- // // FUNCTION: rat_gt @@ -366,73 +375,67 @@ bool rat_ge( PRAT a, PRAT b, int32_t precision) // //--------------------------------------------------------------------------- -bool rat_gt( PRAT a, PRAT b, int32_t precision) +bool rat_gt(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); b->pp->sign *= -1; - addrat( &rattmp, b, precision); + addrat(&rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( !zernum( rattmp->pp ) && - rattmp->pp->sign * rattmp->pq->sign == 1 ); - destroyrat( rattmp ); - return( bret ); + bool bret = (!zernum(rattmp->pp) && SIGN(rattmp) == 1); + destroyrat(rattmp); + return (bret); } //--------------------------------------------------------------------------- // // FUNCTION: rat_le // -// ARGUMENTS: PRAT a, PRAT b and long precision +// ARGUMENTS: PRAT a, PRAT b and int32_t precision // // RETURN: true if a is less than or equal to b // // //--------------------------------------------------------------------------- -bool rat_le( PRAT a, PRAT b, int32_t precision) +bool rat_le(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); b->pp->sign *= -1; - addrat( &rattmp, b, precision); + addrat(&rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( zernum( rattmp->pp ) || - rattmp->pp->sign * rattmp->pq->sign == -1 ); - destroyrat( rattmp ); - return( bret ); + bool bret = (zernum(rattmp->pp) || SIGN(rattmp) == -1); + destroyrat(rattmp); + return (bret); } - //--------------------------------------------------------------------------- // // FUNCTION: rat_lt // -// ARGUMENTS: PRAT a, PRAT b and long precision +// ARGUMENTS: PRAT a, PRAT b and int32_t precision // // RETURN: true if a is less than b // // //--------------------------------------------------------------------------- -bool rat_lt( PRAT a, PRAT b, int32_t precision) +bool rat_lt(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); b->pp->sign *= -1; - addrat( &rattmp, b, precision); + addrat(&rattmp, b, precision); b->pp->sign *= -1; - bool bret = ( !zernum( rattmp->pp ) && - rattmp->pp->sign * rattmp->pq->sign == -1 ); - destroyrat( rattmp ); - return( bret ); + bool bret = (!zernum(rattmp->pp) && SIGN(rattmp) == -1); + destroyrat(rattmp); + return (bret); } - //--------------------------------------------------------------------------- // // FUNCTION: rat_neq @@ -444,17 +447,16 @@ bool rat_lt( PRAT a, PRAT b, int32_t precision) // //--------------------------------------------------------------------------- - -bool rat_neq( PRAT a, PRAT b, int32_t precision) +bool rat_neq(_In_ PRAT a, _In_ PRAT b, int32_t precision) { - PRAT rattmp= nullptr; - DUPRAT(rattmp,a); + PRAT rattmp = nullptr; + DUPRAT(rattmp, a); rattmp->pp->sign *= -1; - addrat( &rattmp, b, precision); - bool bret = !( zernum( rattmp->pp ) ); - destroyrat( rattmp ); - return( bret ); + addrat(&rattmp, b, precision); + bool bret = !(zernum(rattmp->pp)); + destroyrat(rattmp); + return (bret); } //--------------------------------------------------------------------------- @@ -468,27 +470,26 @@ bool rat_neq( PRAT a, PRAT b, int32_t precision) // //--------------------------------------------------------------------------- -void scale( PRAT *px, PRAT scalefact, uint32_t radix, int32_t precision ) +void scale(_Inout_ PRAT* px, _In_ PRAT scalefact, uint32_t radix, int32_t precision) { PRAT pret = nullptr; - DUPRAT(pret,*px); + DUPRAT(pret, *px); // Logscale is a quick way to tell how much extra precision is needed for // scaling by scalefact. - long logscale = g_ratio * ( (pret->pp->cdigit+pret->pp->exp) - - (pret->pq->cdigit+pret->pq->exp) ); - if ( logscale > 0 ) + int32_t logscale = g_ratio * ((pret->pp->cdigit + pret->pp->exp) - (pret->pq->cdigit + pret->pq->exp)); + if (logscale > 0) { precision += logscale; } - divrat( &pret, scalefact, precision); + divrat(&pret, scalefact, precision); intrat(&pret, radix, precision); - mulrat( &pret, scalefact, precision); + mulrat(&pret, scalefact, precision); pret->pp->sign *= -1; - addrat( px, pret, precision); + addrat(px, pret, precision); - destroyrat( pret ); + destroyrat(pret); } //--------------------------------------------------------------------------- @@ -502,38 +503,37 @@ void scale( PRAT *px, PRAT scalefact, uint32_t radix, int32_t precision ) // //--------------------------------------------------------------------------- -void scale2pi( PRAT *px, uint32_t radix, int32_t precision ) +void scale2pi(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { PRAT pret = nullptr; PRAT my_two_pi = nullptr; - DUPRAT(pret,*px); + DUPRAT(pret, *px); // Logscale is a quick way to tell how much extra precision is needed for // scaling by 2 pi. - long logscale = g_ratio * ( (pret->pp->cdigit+pret->pp->exp) - - (pret->pq->cdigit+pret->pq->exp) ); - if ( logscale > 0 ) + int32_t logscale = g_ratio * ((pret->pp->cdigit + pret->pp->exp) - (pret->pq->cdigit + pret->pq->exp)); + if (logscale > 0) { precision += logscale; - DUPRAT(my_two_pi,rat_half); - asinrat( &my_two_pi, radix, precision); - mulrat( &my_two_pi, rat_six, precision); - mulrat( &my_two_pi, rat_two, precision); + DUPRAT(my_two_pi, rat_half); + asinrat(&my_two_pi, radix, precision); + mulrat(&my_two_pi, rat_six, precision); + mulrat(&my_two_pi, rat_two, precision); } else { - DUPRAT(my_two_pi,two_pi); + DUPRAT(my_two_pi, two_pi); logscale = 0; } - divrat( &pret, my_two_pi, precision); + divrat(&pret, my_two_pi, precision); intrat(&pret, radix, precision); - mulrat( &pret, my_two_pi, precision); + mulrat(&pret, my_two_pi, precision); pret->pp->sign *= -1; - addrat( px, pret, precision); + addrat(px, pret, precision); - destroyrat( my_two_pi ); - destroyrat( pret ); + destroyrat(my_two_pi); + destroyrat(pret); } //--------------------------------------------------------------------------- @@ -546,22 +546,22 @@ void scale2pi( PRAT *px, uint32_t radix, int32_t precision ) // //--------------------------------------------------------------------------- -void inbetween( PRAT *px, PRAT range, int32_t precision) +void inbetween(_In_ PRAT* px, _In_ PRAT range, int32_t precision) { - if ( rat_gt(*px,range, precision) ) - { - DUPRAT(*px,range); - } + if (rat_gt(*px, range, precision)) + { + DUPRAT(*px, range); + } else + { + range->pp->sign *= -1; + if (rat_lt(*px, range, precision)) { - range->pp->sign *= -1; - if ( rat_lt(*px, range, precision) ) - { - DUPRAT(*px,range); - } - range->pp->sign *= -1; + DUPRAT(*px, range); } + range->pp->sign *= -1; + } } //--------------------------------------------------------------------------- @@ -575,11 +575,11 @@ void inbetween( PRAT *px, PRAT range, int32_t precision) // //--------------------------------------------------------------------------- -void _dumprawrat( const wchar_t *varname, PRAT rat, wostream& out) +void _dumprawrat(_In_ const wchar_t* varname, _In_ PRAT rat, wostream& out) { - _dumprawnum(varname, rat->pp, out ); - _dumprawnum(varname, rat->pq, out ); + _dumprawnum(varname, rat->pp, out); + _dumprawnum(varname, rat->pq, out); } //--------------------------------------------------------------------------- @@ -593,26 +593,24 @@ void _dumprawrat( const wchar_t *varname, PRAT rat, wostream& out) // //--------------------------------------------------------------------------- -void _dumprawnum(const wchar_t *varname, PNUMBER num, 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->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++ ) - { - out << L" "<< num->mant[i] << L","; - } + for (int i = 0; i < num->cdigit; i++) + { + out << L" " << num->mant[i] << L","; + } out << L"}\n"; out << L"};\n"; } -void _readconstants( void ) +void _readconstants(void) { READRAWNUM(num_one); @@ -652,15 +650,15 @@ void _readconstants( void ) READRAWRAT(rat_min_exp); READRAWRAT(rat_max_fact); READRAWRAT(rat_min_fact); - READRAWRAT(rat_min_long); - READRAWRAT(rat_max_long); + READRAWRAT(rat_min_i32); + READRAWRAT(rat_max_i32); } //--------------------------------------------------------------------------- // // FUNCTION: trimit // -// ARGUMENTS: PRAT *px, long precision +// ARGUMENTS: PRAT *px, int32_t precision // // // DESCRIPTION: Chops off digits from rational numbers to avoid time @@ -676,43 +674,42 @@ void _readconstants( void ) // //--------------------------------------------------------------------------- -void trimit( PRAT *px, int32_t precision) +void trimit(_Inout_ PRAT* px, int32_t precision) { - if ( !g_ftrueinfinite ) + if (!g_ftrueinfinite) + { + PNUMBER pp = (*px)->pp; + PNUMBER pq = (*px)->pq; + int32_t trim = g_ratio * (min((pp->cdigit + pp->exp), (pq->cdigit + pq->exp)) - 1) - precision; + if (trim > g_ratio) { - long trim; - PNUMBER pp=(*px)->pp; - PNUMBER pq=(*px)->pq; - trim = g_ratio * (min((pp->cdigit+pp->exp),(pq->cdigit+pq->exp))-1) - precision; - if ( trim > g_ratio ) - { trim /= g_ratio; - if ( trim <= pp->exp ) - { + if (trim <= pp->exp) + { pp->exp -= trim; - } - else - { - memmove( pp->mant, &(pp->mant[trim-pp->exp]), sizeof(MANTTYPE)*(pp->cdigit-trim+pp->exp) ); - pp->cdigit -= trim-pp->exp; - pp->exp = 0; - } - - if ( trim <= pq->exp ) - { - pq->exp -= trim; - } - else - { - memmove( pq->mant, &(pq->mant[trim-pq->exp]), sizeof(MANTTYPE)*(pq->cdigit-trim+pq->exp) ); - pq->cdigit -= trim-pq->exp; - pq->exp = 0; - } } - trim = min(pp->exp,pq->exp); + else + { + memmove(pp->mant, &(pp->mant[trim - pp->exp]), sizeof(MANTTYPE) * (pp->cdigit - trim + pp->exp)); + pp->cdigit -= trim - pp->exp; + pp->exp = 0; + } + + if (trim <= pq->exp) + { + pq->exp -= trim; + } + else + { + memmove(pq->mant, &(pq->mant[trim - pq->exp]), sizeof(MANTTYPE) * (pq->cdigit - trim + pq->exp)); + pq->cdigit -= trim - pq->exp; + pq->exp = 0; + } + } + trim = min(pp->exp, pq->exp); pp->exp -= trim; pq->exp -= trim; - } + } } diff --git a/src/CalcManager/Ratpack/trans.cpp b/src/CalcManager/Ratpack/trans.cpp index 5b46a936..50055e22 100644 --- a/src/CalcManager/Ratpack/trans.cpp +++ b/src/CalcManager/Ratpack/trans.cpp @@ -14,28 +14,24 @@ // //---------------------------------------------------------------------------- -#include "pch.h" #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 ) + switch (angletype) { - case ANGLE_RAD: - scale2pi( pa, radix, precision); + case AngleType::Radians: + scale2pi(pa, radix, precision); break; - case ANGLE_DEG: - scale( pa, rat_360, radix, precision); + case AngleType::Degrees: + scale(pa, rat_360, radix, precision); break; - case ANGLE_GRAD: - scale( pa, rat_400, radix, precision); + case AngleType::Gradians: + scale(pa, rat_400, radix, precision); break; } } - //----------------------------------------------------------------------------- // // FUNCTION: sinrat, _sinrat @@ -67,21 +63,21 @@ void scalerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_t p // //----------------------------------------------------------------------------- - -void _sinrat( PRAT *px, int32_t precision) +void _sinrat(PRAT* px, int32_t precision) { CREATETAYLOR(); - DUPRAT(pret,*px); - DUPRAT(thisterm,*px); + DUPRAT(pret, *px); + DUPRAT(thisterm, *px); - DUPNUM(n2,num_one); + DUPNUM(n2, num_one); xx->pp->sign *= -1; - do { - NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + do + { + NEXTTERM(xx, INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); @@ -90,42 +86,42 @@ void _sinrat( PRAT *px, int32_t precision) inbetween(px, rat_one, precision); // Since *px might be epsilon near zero we must set it to zero. - if ( rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision) ) - { - DUPRAT(*px,rat_zero); - } + if (rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision)) + { + DUPRAT(*px, rat_zero); + } } -void sinrat( PRAT *px, uint32_t radix, int32_t precision) +void sinrat(PRAT* px, uint32_t radix, int32_t precision) { scale2pi(px, radix, 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 ) + scalerat(pa, angletype, radix, precision); + switch (angletype) + { + case AngleType::Degrees: + if (rat_gt(*pa, rat_180, precision)) { - case ANGLE_DEG: - if ( rat_gt( *pa, rat_180, precision) ) - { subrat(pa, rat_360, precision); - } - divrat( pa, rat_180, precision); - mulrat( pa, pi, precision); - break; - case ANGLE_GRAD: - if ( rat_gt( *pa, rat_200, precision) ) - { - subrat(pa,rat_400, precision); - } - divrat( pa, rat_200, precision); - mulrat( pa, pi, precision); - break; } - _sinrat( pa, precision); + divrat(pa, rat_180, precision); + mulrat(pa, pi, precision); + break; + case AngleType::Gradians: + if (rat_gt(*pa, rat_200, precision)) + { + subrat(pa, rat_400, precision); + } + divrat(pa, rat_200, precision); + mulrat(pa, pi, precision); + break; + } + _sinrat(pa, precision); } //----------------------------------------------------------------------------- @@ -159,8 +155,7 @@ void sinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_ // //----------------------------------------------------------------------------- - -void _cosrat( PRAT *px, uint32_t radix, int32_t precision) +void _cosrat(PRAT* px, uint32_t radix, int32_t precision) { CREATETAYLOR(); @@ -168,67 +163,68 @@ void _cosrat( PRAT *px, uint32_t radix, int32_t precision) destroynum(pret->pp); destroynum(pret->pq); - pret->pp=longtonum( 1L, radix); - pret->pq=longtonum( 1L, radix); + pret->pp = i32tonum(1L, radix); + pret->pq = i32tonum(1L, radix); - DUPRAT(thisterm,pret) + DUPRAT(thisterm, pret) - n2=longtonum(0L, radix); + n2 = i32tonum(0L, radix); xx->pp->sign *= -1; - do { - NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + do + { + NEXTTERM(xx, INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); // Since *px might be epsilon above 1 or below -1, due to TRIMIT we need // this trick here. inbetween(px, rat_one, precision); // Since *px might be epsilon near zero we must set it to zero. - if ( rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision) ) - { - DUPRAT(*px,rat_zero); - } + if (rat_le(*px, rat_smallest, precision) && rat_ge(*px, rat_negsmallest, precision)) + { + DUPRAT(*px, rat_zero); + } } -void cosrat( PRAT *px, uint32_t radix, int32_t precision) +void cosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { scale2pi(px, radix, 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 ) + scalerat(pa, angletype, radix, precision); + switch (angletype) + { + case AngleType::Degrees: + if (rat_gt(*pa, rat_180, precision)) { - case ANGLE_DEG: - if ( rat_gt( *pa, rat_180, precision) ) - { - PRAT ptmp= nullptr; - DUPRAT(ptmp,rat_360); + PRAT ptmp = nullptr; + DUPRAT(ptmp, rat_360); subrat(&ptmp, *pa, precision); destroyrat(*pa); - *pa=ptmp; - } - divrat( pa, rat_180, precision); - mulrat( pa, pi, precision); - break; - case ANGLE_GRAD: - if ( rat_gt( *pa, rat_200, precision) ) - { - PRAT ptmp= nullptr; - DUPRAT(ptmp,rat_400); - subrat(&ptmp, *pa, precision); - destroyrat(*pa); - *pa=ptmp; - } - divrat( pa, rat_200, precision); - mulrat( pa, pi, precision); - break; + *pa = ptmp; } - _cosrat( pa, radix, precision); + divrat(pa, rat_180, precision); + mulrat(pa, pi, precision); + break; + case AngleType::Gradians: + if (rat_gt(*pa, rat_200, precision)) + { + PRAT ptmp = nullptr; + DUPRAT(ptmp, rat_400); + subrat(&ptmp, *pa, precision); + destroyrat(*pa); + *pa = ptmp; + } + divrat(pa, rat_200, precision); + mulrat(pa, pi, precision); + break; + } + _cosrat(pa, radix, precision); } //----------------------------------------------------------------------------- @@ -243,55 +239,52 @@ void cosanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32_ // //----------------------------------------------------------------------------- - -void _tanrat( PRAT *px, uint32_t radix, int32_t precision) +void _tanrat(PRAT* px, uint32_t radix, int32_t precision) { - PRAT ptmp= nullptr; + PRAT ptmp = nullptr; - DUPRAT(ptmp,*px); + DUPRAT(ptmp, *px); _sinrat(px, precision); _cosrat(&ptmp, radix, precision); - if ( zerrat( ptmp ) ) - { + if (zerrat(ptmp)) + { destroyrat(ptmp); - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } divrat(px, ptmp, precision); destroyrat(ptmp); - } -void tanrat( PRAT *px, uint32_t radix, int32_t precision) +void tanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { scale2pi(px, radix, 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 ) + scalerat(pa, angletype, radix, precision); + switch (angletype) + { + case AngleType::Degrees: + if (rat_gt(*pa, rat_180, precision)) { - case ANGLE_DEG: - if ( rat_gt( *pa, rat_180, precision) ) - { subrat(pa, rat_180, precision); - } - divrat( pa, rat_180, precision); - mulrat( pa, pi, precision); - break; - case ANGLE_GRAD: - if ( rat_gt( *pa, rat_200, precision) ) - { - subrat(pa, rat_200, precision); - } - divrat( pa, rat_200, precision); - mulrat( pa, pi, precision); - break; } - _tanrat( pa, radix, precision); + divrat(pa, rat_180, precision); + mulrat(pa, pi, precision); + break; + case AngleType::Gradians: + if (rat_gt(*pa, rat_200, precision)) + { + subrat(pa, rat_200, precision); + } + divrat(pa, rat_200, precision); + mulrat(pa, pi, precision); + break; + } + _tanrat(pa, radix, precision); } - diff --git a/src/CalcManager/Ratpack/transh.cpp b/src/CalcManager/Ratpack/transh.cpp index 8a835341..8e033aa5 100644 --- a/src/CalcManager/Ratpack/transh.cpp +++ b/src/CalcManager/Ratpack/transh.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- @@ -14,25 +14,21 @@ // // //----------------------------------------------------------------------------- -#include "pch.h" #include "ratpak.h" - - bool IsValidForHypFunc(PRAT px, int32_t precision) { PRAT ptmp = nullptr; bool bRet = true; - DUPRAT(ptmp,rat_min_exp); + DUPRAT(ptmp, rat_min_exp); divrat(&ptmp, rat_ten, precision); - if ( rat_lt( px, ptmp, precision) ) + if (rat_lt(px, ptmp, precision)) { bRet = false; } - destroyrat( ptmp ); + destroyrat(ptmp); return bRet; - } //----------------------------------------------------------------------------- @@ -68,49 +64,49 @@ bool IsValidForHypFunc(PRAT px, int32_t precision) // //----------------------------------------------------------------------------- - -void _sinhrat( PRAT *px, int32_t precision) +void _sinhrat(PRAT* px, int32_t precision) { - if ( !IsValidForHypFunc(*px, precision)) - { + if (!IsValidForHypFunc(*px, precision)) + { // Don't attempt exp of anything large or small - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } CREATETAYLOR(); - DUPRAT(pret,*px); - DUPRAT(thisterm,pret); + DUPRAT(pret, *px); + DUPRAT(thisterm, pret); - DUPNUM(n2,num_one); + DUPNUM(n2, num_one); - do { - NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + do + { + NEXTTERM(xx, INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } -void sinhrat( PRAT *px, uint32_t radix, int32_t precision) +void sinhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - PRAT tmpx= nullptr; + PRAT tmpx = nullptr; - if ( rat_ge( *px, rat_one, precision) ) - { - DUPRAT(tmpx,*px); + if (rat_ge(*px, rat_one, precision)) + { + DUPRAT(tmpx, *px); exprat(px, radix, precision); tmpx->pp->sign *= -1; exprat(&tmpx, radix, precision); - subrat( px, tmpx, precision); - divrat( px, rat_two, precision); - destroyrat( tmpx ); - } + subrat(px, tmpx, precision); + divrat(px, rat_two, precision); + destroyrat(tmpx); + } else - { - _sinhrat( px, precision); - } + { + _sinhrat(px, precision); + } } //----------------------------------------------------------------------------- @@ -147,59 +143,59 @@ void sinhrat( PRAT *px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- - -void _coshrat( PRAT *px, uint32_t radix, int32_t precision) +void _coshrat(PRAT* px, uint32_t radix, int32_t precision) { - if ( !IsValidForHypFunc(*px, precision)) - { + if (!IsValidForHypFunc(*px, precision)) + { // Don't attempt exp of anything large or small - throw( CALC_E_DOMAIN ); - } + throw(CALC_E_DOMAIN); + } CREATETAYLOR(); - pret->pp=longtonum( 1L, radix); - pret->pq=longtonum( 1L, radix); + pret->pp = i32tonum(1L, radix); + pret->pq = i32tonum(1L, radix); - DUPRAT(thisterm,pret) + DUPRAT(thisterm, pret) - n2=longtonum(0L, radix); + n2 = i32tonum(0L, radix); - do { - NEXTTERM(xx,INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); - } while ( !SMALL_ENOUGH_RAT( thisterm, precision) ); + do + { + NEXTTERM(xx, INC(n2) DIVNUM(n2) INC(n2) DIVNUM(n2), precision); + } while (!SMALL_ENOUGH_RAT(thisterm, precision)); DESTROYTAYLOR(); } -void coshrat( PRAT *px, uint32_t radix, int32_t precision) +void coshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - PRAT tmpx= nullptr; + PRAT tmpx = nullptr; (*px)->pp->sign = 1; (*px)->pq->sign = 1; - if ( rat_ge( *px, rat_one, precision) ) - { - DUPRAT(tmpx,*px); + if (rat_ge(*px, rat_one, precision)) + { + DUPRAT(tmpx, *px); exprat(px, radix, precision); tmpx->pp->sign *= -1; exprat(&tmpx, radix, precision); - addrat( px, tmpx, precision); - divrat( px, rat_two, precision); - destroyrat( tmpx ); - } + addrat(px, tmpx, precision); + divrat(px, rat_two, precision); + destroyrat(tmpx); + } else - { - _coshrat( px, radix, precision); - } + { + _coshrat(px, radix, precision); + } // Since *px might be epsilon below 1 due to TRIMIT // we need this trick here. - if ( rat_lt(*px, rat_one, precision) ) - { - DUPRAT(*px,rat_one); - } + if (rat_lt(*px, rat_one, precision)) + { + DUPRAT(*px, rat_one); + } } //----------------------------------------------------------------------------- @@ -215,17 +211,16 @@ void coshrat( PRAT *px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void tanhrat( PRAT *px, uint32_t radix, int32_t precision) +void tanhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { - PRAT ptmp= nullptr; + PRAT ptmp = nullptr; - DUPRAT(ptmp,*px); + DUPRAT(ptmp, *px); sinhrat(px, radix, precision); coshrat(&ptmp, radix, precision); - mulnumx(&((*px)->pp),ptmp->pq); - mulnumx(&((*px)->pq),ptmp->pp); + mulnumx(&((*px)->pp), ptmp->pq); + mulnumx(&((*px)->pq), ptmp->pp); destroyrat(ptmp); - } diff --git a/src/CalcManager/UnitConverter.cpp b/src/CalcManager/UnitConverter.cpp index bb1df9d6..d760b611 100644 --- a/src/CalcManager/UnitConverter.cpp +++ b/src/CalcManager/UnitConverter.cpp @@ -1,29 +1,31 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +#include +#include +#include +#include // for std::sort #include "Command.h" #include "UnitConverter.h" +#include "NumberFormattingUtils.h" -using namespace concurrency; using namespace std; using namespace UnitConversionManager; +using namespace CalcManager::NumberFormattingUtils; -static constexpr uint32_t EXPECTEDSERIALIZEDTOKENCOUNT = 7; -static constexpr uint32_t EXPECTEDSERIALIZEDCONVERSIONDATATOKENCOUNT = 3; -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 quoteConversions; unordered_map unquoteConversions; @@ -32,8 +34,8 @@ unordered_map unquoteConversions; /// Constructor, sets up all the variables and requires a configLoader /// /// An instance of the IConverterDataLoader interface which we use to read in category/unit names and conversion data -UnitConverter::UnitConverter(_In_ const shared_ptr& dataLoader) : - UnitConverter::UnitConverter(dataLoader, nullptr) +UnitConverter::UnitConverter(_In_ const shared_ptr& dataLoader) + : UnitConverter::UnitConverter(dataLoader, nullptr) { } @@ -63,7 +65,8 @@ UnitConverter::UnitConverter(_In_ const shared_ptr& dataLo unquoteConversions[L"{sc}"] = L';'; unquoteConversions[L"{lb}"] = LEFTESCAPECHAR; unquoteConversions[L"{rb}"] = RIGHTESCAPECHAR; - Reset(); + ClearValues(); + ResetCategoriesAndRatios(); } void UnitConverter::Initialize() @@ -75,7 +78,7 @@ bool UnitConverter::CheckLoad() { if (m_categories.empty()) { - Reset(); + ResetCategoriesAndRatios(); } return !m_categories.empty(); } @@ -106,25 +109,10 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i { if (m_currentCategory.id != input.id) { - vector& unitVector = m_categoryToUnits[m_currentCategory]; - for (unsigned int i = 0; i < unitVector.size(); i++) + for (auto& unit : m_categoryToUnits[m_currentCategory.id]) { - if (unitVector[i].id == m_fromType.id) - { - unitVector[i].isConversionSource = true; - } - else - { - unitVector[i].isConversionSource = false; - } - if (unitVector[i].id == m_toType.id) - { - unitVector[i].isConversionTarget = true; - } - else - { - unitVector[i].isConversionTarget = false; - } + unit.isConversionSource = (unit.id == m_fromType.id); + unit.isConversionTarget = (unit.id == m_toType.id); } m_currentCategory = input; if (!m_currentCategory.supportsNegative && m_currentDisplay.front() == L'-') @@ -133,7 +121,7 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i } } - newUnitList = m_categoryToUnits[input]; + newUnitList = m_categoryToUnits[input.id]; } InitializeSelectedUnits(); @@ -156,15 +144,16 @@ Category UnitConverter::GetCurrentCategory() /// Unit struct we are converting to void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) { - if (CheckLoad()) + if (!CheckLoad()) { - m_fromType = fromType; - m_toType = toType; - Calculate(); - - UpdateCurrencySymbols(); - UpdateViewModel(); + return; } + + m_fromType = fromType; + m_toType = toType; + Calculate(); + + UpdateCurrencySymbols(); } /// @@ -181,239 +170,130 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType /// void UnitConverter::SwitchActive(const wstring& newValue) { - if (CheckLoad()) + if (!CheckLoad()) { - swap(m_fromType, m_toType); - swap(m_currentHasDecimal, m_returnHasDecimal); - m_returnDisplay = m_currentDisplay; - m_currentDisplay = newValue; - m_currentHasDecimal = (m_currentDisplay.find(L'.') != m_currentDisplay.npos); - m_switchedActive = true; + return; + } - if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr) - { - shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); - const pair currencyRatios = currencyDataLoader->GetCurrencyRatioEquality(m_fromType, m_toType); + swap(m_fromType, m_toType); + swap(m_currentHasDecimal, m_returnHasDecimal); + m_returnDisplay = m_currentDisplay; + m_currentDisplay = newValue; + m_currentHasDecimal = (m_currentDisplay.find(L'.') != wstring::npos); + m_switchedActive = true; - m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second); - } + if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr) + { + shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); + const pair currencyRatios = currencyDataLoader->GetCurrencyRatioEquality(m_fromType, m_toType); + + m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second); } } -wstring UnitConverter::CategoryToString(const Category& c, const wchar_t * delimiter) +wstring UnitConverter::CategoryToString(const Category& c, wstring_view delimiter) { - wstringstream out(wstringstream::out); - out << Quote(std::to_wstring(c.id)) << delimiter << Quote(std::to_wstring(c.supportsNegative)) << delimiter << Quote(c.name) << delimiter; - return out.str(); + return Quote(std::to_wstring(c.id)) + .append(delimiter) + .append(Quote(std::to_wstring(c.supportsNegative))) + .append(delimiter) + .append(Quote(c.name)) + .append(delimiter); } -vector UnitConverter::StringToVector(const wstring& w, const wchar_t * delimiter, bool addRemainder) +vector UnitConverter::StringToVector(wstring_view w, wstring_view delimiter, bool addRemainder) { size_t delimiterIndex = w.find(delimiter); size_t startIndex = 0; - vector serializedTokens = vector(); - while (delimiterIndex != w.npos) + vector serializedTokens; + while (delimiterIndex != wstring_view::npos) { - serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex)); - startIndex = delimiterIndex + (int)wcslen(delimiter); + serializedTokens.emplace_back(w.substr(startIndex, delimiterIndex - startIndex)); + startIndex = delimiterIndex + static_cast(delimiter.size()); delimiterIndex = w.find(delimiter, startIndex); } if (addRemainder) { delimiterIndex = w.size(); - serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex)); + serializedTokens.emplace_back(w.substr(startIndex, delimiterIndex - startIndex)); } return serializedTokens; } - -Category UnitConverter::StringToCategory(const wstring& w) +wstring UnitConverter::UnitToString(const Unit& u, wstring_view delimiter) { - vector tokenList = StringToVector(w, L";"); - assert(tokenList.size() == EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT); - Category serializedCategory; - serializedCategory.id = _wtoi(Unquote(tokenList[0]).c_str()); - serializedCategory.supportsNegative = (tokenList[1].compare(L"1") == 0); - serializedCategory.name = Unquote(tokenList[2]); - return serializedCategory; + return Quote(std::to_wstring(u.id)) + .append(delimiter) + .append(Quote(u.name)) + .append(delimiter) + .append(Quote(u.abbreviation)) + .append(delimiter) + .append(std::to_wstring(u.isConversionSource)) + .append(delimiter) + .append(std::to_wstring(u.isConversionTarget)) + .append(delimiter) + .append(std::to_wstring(u.isWhimsical)) + .append(delimiter); } -wstring UnitConverter::UnitToString(const Unit& u, const wchar_t * delimiter) -{ - wstringstream out(wstringstream::out); - out << Quote(std::to_wstring(u.id)) << delimiter << Quote(u.name) << delimiter << Quote(u.abbreviation) << delimiter << std::to_wstring(u.isConversionSource) << delimiter << std::to_wstring(u.isConversionTarget) << delimiter << std::to_wstring(u.isWhimsical) << delimiter; - return out.str(); -} - -Unit UnitConverter::StringToUnit(const wstring& w) +Unit UnitConverter::StringToUnit(wstring_view w) { vector tokenList = StringToVector(w, L";"); assert(tokenList.size() == EXPECTEDSERIALIZEDUNITTOKENCOUNT); Unit serializedUnit; - serializedUnit.id = _wtoi(Unquote(tokenList[0]).c_str()); + serializedUnit.id = wcstol(Unquote(tokenList[0]).c_str(), nullptr, 10); 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; } -ConversionData UnitConverter::StringToConversionData(const wstring& w) +Category UnitConverter::StringToCategory(wstring_view w) { vector tokenList = StringToVector(w, L";"); - assert(tokenList.size() == EXPECTEDSERIALIZEDCONVERSIONDATATOKENCOUNT); - ConversionData serializedConversionData; - serializedConversionData.ratio = stod(Unquote(tokenList[0]).c_str()); - serializedConversionData.offset = stod(Unquote(tokenList[1]).c_str()); - serializedConversionData.offsetFirst = (tokenList[2].compare(L"1") == 0); - return serializedConversionData; -} - -wstring UnitConverter::ConversionDataToString(ConversionData d, const wchar_t * delimiter) -{ - wstringstream out(wstringstream::out); - out.precision(32); - out << fixed << d.ratio; - wstring ratio = out.str(); - out.str(L""); - out << fixed << d.offset; - wstring offset = out.str(); - out.str(L""); - TrimString(ratio); - TrimString(offset); - out << Quote(ratio) << delimiter << Quote(offset) << delimiter << std::to_wstring(d.offsetFirst) << delimiter; - return out.str(); -} - -/// -/// Serializes the data in the converter and returns it as a string -/// -wstring UnitConverter::Serialize() -{ - if (CheckLoad()) - { - wstringstream out(wstringstream::out); - const wchar_t * delimiter = L";"; - - out << UnitToString(m_fromType, delimiter) << "|"; - out << UnitToString(m_toType, delimiter) << "|"; - out << CategoryToString(m_currentCategory, delimiter) << "|"; - out << std::to_wstring(m_currentHasDecimal) << delimiter << std::to_wstring(m_returnHasDecimal) << delimiter << std::to_wstring(m_switchedActive) << delimiter; - out << m_currentDisplay << delimiter << m_returnDisplay << delimiter << "|"; - wstringstream categoryString(wstringstream::out); - wstringstream categoryToUnitString(wstringstream::out); - wstringstream unitToUnitToDoubleString(wstringstream::out); - for (const Category& c : m_categories) - { - categoryString << CategoryToString(c, delimiter) << ","; - } - - for (const auto& cur : m_categoryToUnits) - { - categoryToUnitString << CategoryToString(cur.first, delimiter) << "["; - for (const Unit& u : cur.second) - { - categoryToUnitString << UnitToString(u, delimiter) << ","; - } - categoryToUnitString << "[" << "]"; - } - - for (const auto& cur : m_ratioMap) - { - unitToUnitToDoubleString << UnitToString(cur.first, delimiter) << "["; - for (const auto& curConversion : cur.second) - { - unitToUnitToDoubleString << UnitToString(curConversion.first, delimiter) << ":"; - unitToUnitToDoubleString << ConversionDataToString(curConversion.second, delimiter) << ":,"; - } - unitToUnitToDoubleString << "[" << "]"; - } - - out << categoryString.str() << "|"; - out << categoryToUnitString.str() << "|"; - out << unitToUnitToDoubleString.str() << "|"; - wstring test = out.str(); - return test; - } - else - { - return wstring(); - } + assert(tokenList.size() == EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT); + Category serializedCategory; + serializedCategory.id = wcstol(Unquote(tokenList[0]).c_str(), nullptr, 10); + serializedCategory.supportsNegative = (tokenList[1] == L"1"); + serializedCategory.name = Unquote(tokenList[2]); + return serializedCategory; } /// /// De-Serializes the data in the converter from a string /// -/// wstring holding the serialized data. If it does not have expected number of parameters, we will ignore it -void UnitConverter::DeSerialize(const wstring& serializedData) +/// wstring_view holding the serialized data. If it does not have expected number of parameters, we will ignore it +void UnitConverter::RestoreUserPreferences(wstring_view userPreferences) { - Reset(); - if (!serializedData.empty()) + if (userPreferences.empty()) { - vector outerTokens = StringToVector(serializedData, L"|"); - assert(outerTokens.size() == EXPECTEDSERIALIZEDTOKENCOUNT); - m_fromType = StringToUnit(outerTokens[0]); - m_toType = StringToUnit(outerTokens[1]); - m_currentCategory = StringToCategory(outerTokens[2]); - vector stateDataTokens = StringToVector(outerTokens[3], L";"); - assert(stateDataTokens.size() == EXPECTEDSTATEDATATOKENCOUNT); - m_currentHasDecimal = (stateDataTokens[0].compare(L"1") == 0); - m_returnHasDecimal = (stateDataTokens[1].compare(L"1") == 0); - m_switchedActive = (stateDataTokens[2].compare(L"1") == 0); - m_currentDisplay = stateDataTokens[3]; - m_returnDisplay = stateDataTokens[4]; - vector categoryListTokens = StringToVector(outerTokens[4], L","); - for (wstring token : categoryListTokens) - { - m_categories.push_back(StringToCategory(token)); - } - vector unitVectorTokens = StringToVector(outerTokens[5], L"]"); - for (wstring unitVector : unitVectorTokens) - { - vector mapcomponents = StringToVector(unitVector, L"["); - assert(mapcomponents.size() == EXPECTEDMAPCOMPONENTTOKENCOUNT); - Category key = StringToCategory(mapcomponents[0]); - vector units = StringToVector(mapcomponents[1], L","); - for (wstring unit : units) - { - m_categoryToUnits[key].push_back(StringToUnit(unit)); - } - } - vector ratioMapTokens = StringToVector(outerTokens[6], L"]"); - for (wstring token : ratioMapTokens) - { - vector ratioMapComponentTokens = StringToVector(token, L"["); - assert(ratioMapComponentTokens.size() == EXPECTEDMAPCOMPONENTTOKENCOUNT); - Unit key = StringToUnit(ratioMapComponentTokens[0]); - vector ratioMapList = StringToVector(ratioMapComponentTokens[1], L","); - for (wstring subtoken : ratioMapList) - { - vector ratioMapSubComponentTokens = StringToVector(subtoken, L":"); - assert(ratioMapSubComponentTokens.size() == EXPECTEDMAPCOMPONENTTOKENCOUNT); - Unit subkey = StringToUnit(ratioMapSubComponentTokens[0]); - ConversionData conversion = StringToConversionData(ratioMapSubComponentTokens[1]); - m_ratioMap[key][subkey] = conversion; - } - } - UpdateViewModel(); + return; } -} -/// -/// De-Serializes the data in the converter from a string -/// -/// wstring holding the serialized data. If it does not have expected number of parameters, we will ignore it -void UnitConverter::RestoreUserPreferences(const wstring& userPreferences) -{ - if (!userPreferences.empty()) + vector outerTokens = StringToVector(userPreferences, L"|"); + if (outerTokens.size() != 3) { - vector outerTokens = StringToVector(userPreferences, L"|"); - if (outerTokens.size() == 3) + return; + } + + auto fromType = StringToUnit(outerTokens[0]); + auto toType = StringToUnit(outerTokens[1]); + 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.id); + if (itr != m_categoryToUnits.end()) + { + const auto& curUnits = itr->second; + if (find(curUnits.begin(), curUnits.end(), fromType) != curUnits.end()) { - m_fromType = StringToUnit(outerTokens[0]); - m_toType = StringToUnit(outerTokens[1]); - m_currentCategory = StringToCategory(outerTokens[2]); + m_fromType = fromType; + } + if (find(curUnits.begin(), curUnits.end(), toType) != curUnits.end()) + { + m_toType = toType; } } } @@ -423,58 +303,57 @@ void UnitConverter::RestoreUserPreferences(const wstring& userPreferences) /// wstring UnitConverter::SaveUserPreferences() { - wstringstream out(wstringstream::out); - const wchar_t * delimiter = L";"; - - out << UnitToString(m_fromType, delimiter) << "|"; - out << UnitToString(m_toType, delimiter) << "|"; - out << CategoryToString(m_currentCategory, delimiter) << "|"; - wstring test = out.str(); - return test; + constexpr auto delimiter = L";"sv; + constexpr auto pipe = L"|"sv; + return UnitToString(m_fromType, delimiter) + .append(pipe) + .append(UnitToString(m_toType, delimiter)) + .append(pipe) + .append(CategoryToString(m_currentCategory, delimiter)) + .append(pipe); } /// /// Sanitizes the input string, escape quoting any symbols we rely on for our delimiters, and returns the sanitized string. /// -/// wstring to be sanitized -wstring UnitConverter::Quote(const wstring& s) +/// wstring_view to be sanitized +wstring UnitConverter::Quote(wstring_view s) { - wstringstream quotedString(wstringstream::out); + wstring quotedString; // Iterate over the delimiter characters we need to quote - wstring::const_iterator cursor = s.begin(); - while(cursor != s.end()) + for (const auto ch : s) { - if (quoteConversions.find(*cursor) != quoteConversions.end()) + if (quoteConversions.find(ch) != quoteConversions.end()) { - quotedString << quoteConversions[*cursor]; + quotedString += quoteConversions[ch]; } else { - quotedString << *cursor; + quotedString += ch; } - ++cursor; } - return quotedString.str(); + + return quotedString; } /// /// Unsanitizes the sanitized input string, returning it to its original contents before we had quoted it. /// -/// wstring to be unsanitized -wstring UnitConverter::Unquote(const wstring& s) +/// wstring_view to be unsanitized +wstring UnitConverter::Unquote(wstring_view s) { - wstringstream quotedSubString(wstringstream::out); - wstringstream unquotedString(wstringstream::out); - wstring::const_iterator cursor = s.begin(); - while(cursor != s.end()) + wstring quotedSubString; + wstring unquotedString; + wstring_view::const_iterator cursor = s.begin(); + while (cursor != s.end()) { - if(*cursor == LEFTESCAPECHAR) + if (*cursor == LEFTESCAPECHAR) { - quotedSubString.str(L""); + quotedSubString.clear(); while (cursor != s.end() && *cursor != RIGHTESCAPECHAR) { - quotedSubString << *cursor; + quotedSubString += *cursor; ++cursor; } if (cursor == s.end()) @@ -484,17 +363,17 @@ wstring UnitConverter::Unquote(const wstring& s) } else { - quotedSubString << *cursor; - unquotedString << unquoteConversions[quotedSubString.str()]; + quotedSubString += *cursor; + unquotedString += unquoteConversions[quotedSubString]; } } else { - unquotedString << *cursor; + unquotedString += *cursor; } ++cursor; } - return unquotedString.str(); + return unquotedString; } /// @@ -503,144 +382,143 @@ wstring UnitConverter::Unquote(const wstring& s) /// Command enum representing the command that was entered void UnitConverter::SendCommand(Command command) { - if (CheckLoad()) + if (!CheckLoad()) { - // TODO: Localization of characters - bool clearFront = false; - if (m_currentDisplay == L"0") + return; + } + + // TODO: Localization of characters + bool clearFront = false; + bool clearBack = false; + if (command != Command::Negate && m_switchedActive) + { + ClearValues(); + m_switchedActive = false; + clearFront = true; + clearBack = false; + } + else + { + clearFront = (m_currentDisplay == L"0"); + clearBack = + ((m_currentHasDecimal && m_currentDisplay.size() - 1 >= MAXIMUMDIGITSALLOWED) + || (!m_currentHasDecimal && m_currentDisplay.size() >= MAXIMUMDIGITSALLOWED)); + } + + switch (command) + { + case Command::Zero: + m_currentDisplay += L'0'; + break; + + case Command::One: + m_currentDisplay += L'1'; + break; + + case Command::Two: + m_currentDisplay += L'2'; + break; + + case Command::Three: + m_currentDisplay += L'3'; + break; + + case Command::Four: + m_currentDisplay += L'4'; + break; + + case Command::Five: + m_currentDisplay += L'5'; + break; + + case Command::Six: + m_currentDisplay += L'6'; + break; + + case Command::Seven: + m_currentDisplay += L'7'; + break; + + case Command::Eight: + m_currentDisplay += L'8'; + break; + + case Command::Nine: + m_currentDisplay += L'9'; + break; + + case Command::Decimal: + clearFront = false; + clearBack = false; + if (!m_currentHasDecimal) { - clearFront = true; + m_currentDisplay += L'.'; + m_currentHasDecimal = true; } - bool clearBack = false; - if ((m_currentHasDecimal && m_currentDisplay.size() - 1 >= MAXIMUMDIGITSALLOWED) || (!m_currentHasDecimal && m_currentDisplay.size() >= MAXIMUMDIGITSALLOWED)) + break; + + case Command::Backspace: + clearFront = false; + clearBack = false; + if ((m_currentDisplay.front() != L'-' && m_currentDisplay.size() > 1) || m_currentDisplay.size() > 2) { - clearBack = true; - } - if (command != Command::Negate && m_switchedActive) - { - ClearValues(); - m_switchedActive = false; - clearFront = true; - clearBack = false; - } - switch (command) - { - case Command::Zero: - m_currentDisplay += L"0"; - break; - - case Command::One: - m_currentDisplay += L"1"; - break; - - case Command::Two: - m_currentDisplay += L"2"; - break; - - case Command::Three: - m_currentDisplay += L"3"; - break; - - case Command::Four: - m_currentDisplay += L"4"; - break; - - case Command::Five: - m_currentDisplay += L"5"; - break; - - case Command::Six: - m_currentDisplay += L"6"; - break; - - case Command::Seven: - m_currentDisplay += L"7"; - break; - - case Command::Eight: - m_currentDisplay += L"8"; - break; - - case Command::Nine: - m_currentDisplay += L"9"; - break; - - case Command::Decimal: - clearFront = false; - clearBack = false; - if (!m_currentHasDecimal) + if (m_currentDisplay.back() == L'.') { - m_currentDisplay += L"."; - m_currentHasDecimal = true; + m_currentHasDecimal = false; } - break; + m_currentDisplay.pop_back(); + } + else + { + m_currentDisplay = L"0"; + m_currentHasDecimal = false; + } + break; - case Command::Backspace: - clearFront = false; - clearBack = false; - if ((m_currentDisplay.front() != '-' && m_currentDisplay.size() > 1) || m_currentDisplay.size() > 2) + case Command::Negate: + clearFront = false; + clearBack = false; + if (m_currentCategory.supportsNegative) + { + if (m_currentDisplay.front() == L'-') { - if (m_currentDisplay.back() == '.') - { - m_currentHasDecimal = false; - } - m_currentDisplay.pop_back(); + m_currentDisplay.erase(0, 1); } else { - m_currentDisplay = L"0"; - m_currentHasDecimal = false; + m_currentDisplay.insert(0, 1, L'-'); } - break; - - case Command::Negate: - clearFront = false; - clearBack = false; - if (m_currentCategory.supportsNegative) - { - if (m_currentDisplay.front() == '-') - { - m_currentDisplay.erase(0, 1); - } - else - { - m_currentDisplay.insert(0, 1, '-'); - } - } - break; - - case Command::Clear: - clearFront = false; - clearBack = false; - ClearValues(); - break; - - case Command::Reset: - clearFront = false; - clearBack = false; - ClearValues(); - Reset(); - break; - - default: - break; } + break; + case Command::Clear: + clearFront = false; + clearBack = false; + ClearValues(); + break; - if (clearFront) - { - m_currentDisplay.erase(0, 1); - } - if (clearBack) - { - m_currentDisplay.erase(m_currentDisplay.size() - 1, 1); - m_vmCallback->MaxDigitsReached(); - } + case Command::Reset: + clearFront = false; + clearBack = false; + ClearValues(); + ResetCategoriesAndRatios(); + break; - Calculate(); - - UpdateViewModel(); + default: + break; } + + if (clearFront) + { + m_currentDisplay.erase(0, 1); + } + if (clearBack) + { + m_currentDisplay.pop_back(); + m_vmCallback->MaxDigitsReached(); + } + + Calculate(); } /// @@ -667,29 +545,31 @@ void UnitConverter::SetViewModelCurrencyCallback(_In_ const shared_ptr> UnitConverter::RefreshCurrencyRatios() +future> UnitConverter::RefreshCurrencyRatios() { shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); - return create_task([this, currencyDataLoader]() + future loadDataResult; + if (currencyDataLoader != nullptr) { - if (currencyDataLoader != nullptr) - { - return currencyDataLoader->TryLoadDataFromWebOverrideAsync(); - } - else - { - return task_from_result(false); - } - }).then([this, currencyDataLoader](bool didLoad) + loadDataResult = currencyDataLoader->TryLoadDataFromWebOverrideAsync(); + } + else { - wstring timestamp = L""; + loadDataResult = async([] { return false; }); + } + + shared_future sharedLoadResult = loadDataResult.share(); + return async([currencyDataLoader, sharedLoadResult]() { + sharedLoadResult.wait(); + bool didLoad = sharedLoadResult.get(); + wstring timestamp; if (currencyDataLoader != nullptr) { timestamp = currencyDataLoader->GetCurrencyTimestamp(); } return make_pair(didLoad, timestamp); - }, task_continuation_context::use_default()); + }); } shared_ptr UnitConverter::GetCurrencyConverterDataLoader() @@ -698,11 +578,11 @@ shared_ptr UnitConverter::GetCurrencyConverterData } /// -/// Converts a double value into another unit type, currently by multiplying by the given double ratio +/// Converts a double value into another unit type /// /// double input value to convert -/// double conversion ratio to use -double UnitConverter::Convert(double value, ConversionData conversionData) +/// offset and ratio to use +double UnitConverter::Convert(double value, const ConversionData& conversionData) { if (conversionData.offsetFirst) { @@ -738,17 +618,15 @@ vector> 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); } } // Sort the resulting list by absolute magnitude, breaking ties by choosing the positive value - sort(intermediateVector.begin(), intermediateVector.end(), [] - (SuggestedValueIntermediate first, SuggestedValueIntermediate second) - { + sort(intermediateVector.begin(), intermediateVector.end(), [](SuggestedValueIntermediate first, SuggestedValueIntermediate second) { if (abs(first.magnitude) == abs(second.magnitude)) { return first.magnitude > second.magnitude; @@ -765,28 +643,26 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificant(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2U); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificant(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1U); } else { - roundedString = RoundSignificant(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0U); } if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative) { - TrimString(roundedString); - returnVector.push_back(make_tuple(roundedString, entry.type)); + TrimTrailingZeros(roundedString); + returnVector.emplace_back(roundedString, entry.type); } } // The Whimsicals are determined differently // Sort the resulting list by absolute magnitude, breaking ties by choosing the positive value - sort(intermediateWhimsicalVector.begin(), intermediateWhimsicalVector.end(), [] - (SuggestedValueIntermediate first, SuggestedValueIntermediate second) - { + sort(intermediateWhimsicalVector.begin(), intermediateWhimsicalVector.end(), [](SuggestedValueIntermediate first, SuggestedValueIntermediate second) { if (abs(first.magnitude) == abs(second.magnitude)) { return first.magnitude > second.magnitude; @@ -805,86 +681,82 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificant(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2U); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificant(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1U); } else { - roundedString = RoundSignificant(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0U); } // How to work out which is the best whimsical value to add to the vector? if (stod(roundedString) != 0.0) { - TrimString(roundedString); - whimsicalReturnVector.push_back(make_tuple(roundedString, entry.type)); + TrimTrailingZeros(roundedString); + whimsicalReturnVector.emplace_back(roundedString, entry.type); } } // Pickup the 'best' whimsical value - currently the first one - if (whimsicalReturnVector.size() != 0) + if (!whimsicalReturnVector.empty()) { - returnVector.push_back(whimsicalReturnVector.at(0)); + returnVector.push_back(whimsicalReturnVector.front()); } - // - return returnVector; } /// -/// Resets the converter to its initial state +/// Resets categories and ratios /// -void UnitConverter::Reset() +void UnitConverter::ResetCategoriesAndRatios() { - m_categories = m_dataLoader->LoadOrderedCategories(); - - ClearValues(); m_switchedActive = false; - - if (!m_categories.empty()) + m_categories = m_dataLoader->GetOrderedCategories(); + if (m_categories.empty()) { - m_currentCategory = m_categories[0]; + return; + } - m_categoryToUnits.clear(); - m_ratioMap.clear(); - bool readyCategoryFound = false; - for (const Category& category : m_categories) + m_currentCategory = m_categories[0]; + + m_categoryToUnits.clear(); + m_ratioMap.clear(); + bool readyCategoryFound = false; + for (const Category& category : m_categories) + { + shared_ptr activeDataLoader = GetDataLoaderForCategory(category); + if (activeDataLoader == nullptr) { - shared_ptr activeDataLoader = GetDataLoaderForCategory(category); - if (activeDataLoader == nullptr) - { - // The data loader is different depending on the category, e.g. currency data loader - // is different from the static data loader. - // If there is no data loader for this category, continue. - continue; - } - - vector units = activeDataLoader->LoadOrderedUnits(category); - m_categoryToUnits[category] = 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. - if (!units.empty()) - { - for (Unit u : units) - { - m_ratioMap[u] = activeDataLoader->LoadOrderedRatios(u); - } - - if (!readyCategoryFound) - { - m_currentCategory = category; - readyCategoryFound = true; - } - } + // The data loader is different depending on the category, e.g. currency data loader + // is different from the static data loader. + // If there is no data loader for this category, continue. + continue; } - InitializeSelectedUnits(); - Calculate(); + vector 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. + if (!units.empty()) + { + for (const Unit& u : units) + { + m_ratioMap[u] = activeDataLoader->LoadOrderedRatios(u); + } + + if (!readyCategoryFound) + { + m_currentCategory = category; + readyCategoryFound = true; + } + } } + + InitializeSelectedUnits(); } /// @@ -910,32 +782,41 @@ shared_ptr UnitConverter::GetDataLoaderForCategory(const C /// void UnitConverter::InitializeSelectedUnits() { - if (m_categoryToUnits.empty()) { return; } - auto itr = m_categoryToUnits.find(m_currentCategory); + auto itr = m_categoryToUnits.find(m_currentCategory.id); if (itr == m_categoryToUnits.end()) { return; } - vector curUnits = itr->second; + const vector& curUnits = itr->second; if (!curUnits.empty()) { + // Units may already have been initialized through UnitConverter::RestoreUserPreferences(). + // Check if they have been, and if so, do not override restored units. + 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) + { + return; + } + bool conversionSourceSet = false; bool conversionTargetSet = false; for (const Unit& cur : curUnits) { - if (!conversionSourceSet && cur.isConversionSource) + if (!conversionSourceSet && cur.isConversionSource && !isFromUnitValid) { m_fromType = cur; conversionSourceSet = true; } - if (!conversionTargetSet && cur.isConversionTarget) + if (!conversionTargetSet && cur.isConversionTarget && !isToUnitValid) { m_toType = cur; conversionTargetSet = true; @@ -975,90 +856,64 @@ bool UnitConverter::AnyUnitIsEmpty() /// void UnitConverter::Calculate() { + if (AnyUnitIsEmpty()) + { + m_returnDisplay = m_currentDisplay; + m_returnHasDecimal = m_currentHasDecimal; + TrimTrailingZeros(m_returnDisplay); + UpdateViewModel(); + return; + } + unordered_map conversionTable = m_ratioMap[m_fromType]; - double returnValue = stod(m_currentDisplay); if (AnyUnitIsEmpty() || (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0)) { m_returnDisplay = m_currentDisplay; - TrimString(m_returnDisplay); + m_returnHasDecimal = m_currentHasDecimal; + TrimTrailingZeros(m_returnDisplay); } else { - returnValue = Convert(returnValue, conversionTable[m_toType]); - m_returnDisplay = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED); - TrimString(m_returnDisplay); - int numPreDecimal = (int)m_returnDisplay.size(); - if (m_returnDisplay.find(L'.') != m_returnDisplay.npos) - { - numPreDecimal = (int)m_returnDisplay.find(L'.'); - } - if (returnValue < 0) - { - numPreDecimal--; - } + double currentValue = stod(m_currentDisplay); + const double returnValue = Convert(currentValue, conversionTable[m_toType]); - if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) + const auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory); + if (isCurrencyConverter) { - wstringstream out(wstringstream::out); - out << scientific << returnValue; - m_returnDisplay = out.str(); + // We don't need to trim the value when it's a currency. + m_returnDisplay = RoundSignificantDigits(returnValue, MAXIMUMDIGITSALLOWED); + TrimTrailingZeros(m_returnDisplay); } else { - returnValue = stod(m_returnDisplay); - wstring returnString; - if (m_currentDisplay.size() <= OPTIMALDIGITSALLOWED && abs(returnValue) >= OPTIMALDECIMALALLOWED) + const unsigned int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue); + if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) { - returnString = RoundSignificant(returnValue, OPTIMALDIGITSALLOWED - min(numPreDecimal, OPTIMALDIGITSALLOWED)); + m_returnDisplay = ToScientificNumber(returnValue); } else { - returnString = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED - min(numPreDecimal, MAXIMUMDIGITSALLOWED)); + const unsigned int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay); + unsigned int precision; + if (abs(returnValue) < OPTIMALDECIMALALLOWED) + { + precision = MAXIMUMDIGITSALLOWED; + } + else + { + // 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. + auto numberDigits = max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits)); + precision = numberDigits > numPreDecimal ? numberDigits - numPreDecimal : 0; + } + + m_returnDisplay = RoundSignificantDigits(returnValue, precision); + TrimTrailingZeros(m_returnDisplay); } - m_returnDisplay = returnString; - TrimString(m_returnDisplay); + m_returnHasDecimal = (m_returnDisplay.find(L'.') != wstring::npos); } } - - m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos); -} - -/// -/// Trims out any trailing zeros or decimals in the given input string -/// -/// wstring to trim -void UnitConverter::TrimString(wstring& returnString) -{ - if (returnString.find(L'.') != m_returnDisplay.npos) - { - wstring::iterator iter; - for (iter = returnString.end() - 1; ;iter--) - { - if (*iter != L'0') - { - returnString.erase(iter + 1, returnString.end()); - break; - } - } - if (*(returnString.end()-1) == L'.') - { - returnString.erase(returnString.end()-1, returnString.end()); - } - } -} - -/// -/// Rounds the given double to the given number of significant digits -/// -/// input double -/// int number of significant digits to round to -wstring UnitConverter::RoundSignificant(double num, int numSignificant) -{ - wstringstream out(wstringstream::out); - out << fixed; - out.precision(numSignificant); - out << num; - return out.str(); + UpdateViewModel(); } void UnitConverter::UpdateCurrencySymbols() diff --git a/src/CalcManager/UnitConverter.h b/src/CalcManager/UnitConverter.h index 4e130d12..81a2629f 100644 --- a/src/CalcManager/UnitConverter.h +++ b/src/CalcManager/UnitConverter.h @@ -1,32 +1,58 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once +#include +#include +#include +#include "sal_cross_platform.h" // for SAL +#include // for std::shared_ptr + namespace UnitConversionManager { enum class Command; struct Unit { - Unit(){} - Unit(int id, std::wstring name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical) - : id(id), name(name), accessibleName(name), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(isWhimsical) + Unit() + { + } + Unit(int id, std::wstring_view name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical) + : id(id) + , name(name) + , accessibleName(name) + , abbreviation(std::move(abbreviation)) + , isConversionSource(isConversionSource) + , isConversionTarget(isConversionTarget) + , isWhimsical(isWhimsical) { } - Unit(int id, std::wstring currencyName, std::wstring countryName, std::wstring abbreviation, bool isRtlLanguage, bool isConversionSource, bool isConversionTarget) - : id(id), abbreviation(abbreviation), isConversionSource(isConversionSource), isConversionTarget(isConversionTarget), isWhimsical(false) + Unit( + int id, + std::wstring_view currencyName, + std::wstring_view countryName, + std::wstring abbreviation, + bool isRtlLanguage, + bool isConversionSource, + bool isConversionTarget) + : id(id) + , 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); } - virtual ~Unit() {} - int id; std::wstring name; std::wstring accessibleName; @@ -35,12 +61,12 @@ namespace UnitConversionManager bool isConversionTarget; bool isWhimsical; - bool operator!= (const Unit& that) const + bool operator!=(const Unit& that) const { return that.id != id; } - bool operator== (const Unit& that) const + bool operator==(const Unit& that) const { return that.id == id; } @@ -55,9 +81,14 @@ namespace UnitConversionManager struct Category { - Category(){} + Category() + { + } - Category(int id, std::wstring name, bool supportsNegative) : id(id), name(name), supportsNegative(supportsNegative) + Category(int id, std::wstring name, bool supportsNegative) + : id(id) + , name(std::move(name)) + , supportsNegative(supportsNegative) { } @@ -65,12 +96,12 @@ namespace UnitConversionManager std::wstring name; bool supportsNegative; - bool operator!= (const Category& that) const + bool operator!=(const Category& that) const { return that.id != id; } - bool operator== (const Category& that) const + bool operator==(const Category& that) const { return that.id == id; } @@ -79,15 +110,8 @@ namespace UnitConversionManager class UnitHash { public: - size_t operator() (const Unit & x) const { - return x.id; - } - }; - - class CategoryHash - { - public: - size_t operator() (const Category & x) const { + size_t operator()(const Unit& x) const + { return x.id; } }; @@ -101,12 +125,15 @@ namespace UnitConversionManager struct ConversionData { - ConversionData(){} - ConversionData(double ratio, double offset, bool offsetFirst) : ratio(ratio), offset(offset), offsetFirst(offsetFirst) + ConversionData() + { + } + ConversionData(double ratio, double offset, bool offsetFirst) + : ratio(ratio) + , offset(offset) + , offsetFirst(offsetFirst) { } - - virtual ~ConversionData() {} double ratio; double offset; @@ -130,13 +157,18 @@ namespace UnitConversionManager }; typedef std::tuple, UnitConversionManager::Unit, UnitConversionManager::Unit> CategorySelectionInitializer; - typedef std::unordered_map, UnitConversionManager::UnitHash> UnitToUnitToConversionDataMap; - typedef std::unordered_map, UnitConversionManager::CategoryHash> CategoryToUnitVectorMap; + typedef std::unordered_map< + UnitConversionManager::Unit, + std::unordered_map, + UnitConversionManager::UnitHash> + UnitToUnitToConversionDataMap; + typedef std::unordered_map> + CategoryToUnitVectorMap; class IViewModelCurrencyCallback { public: - virtual ~IViewModelCurrencyCallback() { }; + virtual ~IViewModelCurrencyCallback(){}; virtual void CurrencyDataLoadFinished(bool didLoad) = 0; virtual void CurrencySymbolsCallback(_In_ const std::wstring& fromSymbol, _In_ const std::wstring& toSymbol) = 0; virtual void CurrencyRatiosCallback(_In_ const std::wstring& ratioEquality, _In_ const std::wstring& accRatioEquality) = 0; @@ -147,10 +179,10 @@ namespace UnitConversionManager class IConverterDataLoader { public: - virtual ~IConverterDataLoader() { }; - virtual void LoadData() = 0; // prepare data if necessary before calling other functions - virtual std::vector LoadOrderedCategories() = 0; - virtual std::vector LoadOrderedUnits(const Category& c) = 0; + virtual ~IConverterDataLoader(){}; + virtual void LoadData() = 0; // prepare data if necessary before calling other functions + virtual std::vector GetOrderedCategories() = 0; + virtual std::vector GetOrderedUnits(const Category& c) = 0; virtual std::unordered_map LoadOrderedRatios(const Unit& u) = 0; virtual bool SupportsCategory(const Category& target) = 0; }; @@ -159,19 +191,21 @@ namespace UnitConversionManager { public: virtual void SetViewModelCallback(const std::shared_ptr& callback) = 0; - virtual std::pair GetCurrencySymbols(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0; - virtual std::pair GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0; + virtual std::pair + GetCurrencySymbols(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0; + virtual std::pair + GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) = 0; virtual std::wstring GetCurrencyTimestamp() = 0; - virtual concurrency::task TryLoadDataFromCacheAsync() = 0; - virtual concurrency::task TryLoadDataFromWebAsync() = 0; - virtual concurrency::task TryLoadDataFromWebOverrideAsync() = 0; + virtual std::future TryLoadDataFromCacheAsync() = 0; + virtual std::future TryLoadDataFromWebAsync() = 0; + virtual std::future TryLoadDataFromWebOverrideAsync() = 0; }; class IUnitConverterVMCallback { public: - virtual ~IUnitConverterVMCallback() { }; + virtual ~IUnitConverterVMCallback(){}; virtual void DisplayCallback(const std::wstring& from, const std::wstring& to) = 0; virtual void SuggestedValueCallback(const std::vector>& suggestedValues) = 0; virtual void MaxDigitsReached() = 0; @@ -180,21 +214,23 @@ namespace UnitConversionManager class IUnitConverter { public: - virtual ~IUnitConverter() { } - virtual void Initialize() = 0; // Use to initialize first time, use deserialize instead to rehydrate + virtual ~IUnitConverter() + { + } + virtual void Initialize() = 0; // Use to initialize first time, use deserialize instead to rehydrate virtual std::vector GetCategories() = 0; virtual CategorySelectionInitializer SetCurrentCategory(const Category& input) = 0; virtual Category GetCurrentCategory() = 0; virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0; virtual void SwitchActive(const std::wstring& newValue) = 0; - virtual std::wstring Serialize() = 0; - virtual void DeSerialize(const std::wstring& serializedData) = 0; virtual std::wstring SaveUserPreferences() = 0; - virtual void RestoreUserPreferences(_In_ const std::wstring& userPreferences) = 0; + virtual void RestoreUserPreferences(_In_ std::wstring_view userPreferences) = 0; virtual void SendCommand(Command command) = 0; virtual void SetViewModelCallback(_In_ const std::shared_ptr& newCallback) = 0; virtual void SetViewModelCurrencyCallback(_In_ const std::shared_ptr& newCallback) = 0; - virtual concurrency::task> RefreshCurrencyRatios() = 0; + virtual std::future> RefreshCurrencyRatios() = 0; + virtual void Calculate() = 0; + virtual void ResetCategoriesAndRatios() = 0; }; class UnitConverter : public IUnitConverter, public std::enable_shared_from_this @@ -210,36 +246,30 @@ namespace UnitConversionManager Category GetCurrentCategory() override; void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override; void SwitchActive(const std::wstring& newValue) override; - std::wstring Serialize() override; - void DeSerialize(const std::wstring& serializedData) override; std::wstring SaveUserPreferences() override; - void RestoreUserPreferences(const std::wstring& userPreference) override; + void RestoreUserPreferences(std::wstring_view userPreference) override; void SendCommand(Command command) override; void SetViewModelCallback(_In_ const std::shared_ptr& newCallback) override; void SetViewModelCurrencyCallback(_In_ const std::shared_ptr& newCallback) override; - concurrency::task> RefreshCurrencyRatios() override; + std::future> RefreshCurrencyRatios() override; + void Calculate() override; + void ResetCategoriesAndRatios() override; // IUnitConverter - static std::vector StringToVector(const std::wstring& w, const wchar_t * delimiter, bool addRemainder = false); - static std::wstring Quote(const std::wstring& s); - static std::wstring Unquote(const std::wstring& s); + static std::vector StringToVector(std::wstring_view w, std::wstring_view delimiter, bool addRemainder = false); + static std::wstring Quote(std::wstring_view s); + static std::wstring Unquote(std::wstring_view s); private: bool CheckLoad(); - double Convert(double value, ConversionData conversionData); + double Convert(double value, const ConversionData& conversionData); std::vector> CalculateSuggested(); - void Reset(); void ClearValues(); - void Calculate(); - void TrimString(std::wstring& input); void InitializeSelectedUnits(); - std::wstring RoundSignificant(double num, int numSignificant); - Category StringToCategory(const std::wstring& w); - std::wstring CategoryToString(const Category& c, const wchar_t * delimiter); - std::wstring UnitToString(const Unit& u, const wchar_t * delimiter); - Unit StringToUnit(const std::wstring& w); - ConversionData StringToConversionData(const std::wstring& w); - std::wstring ConversionDataToString(ConversionData d, const wchar_t * delimiter); + Category StringToCategory(std::wstring_view w); + std::wstring CategoryToString(const Category& c, std::wstring_view delimiter); + std::wstring UnitToString(const Unit& u, std::wstring_view delimiter); + Unit StringToUnit(std::wstring_view w); void UpdateCurrencySymbols(); void UpdateViewModel(); bool AnyUnitIsEmpty(); diff --git a/src/CalcManager/pch.cpp b/src/CalcManager/pch.cpp index 1da170eb..8ca9987d 100644 --- a/src/CalcManager/pch.cpp +++ b/src/CalcManager/pch.cpp @@ -1,4 +1,6 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "pch.h" +// Intentionally do not include the pch.h here. For projects that don't +// use precompiled headers, including the header here would force unnecessary compilation. +// The pch will be included through forced include. diff --git a/src/CalcManager/pch.h b/src/CalcManager/pch.h index 4690713c..9c71ff3a 100644 --- a/src/CalcManager/pch.h +++ b/src/CalcManager/pch.h @@ -1,24 +1,26 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif +// The CalcManager project should be able to be compiled with or without a precompiled header +// in - order to support other toolchains besides MSVC. When adding new system headers, make sure +// that the relevant source file includes all headers it needs, but then also add the system headers +// here so that MSVC users see the performance benefit. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/src/CalcManager/sal_cross_platform.h b/src/CalcManager/sal_cross_platform.h new file mode 100644 index 00000000..416bf096 --- /dev/null +++ b/src/CalcManager/sal_cross_platform.h @@ -0,0 +1,19 @@ +#pragma once + +#if defined(_WIN32) && defined(_MSC_VER) + +#include + +#else + +// Empty macro definitions for source annotations + +#define _In_opt_ +#define _Out_opt_ +#define _In_ +#define _Out_ +#define _Inout_ +#define __in_opt +#define _Frees_ptr_opt_ + +#endif diff --git a/src/CalcManager/winerror_cross_platform.h b/src/CalcManager/winerror_cross_platform.h new file mode 100644 index 00000000..07b6cd3f --- /dev/null +++ b/src/CalcManager/winerror_cross_platform.h @@ -0,0 +1,25 @@ +#pragma once + +#if defined(_WIN32) && defined(_MSC_VER) + +#include + +#else + +#include "Ratpack/CalcErr.h" + +#define E_ACCESSDENIED 0x80070005 +#define E_FAIL 0x80004005 +#define E_INVALIDARG 0x80070057 +#define E_OUTOFMEMORY 0x8007000E +#define E_POINTER 0x80004003 +#define E_UNEXPECTED 0x8000FFFF +#define E_BOUNDS 0x8000000B +#define S_OK 0x0 +#define S_FALSE 0x1 + +#define SUCCEEDED(hr) (((ResultCode)(hr)) >= 0) +#define FAILED(hr) (((ResultCode)(hr)) < 0) +#define SCODE_CODE(sc) ((sc) & 0xFFFF) + +#endif diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index 4df5bac3..46855071 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -31,24 +31,23 @@ using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; +using namespace Windows::Foundation; +using namespace Concurrency; -namespace CalculatorApp::ViewModel::ApplicationViewModelProperties +namespace { - StringReference Mode(L"Mode"); - StringReference PreviousMode(L"PreviousMode"); - StringReference ClearMemoryVisibility(L"ClearMemoryVisibility"); - StringReference AppBarVisibility(L"AppBarVisibility"); - StringReference CategoryName(L"CategoryName"); - StringReference Categories(L"Categories"); + StringReference CategoriesPropertyName(L"Categories"); + StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility"); } -ApplicationViewModel::ApplicationViewModel() : - m_CalculatorViewModel(nullptr), - m_DateCalcViewModel(nullptr), - m_ConverterViewModel(nullptr), - m_PreviousMode(ViewMode::None), - m_mode(ViewMode::None), - m_categories(nullptr) +ApplicationViewModel::ApplicationViewModel() + : m_CalculatorViewModel(nullptr) + , m_DateCalcViewModel(nullptr) + , m_GraphingCalcViewModel(nullptr) + , m_ConverterViewModel(nullptr) + , m_PreviousMode(ViewMode::None) + , m_mode(ViewMode::None) + , m_categories(nullptr) { SetMenuCategories(); } @@ -59,23 +58,24 @@ void ApplicationViewModel::Mode::set(ViewMode value) { PreviousMode = m_mode; m_mode = value; + SetDisplayNormalAlwaysOnTopOption(); OnModeChanged(); - RaisePropertyChanged(ApplicationViewModelProperties::Mode); + RaisePropertyChanged(ModePropertyName); } } -void ApplicationViewModel::Categories::set(IObservableVector^ value) +void ApplicationViewModel::Categories::set(IObservableVector ^ value) { if (m_categories != value) { m_categories = value; - RaisePropertyChanged(ApplicationViewModelProperties::Categories); + RaisePropertyChanged(CategoriesPropertyName); } } void ApplicationViewModel::Initialize(ViewMode mode) { - if (!NavCategory::IsValidViewMode(mode)) + if (!NavCategory::IsValidViewMode(mode) || !NavCategory::IsViewModeEnabled(mode)) { mode = ViewMode::Standard; } @@ -86,7 +86,7 @@ void ApplicationViewModel::Initialize(ViewMode mode) } catch (const std::exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance()->LogStandardException(mode, __FUNCTIONW__, e); if (!TryRecoverFromNavigationModeFailure()) { // Could not navigate to standard mode either. @@ -94,9 +94,9 @@ void ApplicationViewModel::Initialize(ViewMode mode) throw; } } - catch (Exception^ e) + catch (Exception ^ e) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, e); + TraceLogger::GetInstance()->LogPlatformException(mode, __FUNCTIONW__, e); if (!TryRecoverFromNavigationModeFailure()) { // Could not navigate to standard mode either. @@ -125,19 +125,23 @@ bool ApplicationViewModel::TryRecoverFromNavigationModeFailure() void ApplicationViewModel::OnModeChanged() { assert(NavCategory::IsValidViewMode(m_mode)); - TraceLogger::GetInstance().LogModeChangeBegin(m_PreviousMode, m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (NavCategory::IsCalculatorViewMode(m_mode)) { - TraceLogger::GetInstance().LogCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_CalculatorViewModel) { m_CalculatorViewModel = ref new StandardCalculatorViewModel(); } m_CalculatorViewModel->SetCalculatorType(m_mode); } + else if (NavCategory::IsGraphingCalculatorViewMode(m_mode)) + { + if (!m_GraphingCalcViewModel) + { + m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel(); + } + } else if (NavCategory::IsDateCalculatorViewMode(m_mode)) { - TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_DateCalcViewModel) { m_DateCalcViewModel = ref new DateCalculatorViewModel(); @@ -145,7 +149,6 @@ void ApplicationViewModel::OnModeChanged() } else if (NavCategory::IsConverterViewMode(m_mode)) { - TraceLogger::GetInstance().LogConverterModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_ConverterViewModel) { auto dataLoader = make_shared(ref new GeographicRegion()); @@ -157,20 +160,27 @@ void ApplicationViewModel::OnModeChanged() } auto resProvider = AppResourceProvider::GetInstance(); - CategoryName = resProvider.GetResourceString(NavCategory::GetNameResourceKey(m_mode)); + CategoryName = resProvider->GetResourceString(NavCategory::GetNameResourceKey(m_mode)); - // This is the only place where a ViewMode enum should be cast to an int. - // + // Cast mode to an int in order to save it to app data. // Save the changed mode, so that the new window launches in this mode. // Don't save until after we have adjusted to the new mode, so we don't save a mode that fails to load. - ApplicationData::Current->LocalSettings->Values->Insert(ApplicationViewModelProperties::Mode, NavCategory::Serialize(m_mode)); + ApplicationData::Current->LocalSettings->Values->Insert(ModePropertyName, NavCategory::Serialize(m_mode)); - TraceLogger::GetInstance().LogModeChangeEnd(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); - RaisePropertyChanged(ApplicationViewModelProperties::ClearMemoryVisibility); - RaisePropertyChanged(ApplicationViewModelProperties::AppBarVisibility); + // Log ModeChange event when not first launch, log WindowCreated on first launch + if (NavCategory::IsValidViewMode(m_PreviousMode)) + { + TraceLogger::GetInstance()->LogModeChange(m_mode); + } + else + { + TraceLogger::GetInstance()->LogWindowCreated(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + } + + RaisePropertyChanged(ClearMemoryVisibilityPropertyName); } -void ApplicationViewModel::OnCopyCommand(Object^ parameter) +void ApplicationViewModel::OnCopyCommand(Object ^ parameter) { if (NavCategory::IsConverterViewMode(m_mode)) { @@ -180,19 +190,19 @@ void ApplicationViewModel::OnCopyCommand(Object^ parameter) { DateCalcViewModel->OnCopyCommand(parameter); } - else + else if (NavCategory::IsCalculatorViewMode(m_mode)) { CalculatorViewModel->OnCopyCommand(parameter); } } -void ApplicationViewModel::OnPasteCommand(Object^ parameter) +void ApplicationViewModel::OnPasteCommand(Object ^ parameter) { if (NavCategory::IsConverterViewMode(m_mode)) { ConverterViewModel->OnPasteCommand(parameter); } - else + else if (NavCategory::IsCalculatorViewMode(m_mode)) { CalculatorViewModel->OnPasteCommand(parameter); } @@ -205,3 +215,60 @@ void ApplicationViewModel::SetMenuCategories() // property setter logic. Categories = NavCategoryGroup::CreateMenuOptions(); } + +void ApplicationViewModel::ToggleAlwaysOnTop(float width, float height) +{ + HandleToggleAlwaysOnTop(width, height); +} + +#pragma optimize("", off) +task ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float height) +{ + if (ApplicationView::GetForCurrentView()->ViewMode == ApplicationViewMode::CompactOverlay) + { + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; + localSettings->Values->Insert(WidthLocalSettings, width); + localSettings->Values->Insert(HeightLocalSettings, height); + + bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::Default); + CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = success; + CalculatorViewModel->IsAlwaysOnTop = !success; + IsAlwaysOnTop = !success; + } + else + { + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; + ViewModePreferences ^ compactOptions = ViewModePreferences::CreateDefault(ApplicationViewMode::CompactOverlay); + if (!localSettings->Values->GetView()->HasKey(LaunchedLocalSettings)) + { + compactOptions->CustomSize = Size(320, 394); + localSettings->Values->Insert(LaunchedLocalSettings, true); + } + else + { + if (localSettings->Values->GetView()->HasKey(WidthLocalSettings) && localSettings->Values->GetView()->HasKey(HeightLocalSettings)) + { + float oldWidth = safe_cast(localSettings->Values->GetView()->Lookup(WidthLocalSettings))->GetSingle(); + float oldHeight = safe_cast(localSettings->Values->GetView()->Lookup(HeightLocalSettings))->GetSingle(); + compactOptions->CustomSize = Size(oldWidth, oldHeight); + } + else + { + compactOptions->CustomSize = Size(320, 394); + } + } + + bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::CompactOverlay, compactOptions); + CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = !success; + CalculatorViewModel->IsAlwaysOnTop = success; + IsAlwaysOnTop = success; + } + SetDisplayNormalAlwaysOnTopOption(); +}; +#pragma optimize("", on) + +void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption() +{ + DisplayNormalAlwaysOnTopOption = + m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop; +} diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index a8d459f7..7e4238d8 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -1,40 +1,35 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once #include "StandardCalculatorViewModel.h" #include "DateCalculatorViewModel.h" +#include "GraphingCalculator/GraphingCalculatorViewModel.h" #include "UnitConverterViewModel.h" namespace CalculatorApp { namespace ViewModel { - namespace ApplicationViewModelProperties - { - extern Platform::StringReference Mode; - extern Platform::StringReference PreviousMode; - extern Platform::StringReference ClearMemoryVisibility; - extern Platform::StringReference AppBarVisibility; - extern Platform::StringReference CategoryName; - extern Platform::StringReference Categories; - } - - [Windows::UI::Xaml::Data::Bindable] - public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: ApplicationViewModel(); - void Initialize(CalculatorApp::Common::ViewMode mode); // Use for first init, use deserialize for rehydration + void Initialize(CalculatorApp::Common::ViewMode mode); // Use for first init, use deserialize for rehydration OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel^, CalculatorViewModel); - OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel^, DateCalcViewModel); - OBSERVABLE_PROPERTY_RW(CalculatorApp::ViewModel::UnitConverterViewModel^, ConverterViewModel); + OBSERVABLE_PROPERTY_RW(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_RW(Platform::String^, CategoryName); + OBSERVABLE_PROPERTY_R(bool, IsAlwaysOnTop); + OBSERVABLE_NAMED_PROPERTY_RW(Platform::String ^, CategoryName); + + // Indicates whether calculator is currently in standard mode _and_ supports CompactOverlay _and_ is not in Always-on-Top mode + OBSERVABLE_PROPERTY_R(bool, DisplayNormalAlwaysOnTopOption); COMMAND_FOR_METHOD(CopyCommand, ApplicationViewModel::OnCopyCommand); COMMAND_FOR_METHOD(PasteCommand, ApplicationViewModel::OnPasteCommand); @@ -48,6 +43,13 @@ namespace CalculatorApp void set(CalculatorApp::Common::ViewMode value); } + static property Platform::String^ ModePropertyName + { + Platform::String^ get() + { + return Platform::StringReference(L"Mode"); + } + } property Windows::Foundation::Collections::IObservableVector^ Categories { @@ -63,34 +65,51 @@ namespace CalculatorApp { Windows::UI::Xaml::Visibility get() { - return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode) - ? Windows::UI::Xaml::Visibility::Visible - : Windows::UI::Xaml::Visibility::Collapsed; + return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode) ? Windows::UI::Xaml::Visibility::Visible + : Windows::UI::Xaml::Visibility::Collapsed; } } - property Windows::UI::Xaml::Visibility AppBarVisibility + static property Platform::String ^ LaunchedLocalSettings { - Windows::UI::Xaml::Visibility get() + Platform::String ^ get() { - return CalculatorApp::Common::NavCategory::IsCalculatorViewMode(Mode) - ? Windows::UI::Xaml::Visibility::Visible - : Windows::UI::Xaml::Visibility::Collapsed; + return Platform::StringReference(L"calculatorAlwaysOnTopLaunched"); } } + static property Platform::String ^ WidthLocalSettings + { + Platform::String ^ get() + { + return Platform::StringReference(L"calculatorAlwaysOnTopLastWidth"); + } + } + + static property Platform::String ^ HeightLocalSettings + { + Platform::String ^ get() + { + return Platform::StringReference(L"calculatorAlwaysOnTopLastHeight"); + } + } + + void ToggleAlwaysOnTop(float width, float height); + private: bool TryRecoverFromNavigationModeFailure(); void OnModeChanged(); - void OnCopyCommand(Platform::Object^ parameter); - void OnPasteCommand(Platform::Object^ parameter); + void OnCopyCommand(Platform::Object ^ parameter); + void OnPasteCommand(Platform::Object ^ parameter); void SetMenuCategories(); CalculatorApp::Common::ViewMode m_mode; - Windows::Foundation::Collections::IObservableVector^ m_categories; + Windows::Foundation::Collections::IObservableVector ^ m_categories; + Concurrency::task HandleToggleAlwaysOnTop(float width, float height); + void SetDisplayNormalAlwaysOnTopOption(); }; } } diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index e2e98ab9..c2a69b5c 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -42,7 +42,7 @@ 14.0 true Windows Store - 10.0.17763.0 + 10.0.18362.0 10.0.17134.0 10.0 @@ -50,46 +50,46 @@ StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary true - v141 + v142 StaticLibrary false true - v141 + v142 StaticLibrary false true - v141 + v142 StaticLibrary false true - v141 + v142 StaticLibrary false true - v141 + v142 @@ -122,29 +122,9 @@ - - false - - - false - - - false - - - false - - - false - - - false - - - false - - + false + true @@ -153,7 +133,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -173,7 +153,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -193,7 +173,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -213,7 +193,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -233,7 +213,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -253,7 +233,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -273,7 +253,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -293,7 +273,7 @@ true $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) 4453 - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) Level4 true @@ -308,24 +288,18 @@ - /DSEND_TELEMETRY %(AdditionalOptions) + /DSEND_DIAGNOSTICS %(AdditionalOptions) - - - - - - + - @@ -333,23 +307,26 @@ - - + - + + + + + @@ -357,26 +334,19 @@ - - - - - - - @@ -386,6 +356,9 @@ + + + @@ -401,25 +374,38 @@ - {311e866d-8b93-4609-a691-265941fee101} + + {e727a92b-f149-492c-8117-c039a298719b} + + + {fc81ff41-02cd-4cd9-9bc5-45a1e39ac6ed} + + + + /DUSE_MOCK_DATA %(AdditionalOptions) + + + + + + + + + + + + + + - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see https://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + \ No newline at end of file diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index d05aca0b..b219806e 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -10,6 +10,9 @@ {0184f727-b8aa-4af8-a699-63f1b56e7853} + + {cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c} + @@ -20,22 +23,15 @@ - Common - - Common - Common Common - - Common - Common @@ -51,9 +47,6 @@ Common - - Common - Common @@ -69,21 +62,9 @@ Common - - Common\Automation - - - Common\Automation - - - Common\Automation - Common\Automation - - Common\Automation - DataLoaders @@ -93,6 +74,18 @@ DataLoaders + + GraphingCalculator + + + GraphingCalculator + + + Common\Automation + + + GraphingCalculator + @@ -104,16 +97,9 @@ - - - Common - Common - - Common - Common @@ -123,18 +109,12 @@ Common - - Common - Common Common - - Common - Common @@ -147,9 +127,6 @@ Common - - Common - Common @@ -174,27 +151,9 @@ Common - - Common - - - Common\Automation - - - Common\Automation - - - Common\Automation - - - Common\Automation - Common\Automation - - Common\Automation - DataLoaders @@ -210,7 +169,34 @@ DataLoaders - + + DataLoaders + + + Common\Automation + + + Common + + + Common + + + GraphingCalculator + + + GraphingCalculator + + + Common + + + GraphingCalculator + + + GraphingCalculator + + Common @@ -218,6 +204,8 @@ DataLoaders - + + + \ No newline at end of file diff --git a/src/CalcViewModel/Common/AlwaysSelectedCollectionView.h b/src/CalcViewModel/Common/AlwaysSelectedCollectionView.h index 54c3b67c..57e1d74e 100644 --- a/src/CalcViewModel/Common/AlwaysSelectedCollectionView.h +++ b/src/CalcViewModel/Common/AlwaysSelectedCollectionView.h @@ -3,107 +3,104 @@ #pragma once -namespace CalculatorApp { namespace Common +namespace CalculatorApp { - ref class AlwaysSelectedCollectionView sealed: - public Windows::UI::Xaml::DependencyObject, - public Windows::UI::Xaml::Data::ICollectionView + namespace Common { - internal: - AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source): - m_currentPosition(-1) + ref class AlwaysSelectedCollectionView sealed : public Windows::UI::Xaml::DependencyObject, public Windows::UI::Xaml::Data::ICollectionView { - m_source = source; + internal : AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector ^ source) + : m_currentPosition(-1) + { + m_source = source; - Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast(source); - if (observable) - { - observable->VectorChanged += - ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged); - } - } - - private: - // ICollectionView - // Not implemented methods - virtual WF::IAsyncOperation^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious - { - throw ref new Platform::NotImplementedException(); - } - property Windows::Foundation::Collections::IObservableVector^ CollectionGroups - { - virtual Windows::Foundation::Collections::IObservableVector^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get - { - return ref new Platform::Collections::Vector(); - } - } - property bool HasMoreItems - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get - { - return false; - } - } - - // Implemented methods - virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo - { - if (item) - { - unsigned int newCurrentPosition = 0; - bool result = m_source->IndexOf(item, &newCurrentPosition); - if (result) + Windows::UI::Xaml::Interop::IBindableObservableVector ^ observable = + dynamic_cast(source); + if (observable) { - m_currentPosition = newCurrentPosition; - m_currentChanged(this, nullptr); - return true; + observable->VectorChanged += ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler( + this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged); } } - // The item is not in the collection - // We're going to schedule a call back later so we - // restore the selection to the way we wanted it to begin with - if (m_currentPosition >= 0 && m_currentPosition < static_cast(m_source->Size)) + private: + // ICollectionView + // Not implemented methods + virtual WF::IAsyncOperation< + Windows::UI::Xaml::Data::LoadMoreItemsResult> ^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync { - this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, - ref new Windows::UI::Core::DispatchedHandler( - [this]() - { - m_currentChanged(this, nullptr); - })); + throw ref new Platform::NotImplementedException(); } - return false; - } - - virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition - { - if (index < 0 || index >= static_cast(m_source->Size)) + virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast + { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext + { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious + { + throw ref new Platform::NotImplementedException(); + } + property Windows::Foundation::Collections::IObservableVector ^ CollectionGroups { + virtual Windows::Foundation::Collections::IObservableVector< + Platform::Object ^> ^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get + { + return ref new Platform::Collections::Vector(); + } + } property bool HasMoreItems + { + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get + { + return false; + } + } + + // Implemented methods + virtual bool MoveCurrentTo(Platform::Object ^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo + { + if (item) + { + unsigned int newCurrentPosition = 0; + bool result = m_source->IndexOf(item, &newCurrentPosition); + if (result) + { + m_currentPosition = newCurrentPosition; + m_currentChanged(this, nullptr); + return true; + } + } + + // The item is not in the collection + // We're going to schedule a call back later so we + // restore the selection to the way we wanted it to begin with + if (m_currentPosition >= 0 && m_currentPosition < static_cast(m_source->Size)) + { + this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]() { + m_currentChanged(this, nullptr); + })); + } return false; } - m_currentPosition = index; - m_currentChanged(this, nullptr); - return true; - } + virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition + { + if (index < 0 || index >= static_cast(m_source->Size)) + { + return false; + } - property Platform::Object^ CurrentItem + m_currentPosition = index; + m_currentChanged(this, nullptr); + return true; + } + + property Platform::Object^ CurrentItem { virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get { @@ -116,30 +113,30 @@ namespace CalculatorApp { namespace Common } property int CurrentPosition - { - virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get { - return m_currentPosition; + virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get + { + return m_currentPosition; + } } - } - property bool IsCurrentAfterLast - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get + property bool IsCurrentAfterLast { - return m_currentPosition >= static_cast(m_source->Size); + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get + { + return m_currentPosition >= static_cast(m_source->Size); + } } - } - property bool IsCurrentBeforeFirst - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get + property bool IsCurrentBeforeFirst { - return m_currentPosition < 0; + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get + { + return m_currentPosition < 0; + } } - } - event WF::EventHandler^ CurrentChanged + event WF::EventHandler^ CurrentChanged { virtual WF::EventRegistrationToken add(WF::EventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add { @@ -165,63 +162,67 @@ namespace CalculatorApp { namespace Common // IVector // Not implemented methods virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::Append - { - throw ref new Platform::NotImplementedException(); - } - virtual void Clear() = Windows::Foundation::Collections::IVector::Clear - { - throw ref new Platform::NotImplementedException(); - } - virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray^ /*items*/) = Windows::Foundation::Collections::IVector::GetMany - { - throw ref new Platform::NotImplementedException(); - } - virtual Windows::Foundation::Collections::IVectorView^ GetView() = Windows::Foundation::Collections::IVector::GetView - { - throw ref new Platform::NotImplementedException(); - } - virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::InsertAt - { - throw ref new Platform::NotImplementedException(); - } - virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector::RemoveAt - { - throw ref new Platform::NotImplementedException(); - } - virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector::RemoveAtEnd - { - throw ref new Platform::NotImplementedException(); - } - virtual void ReplaceAll(const Platform::Array^ /*items*/) = Windows::Foundation::Collections::IVector::ReplaceAll - { - throw ref new Platform::NotImplementedException(); - } - virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::SetAt - { - throw ref new Platform::NotImplementedException(); - } - - // Implemented methods - virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector::GetAt - { - return m_source->GetAt(index); - } - - virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector::IndexOf - { - return m_source->IndexOf(item, index); - } - - property unsigned int Size - { - virtual unsigned int get() = Windows::Foundation::Collections::IVector::Size::get { - return m_source->Size; + throw ref new Platform::NotImplementedException(); + } + virtual void Clear() = Windows::Foundation::Collections::IVector::Clear + { + throw ref new Platform::NotImplementedException(); + } + virtual unsigned int GetMany( + unsigned int /*startIndex*/, + Platform::WriteOnlyArray ^ /*items*/) = Windows::Foundation::Collections::IVector::GetMany + { + throw ref new Platform::NotImplementedException(); + } + virtual Windows::Foundation::Collections::IVectorView ^ GetView() = Windows::Foundation::Collections::IVector< + Platform::Object ^>::GetView + { + throw ref new Platform::NotImplementedException(); + } + virtual void InsertAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector::InsertAt + { + throw ref new Platform::NotImplementedException(); + } + virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector::RemoveAt + { + throw ref new Platform::NotImplementedException(); + } + virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector::RemoveAtEnd + { + throw ref new Platform::NotImplementedException(); + } + virtual void + ReplaceAll(const Platform::Array ^ /*items*/) = Windows::Foundation::Collections::IVector::ReplaceAll + { + throw ref new Platform::NotImplementedException(); + } + virtual void SetAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector::SetAt + { + throw ref new Platform::NotImplementedException(); } - } - // IObservableVector - event Windows::Foundation::Collections::VectorChangedEventHandler^ VectorChanged + // Implemented methods + virtual Platform::Object ^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector::GetAt + { + return m_source->GetAt(index); + } + + virtual bool IndexOf(Platform::Object ^ item, unsigned int* index) = Windows::Foundation::Collections::IVector::IndexOf + { + return m_source->IndexOf(item, index); + } + + property unsigned int Size + { + virtual unsigned int get() = Windows::Foundation::Collections::IVector::Size::get + { + return m_source->Size; + } + } + + // IObservableVector + event Windows::Foundation::Collections::VectorChangedEventHandler^ VectorChanged { virtual WF::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler^ handler) = Windows::Foundation::Collections::IObservableVector::VectorChanged::add { @@ -236,52 +237,57 @@ namespace CalculatorApp { namespace Common // IIterable // Not implemented virtual Windows::Foundation::Collections::IIterator^ First() = Windows::Foundation::Collections::IIterable::First - { - throw ref new Platform::NotImplementedException(); - } - - // Event handlers - void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e) - { - Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast(e); - m_vectorChanged(this, args); - } - - Windows::UI::Xaml::Interop::IBindableVector^ m_source; - int m_currentPosition; - event WF::EventHandler^ m_currentChanged; - event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging; - event Windows::Foundation::Collections::VectorChangedEventHandler^ m_vectorChanged; - }; - - public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter - { - public: - AlwaysSelectedCollectionViewConverter() - { } - - private: - virtual Platform::Object^ Convert( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert - { - auto result = dynamic_cast(value); - if (result) { - return ref new AlwaysSelectedCollectionView(result); + throw ref new Platform::NotImplementedException(); } - return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert - } - virtual Platform::Object^ ConvertBack( - Platform::Object^ /*value*/, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + // Event handlers + void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector ^ source, Platform::Object ^ e) + { + Windows::Foundation::Collections::IVectorChangedEventArgs ^ args = safe_cast(e); + m_vectorChanged(this, args); + } + + Windows::UI::Xaml::Interop::IBindableVector ^ m_source; + int m_currentPosition; + event WF::EventHandler ^ m_currentChanged; + event Windows::UI::Xaml::Data::CurrentChangingEventHandler ^ m_currentChanging; + event Windows::Foundation::Collections::VectorChangedEventHandler ^ m_vectorChanged; + }; + + public + ref class AlwaysSelectedCollectionViewConverter sealed : public Windows::UI::Xaml::Data::IValueConverter { - return Windows::UI::Xaml::DependencyProperty::UnsetValue; - } - }; -}} + public: + AlwaysSelectedCollectionViewConverter() + { + } + + private: + virtual Platform::Object + ^ Convert( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert + { + auto result = dynamic_cast(value); + if (result) + { + return ref new AlwaysSelectedCollectionView(result); + } + return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert + } + + virtual Platform::Object + ^ ConvertBack( + Platform::Object ^ /*value*/, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + { + return Windows::UI::Xaml::DependencyProperty::UnsetValue; + } + }; + } +} diff --git a/src/CalcViewModel/Common/AppResourceProvider.cpp b/src/CalcViewModel/Common/AppResourceProvider.cpp index 45d3a31c..b843d55b 100644 --- a/src/CalcViewModel/Common/AppResourceProvider.cpp +++ b/src/CalcViewModel/Common/AppResourceProvider.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -14,18 +14,18 @@ AppResourceProvider::AppResourceProvider() m_cEngineStringResLoader = ResourceLoader::GetForViewIndependentUse(L"CEngineStrings"); } -AppResourceProvider & AppResourceProvider::GetInstance() +AppResourceProvider ^ AppResourceProvider::GetInstance() { - static AppResourceProvider s_instance; + static AppResourceProvider ^ s_instance = ref new AppResourceProvider(); return s_instance; } -String^ AppResourceProvider::GetResourceString(_In_ String^ key) +String ^ AppResourceProvider::GetResourceString(_In_ String ^ key) { return m_stringResLoader->GetString(key); } -String^ AppResourceProvider::GetCEngineString(_In_ String^ key) +String ^ AppResourceProvider::GetCEngineString(_In_ String ^ key) { return m_cEngineStringResLoader->GetString(key); } diff --git a/src/CalcViewModel/Common/AppResourceProvider.h b/src/CalcViewModel/Common/AppResourceProvider.h index 5807e82f..be429a13 100644 --- a/src/CalcViewModel/Common/AppResourceProvider.h +++ b/src/CalcViewModel/Common/AppResourceProvider.h @@ -1,20 +1,20 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once namespace CalculatorApp { - class AppResourceProvider + public ref class AppResourceProvider sealed { public: - static AppResourceProvider & GetInstance(); - Platform::String^ GetResourceString(_In_ Platform::String^ key); - Platform::String^ GetCEngineString(_In_ Platform::String^ key); + static AppResourceProvider ^ GetInstance(); + Platform::String ^ GetResourceString(_In_ Platform::String ^ key); + Platform::String ^ GetCEngineString(_In_ Platform::String ^ key); private: AppResourceProvider(); - Windows::ApplicationModel::Resources::ResourceLoader^ m_stringResLoader; - Windows::ApplicationModel::Resources::ResourceLoader^ m_cEngineStringResLoader; + Windows::ApplicationModel::Resources::ResourceLoader ^ m_stringResLoader; + Windows::ApplicationModel::Resources::ResourceLoader ^ m_cEngineStringResLoader; }; } diff --git a/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h b/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h index 40069a7c..9f947f41 100644 --- a/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h +++ b/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h @@ -11,16 +11,17 @@ namespace CalculatorApp::Common::Automation { - public interface class INarratorAnnouncementHost +public + interface class INarratorAnnouncementHost { public: // Is the host available on this OS. bool IsHostAvailable(); // Make a new instance of a concrete host. - INarratorAnnouncementHost^ MakeHost(); + INarratorAnnouncementHost ^ MakeHost(); // Make an announcement using the concrete host's preferred method. - void Announce(NarratorAnnouncement^ announcement); + void Announce(NarratorAnnouncement ^ announcement); }; } diff --git a/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp b/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp index 614f4595..0df3a96c 100644 --- a/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp +++ b/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp @@ -9,9 +9,10 @@ using namespace Windows::UI::Xaml::Automation; using namespace Windows::UI::Xaml::Automation::Peers; using namespace Windows::UI::Xaml::Controls; -LiveRegionHost::LiveRegionHost() : - m_host(nullptr) -{} +LiveRegionHost::LiveRegionHost() + : m_host(nullptr) +{ +} bool LiveRegionHost::IsHostAvailable() { @@ -19,12 +20,12 @@ bool LiveRegionHost::IsHostAvailable() return true; } -INarratorAnnouncementHost^ LiveRegionHost::MakeHost() +INarratorAnnouncementHost ^ LiveRegionHost::MakeHost() { return ref new LiveRegionHost(); } -void LiveRegionHost::Announce(NarratorAnnouncement^ announcement) +void LiveRegionHost::Announce(NarratorAnnouncement ^ announcement) { if (m_host == nullptr) { @@ -33,7 +34,7 @@ void LiveRegionHost::Announce(NarratorAnnouncement^ announcement) } AutomationProperties::SetName(m_host, announcement->Announcement); - AutomationPeer^ peer = FrameworkElementAutomationPeer::FromElement(m_host); + AutomationPeer ^ peer = FrameworkElementAutomationPeer::FromElement(m_host); if (peer != nullptr) { peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged); diff --git a/src/CalcViewModel/Common/Automation/LiveRegionHost.h b/src/CalcViewModel/Common/Automation/LiveRegionHost.h index c80d1477..fef7c714 100644 --- a/src/CalcViewModel/Common/Automation/LiveRegionHost.h +++ b/src/CalcViewModel/Common/Automation/LiveRegionHost.h @@ -16,17 +16,18 @@ namespace CalculatorApp::Common::Automation // 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 + ref class LiveRegionHost sealed : public INarratorAnnouncementHost { public: LiveRegionHost(); virtual bool IsHostAvailable(); - virtual INarratorAnnouncementHost^ MakeHost(); + virtual INarratorAnnouncementHost ^ MakeHost(); - virtual void Announce(NarratorAnnouncement^ announcement); + virtual void Announce(NarratorAnnouncement ^ announcement); private: - Windows::UI::Xaml::UIElement^ m_host; + Windows::UI::Xaml::UIElement ^ m_host; }; } diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp index 823572f3..5365937d 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp @@ -6,6 +6,7 @@ using namespace CalculatorApp::Common::Automation; using namespace Platform; +using namespace Windows::UI::Xaml::Automation::Peers; namespace CalculatorApp::Common::Automation { @@ -17,32 +18,39 @@ 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"); } } NarratorAnnouncement::NarratorAnnouncement( - String^ announcement, - String^ activityId, + String ^ announcement, + String ^ activityId, AutomationNotificationKind kind, AutomationNotificationProcessing processing) - : - m_announcement(announcement), - m_activityId(activityId), - m_kind(kind), - m_processing(processing) -{} + : m_announcement(announcement) + , m_activityId(activityId) + , m_kind(kind) + , m_processing(processing) +{ +} -String^ NarratorAnnouncement::Announcement::get() +String ^ NarratorAnnouncement::Announcement::get() { return m_announcement; } -String^ NarratorAnnouncement::ActivityId::get() +String ^ NarratorAnnouncement::ActivityId::get() { return m_activityId; } @@ -57,68 +65,57 @@ AutomationNotificationProcessing NarratorAnnouncement::Processing::get() return m_processing; } -bool NarratorAnnouncement::IsValid(NarratorAnnouncement^ announcement) +bool NarratorAnnouncement::IsValid(NarratorAnnouncement ^ announcement) { - return announcement != nullptr - && announcement->Announcement != nullptr - && !announcement->Announcement->IsEmpty(); + return announcement != nullptr && announcement->Announcement != nullptr && !announcement->Announcement->IsEmpty(); } -NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::DisplayUpdated, AutomationNotificationKind::Other, AutomationNotificationProcessing::ImportantMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::MaxDigitsReached, AutomationNotificationKind::Other, AutomationNotificationProcessing::ImportantMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetMemoryClearedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::MemoryCleared, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::ImportantMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::MemoryItemChanged, AutomationNotificationKind::ActionCompleted, AutomationNotificationProcessing::MostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::MemoryItemAdded, AutomationNotificationKind::ItemAdded, AutomationNotificationProcessing::MostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::HistoryCleared, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::MostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetHistorySlotClearedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( announcement, - CalculatorActivityIds::DisplayUpdated, - AutomationNotificationKind::Other, - AutomationNotificationProcessing::ImportantMostRecent); -} - -NarratorAnnouncement^ CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(String^ announcement) -{ - return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::MaxDigitsReached, - AutomationNotificationKind::Other, - AutomationNotificationProcessing::ImportantMostRecent); -} - -NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryClearedAnnouncement(String^ announcement) -{ - return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::MemoryCleared, + CalculatorActivityIds::HistorySlotCleared, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::ImportantMostRecent); } -NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(String^ announcement) -{ - return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::MemoryItemChanged, - AutomationNotificationKind::ActionCompleted, - AutomationNotificationProcessing::MostRecent); -} - -NarratorAnnouncement^ CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(String^ announcement) -{ - return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::MemoryItemAdded, - AutomationNotificationKind::ItemAdded, - AutomationNotificationProcessing::MostRecent); -} - -NarratorAnnouncement^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(String^ announcement) -{ - return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::HistoryCleared, - AutomationNotificationKind::ItemRemoved, - AutomationNotificationProcessing::MostRecent); -} - -NarratorAnnouncement^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( announcement, @@ -127,7 +124,7 @@ NarratorAnnouncement^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement AutomationNotificationProcessing::ImportantMostRecent); } -NarratorAnnouncement^ CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( announcement, @@ -136,16 +133,13 @@ NarratorAnnouncement^ CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement AutomationNotificationProcessing::ImportantMostRecent); } -NarratorAnnouncement^ CalculatorAnnouncement::GetDisplayCopiedAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetDisplayCopiedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( - announcement, - CalculatorActivityIds::DisplayCopied, - AutomationNotificationKind::ActionCompleted, - AutomationNotificationProcessing::ImportantMostRecent); + announcement, CalculatorActivityIds::DisplayCopied, AutomationNotificationKind::ActionCompleted, AutomationNotificationProcessing::ImportantMostRecent); } -NarratorAnnouncement^ CalculatorAnnouncement::GetOpenParenthesisCountChangedAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetOpenParenthesisCountChangedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( announcement, @@ -154,7 +148,7 @@ NarratorAnnouncement^ CalculatorAnnouncement::GetOpenParenthesisCountChangedAnno AutomationNotificationProcessing::ImportantMostRecent); } -NarratorAnnouncement^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(String^ announcement) +NarratorAnnouncement ^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( announcement, @@ -162,3 +156,51 @@ NarratorAnnouncement^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnounce 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); +} diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h index 3ec6a5e3..a19de86f 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h @@ -5,54 +5,27 @@ namespace CalculatorApp::Common::Automation { - // These enum types are copied from the types available in - // Windows::UI::Xaml::Automation::Peers in the RS3 SDK. - // When this app switches to min version RS3, these custom - // enums should be removed and the Windows types should be used - // instead. - // TODO - MSFT 12735088 - public enum class AutomationNotificationKind - { - ItemAdded = 0, - ItemRemoved = 1, - ActionCompleted = 2, - ActionAborted = 3, - Other = 4 - }; - - public enum class AutomationNotificationProcessing - { - ImportantAll = 0, - ImportantMostRecent = 1, - All = 2, - MostRecent = 3, - CurrentThenMostRecent = 4 - }; - - public ref class NarratorAnnouncement sealed +public + ref class NarratorAnnouncement sealed { public: - property Platform::String^ Announcement + property Platform::String + ^ Announcement { Platform::String ^ get(); } + + property Platform::String + ^ ActivityId { Platform::String ^ get(); } + + property Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind Kind { - Platform::String^ get(); + Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind get(); } - property Platform::String^ ActivityId + property Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing Processing { - Platform::String^ get(); + Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing get(); } - property AutomationNotificationKind Kind - { - AutomationNotificationKind get(); - } - - property AutomationNotificationProcessing Processing - { - AutomationNotificationProcessing get(); - } - - static bool IsValid(NarratorAnnouncement^ announcement); + static bool IsValid(NarratorAnnouncement ^ announcement); private: // Make CalculatorAnnouncement a friend class so it is the only @@ -60,15 +33,15 @@ namespace CalculatorApp::Common::Automation friend class CalculatorAnnouncement; NarratorAnnouncement( - Platform::String^ announcement, - Platform::String^ activityId, - AutomationNotificationKind kind, - AutomationNotificationProcessing processing); + Platform::String ^ announcement, + Platform::String ^ activityId, + Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind kind, + Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing processing); - Platform::String^ m_announcement; - Platform::String^ m_activityId; - AutomationNotificationKind m_kind; - AutomationNotificationProcessing m_processing; + Platform::String ^ m_announcement; + Platform::String ^ m_activityId; + Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind m_kind; + Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing m_processing; }; // CalculatorAnnouncement is intended to contain only static methods @@ -76,22 +49,34 @@ namespace CalculatorApp::Common::Automation class CalculatorAnnouncement { public: - static NarratorAnnouncement^ GetDisplayUpdatedAnnouncement(Platform::String^ announcement); - static NarratorAnnouncement^ GetMaxDigitsReachedAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetDisplayUpdatedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetMaxDigitsReachedAnnouncement(Platform::String ^ announcement); - static NarratorAnnouncement^ GetMemoryClearedAnnouncement(Platform::String^ announcement); - static NarratorAnnouncement^ GetMemoryItemChangedAnnouncement(Platform::String^ announcement); - static NarratorAnnouncement^ GetMemoryItemAddedAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetMemoryClearedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetMemoryItemChangedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetMemoryItemAddedAnnouncement(Platform::String ^ announcement); - static NarratorAnnouncement^ GetHistoryClearedAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetHistoryClearedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetHistorySlotClearedAnnouncement(Platform::String ^ announcement); - static NarratorAnnouncement^ GetCategoryNameChangedAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetCategoryNameChangedAnnouncement(Platform::String ^ announcement); - static NarratorAnnouncement^ GetUpdateCurrencyRatesAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetUpdateCurrencyRatesAnnouncement(Platform::String ^ announcement); - static NarratorAnnouncement^ GetDisplayCopiedAnnouncement(Platform::String^ announcement); + static NarratorAnnouncement ^ GetDisplayCopiedAnnouncement(Platform::String ^ announcement); + + 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); - static NarratorAnnouncement^ GetOpenParenthesisCountChangedAnnouncement(Platform::String^ announcement); - static NarratorAnnouncement^ GetNoRightParenthesisAddedAnnouncement(Platform::String ^ announcement); }; } diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp index ed773149..a103c7e7 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp @@ -9,8 +9,8 @@ using namespace CalculatorApp::Common::Automation; using namespace std; -INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::s_hostProducer; -vector NarratorAnnouncementHostFactory::s_hosts; +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::s_hostProducer; +vector 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(); @@ -32,15 +32,12 @@ 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() - }; + NarratorAnnouncementHostFactory::s_hosts = { ref new NotificationHost(), ref new LiveRegionHost() }; } -INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::GetHostProducer() +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::GetHostProducer() { - for (INarratorAnnouncementHost^ host : NarratorAnnouncementHostFactory::s_hosts) + for (INarratorAnnouncementHost ^ host : NarratorAnnouncementHostFactory::s_hosts) { if (host->IsHostAvailable()) { @@ -52,7 +49,7 @@ INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::GetHostProducer() return nullptr; } -INarratorAnnouncementHost^ NarratorAnnouncementHostFactory::MakeHost() +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::MakeHost() { if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr) { diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h index c11a894e..4b739a79 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h @@ -14,18 +14,20 @@ namespace CalculatorApp::Common::Automation class NarratorAnnouncementHostFactory { public: - static INarratorAnnouncementHost^ MakeHost(); + static INarratorAnnouncementHost ^ MakeHost(); private: - NarratorAnnouncementHostFactory() {} + NarratorAnnouncementHostFactory() + { + } static int Initialize(); static void RegisterHosts(); - static INarratorAnnouncementHost^ GetHostProducer(); + static INarratorAnnouncementHost ^ GetHostProducer(); private: static int s_init; - static INarratorAnnouncementHost^ s_hostProducer; - static std::vector s_hosts; + static INarratorAnnouncementHost ^ s_hostProducer; + static std::vector s_hosts; }; } diff --git a/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp b/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp index 4dc48074..89fa0c0b 100644 --- a/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp +++ b/src/CalcViewModel/Common/Automation/NarratorNotifier.cpp @@ -1,11 +1,10 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // Implementation of the NarratorNotifier class. #include "pch.h" #include "NarratorNotifier.h" -#include "NarratorAnnouncementHostFactory.h" using namespace CalculatorApp::Common::Automation; using namespace Platform; @@ -13,38 +12,45 @@ using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Automation; using namespace Windows::UI::Xaml::Automation::Peers; -DependencyProperty^ NarratorNotifier::s_announcementProperty; +DependencyProperty ^ NarratorNotifier::s_announcementProperty; NarratorNotifier::NarratorNotifier() { - m_announcementHost = NarratorAnnouncementHostFactory::MakeHost(); } -void NarratorNotifier::Announce(NarratorAnnouncement^ announcement) +void NarratorNotifier::Announce(NarratorAnnouncement ^ announcement) { - if (NarratorAnnouncement::IsValid(announcement) - && m_announcementHost != nullptr) + if (NarratorAnnouncement::IsValid(announcement)) { - m_announcementHost->Announce(announcement); + if (m_announcementElement == nullptr) + { + m_announcementElement = ref new Windows::UI::Xaml::Controls::TextBlock(); + } + + auto peer = FrameworkElementAutomationPeer::FromElement(m_announcementElement); + if (peer != nullptr) + { + peer->RaiseNotificationEvent(announcement->Kind, announcement->Processing, announcement->Announcement, announcement->ActivityId); + } } } void NarratorNotifier::RegisterDependencyProperties() { s_announcementProperty = DependencyProperty::Register( - L"Announcement", // The name of the dependency property. + L"Announcement", // The name of the dependency property. NarratorAnnouncement::typeid, // The type of the dependency property. - NarratorNotifier::typeid, // The owner of the dependency property. + NarratorNotifier::typeid, // The owner of the dependency property. ref new PropertyMetadata( nullptr, // Default value of the dependency property. ref new PropertyChangedCallback(OnAnnouncementChanged))); } -void NarratorNotifier::OnAnnouncementChanged(_In_ DependencyObject^ dependencyObject, _In_ DependencyPropertyChangedEventArgs^ e) +void NarratorNotifier::OnAnnouncementChanged(_In_ DependencyObject ^ dependencyObject, _In_ DependencyPropertyChangedEventArgs ^ e) { - auto instance = safe_cast(dependencyObject); + auto instance = safe_cast(dependencyObject); if (instance != nullptr) { - instance->Announce(safe_cast(e->NewValue)); + instance->Announce(safe_cast(e->NewValue)); } } diff --git a/src/CalcViewModel/Common/Automation/NarratorNotifier.h b/src/CalcViewModel/Common/Automation/NarratorNotifier.h index c12e2277..359bb09e 100644 --- a/src/CalcViewModel/Common/Automation/NarratorNotifier.h +++ b/src/CalcViewModel/Common/Automation/NarratorNotifier.h @@ -1,19 +1,20 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // Declaration of the NarratorNotifier class. #pragma once -#include "INarratorAnnouncementHost.h" +#include "NarratorAnnouncement.h" namespace CalculatorApp::Common::Automation { - public ref class NarratorNotifier sealed : public Windows::UI::Xaml::DependencyObject +public + ref class NarratorNotifier sealed : public Windows::UI::Xaml::DependencyObject { public: NarratorNotifier(); - void Announce(NarratorAnnouncement^ announcement); + void Announce(NarratorAnnouncement ^ announcement); property NarratorAnnouncement^ Announcement { @@ -26,32 +27,26 @@ namespace CalculatorApp::Common::Automation static void RegisterDependencyProperties(); - static property Windows::UI::Xaml::DependencyProperty^ AnnouncementProperty - { - Windows::UI::Xaml::DependencyProperty^ get() - { - return s_announcementProperty; - } - } + static property Windows::UI::Xaml::DependencyProperty + ^ AnnouncementProperty { Windows::UI::Xaml::DependencyProperty ^ get() { return s_announcementProperty; } } - static NarratorAnnouncement^ GetAnnouncement(Windows::UI::Xaml::DependencyObject^ element) - { - return safe_cast(element->GetValue(s_announcementProperty)); - } + static NarratorAnnouncement + ^ GetAnnouncement( + Windows::UI::Xaml::DependencyObject ^ element) { return safe_cast(element->GetValue(s_announcementProperty)); } - static void SetAnnouncement(Windows::UI::Xaml::DependencyObject^ element, NarratorAnnouncement^ value) + static void SetAnnouncement(Windows::UI::Xaml::DependencyObject ^ element, NarratorAnnouncement ^ value) { element->SetValue(s_announcementProperty, value); } private: static void OnAnnouncementChanged( - _In_ Windows::UI::Xaml::DependencyObject^ dependencyObject, - _In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ eventArgs); + _In_ Windows::UI::Xaml::DependencyObject ^ dependencyObject, + _In_ Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ eventArgs); - static Windows::UI::Xaml::DependencyProperty^ s_announcementProperty; + static Windows::UI::Xaml::DependencyProperty ^ s_announcementProperty; private: - INarratorAnnouncementHost^ m_announcementHost; + Windows::UI::Xaml::UIElement ^ m_announcementElement; }; } diff --git a/src/CalcViewModel/Common/Automation/NotificationHost.cpp b/src/CalcViewModel/Common/Automation/NotificationHost.cpp index 77ddddb1..92bf846e 100644 --- a/src/CalcViewModel/Common/Automation/NotificationHost.cpp +++ b/src/CalcViewModel/Common/Automation/NotificationHost.cpp @@ -10,23 +10,22 @@ using namespace Windows::UI::Xaml::Automation; using namespace Windows::UI::Xaml::Automation::Peers; using namespace Windows::UI::Xaml::Controls; -NotificationHost::NotificationHost() : - m_host(nullptr) -{} +NotificationHost::NotificationHost() + : m_host(nullptr) +{ +} bool NotificationHost::IsHostAvailable() { - return ApiInformation::IsMethodPresent( - L"Windows.UI.Xaml.Automation.Peers.AutomationPeer", - L"RaiseNotificationEvent"); + return ApiInformation::IsMethodPresent(L"Windows.UI.Xaml.Automation.Peers.AutomationPeer", L"RaiseNotificationEvent"); } -INarratorAnnouncementHost^ NotificationHost::MakeHost() +INarratorAnnouncementHost ^ NotificationHost::MakeHost() { return ref new NotificationHost(); } -void NotificationHost::Announce(NarratorAnnouncement^ announcement) +void NotificationHost::Announce(NarratorAnnouncement ^ announcement) { if (m_host == nullptr) { @@ -44,8 +43,7 @@ void NotificationHost::Announce(NarratorAnnouncement^ announcement) } } -StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind( - CustomPeers::AutomationNotificationKind customKindType) +StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(CustomPeers::AutomationNotificationKind customKindType) { switch (customKindType) { @@ -71,8 +69,8 @@ StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificati return StandardPeers::AutomationNotificationKind::Other; } -StandardPeers::AutomationNotificationProcessing NotificationHost::GetWindowsNotificationProcessing( - CustomPeers::AutomationNotificationProcessing customProcessingType) +StandardPeers::AutomationNotificationProcessing +NotificationHost::GetWindowsNotificationProcessing(CustomPeers::AutomationNotificationProcessing customProcessingType) { switch (customProcessingType) { diff --git a/src/CalcViewModel/Common/Automation/NotificationHost.h b/src/CalcViewModel/Common/Automation/NotificationHost.h index 7bcd1e24..d0a929c6 100644 --- a/src/CalcViewModel/Common/Automation/NotificationHost.h +++ b/src/CalcViewModel/Common/Automation/NotificationHost.h @@ -10,25 +10,25 @@ namespace CalculatorApp::Common::Automation { - public ref class NotificationHost sealed : public INarratorAnnouncementHost +public + ref class NotificationHost sealed : public INarratorAnnouncementHost { public: NotificationHost(); virtual bool IsHostAvailable(); - virtual INarratorAnnouncementHost^ MakeHost(); + virtual INarratorAnnouncementHost ^ MakeHost(); - virtual void Announce(NarratorAnnouncement^ announcement); + 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::AutomationNotificationKind + GetWindowsNotificationKind(CalculatorApp::Common::Automation::AutomationNotificationKind customKindType); - static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing GetWindowsNotificationProcessing( - CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType); + static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing + GetWindowsNotificationProcessing(CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType); private: - Windows::UI::Xaml::UIElement^ m_host; + Windows::UI::Xaml::UIElement ^ m_host; }; } - diff --git a/src/CalcViewModel/Common/BindableBase.cpp b/src/CalcViewModel/Common/BindableBase.cpp deleted file mode 100644 index b1fdcc78..00000000 --- a/src/CalcViewModel/Common/BindableBase.cpp +++ /dev/null @@ -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; - -/// -/// Notifies listeners that a property value has changed. -/// -/// Name of the property used to notify listeners. -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(); -} diff --git a/src/CalcViewModel/Common/BindableBase.h b/src/CalcViewModel/Common/BindableBase.h deleted file mode 100644 index b6a9f32e..00000000 --- a/src/CalcViewModel/Common/BindableBase.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - /// - /// Implementation of to simplify models. - /// - [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); - }; - } -} diff --git a/src/CalcViewModel/Common/BitLength.h b/src/CalcViewModel/Common/BitLength.h new file mode 100644 index 00000000..8c38e7e4 --- /dev/null +++ b/src/CalcViewModel/Common/BitLength.h @@ -0,0 +1,17 @@ +#pragma once + +namespace CalculatorApp +{ + namespace Common + { + public + enum class BitLength : int + { + BitLengthUnknown = -1, + BitLengthByte = 8, + BitLengthWord = 16, + BitLengthDWord = 32, + BitLengthQWord = 64, + }; + } +} diff --git a/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.cpp b/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.cpp index 4dfd4bce..76c0d4c0 100644 --- a/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.cpp +++ b/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.cpp @@ -8,9 +8,9 @@ using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace Platform; -NumbersAndOperatorsEnum CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(_In_ Object^ commandParameter) +NumbersAndOperatorsEnum CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(_In_ Object ^ commandParameter) { - auto eventArgs = dynamic_cast(commandParameter); + auto eventArgs = dynamic_cast(commandParameter); if (eventArgs != nullptr) { return eventArgs->Operation; @@ -21,9 +21,9 @@ NumbersAndOperatorsEnum CalculatorButtonPressedEventArgs::GetOperationFromComman } } -String^ CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(_In_ Object^ commandParameter) +String ^ CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(_In_ Object ^ commandParameter) { - auto eventArgs = dynamic_cast(commandParameter); + auto eventArgs = dynamic_cast(commandParameter); if (eventArgs != nullptr) { return eventArgs->AuditoryFeedback; @@ -33,4 +33,3 @@ String^ CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParamete return nullptr; } } - diff --git a/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h b/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h index 69f0fee4..27624987 100644 --- a/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h +++ b/src/CalcViewModel/Common/CalculatorButtonPressedEventArgs.h @@ -10,18 +10,21 @@ namespace CalculatorApp { namespace Common { - public ref class CalculatorButtonPressedEventArgs sealed + public + ref class CalculatorButtonPressedEventArgs sealed { public: - PROPERTY_R(Platform::String^, AuditoryFeedback); + PROPERTY_R(Platform::String ^, AuditoryFeedback); PROPERTY_R(CalculatorApp::NumbersAndOperatorsEnum, Operation); - CalculatorButtonPressedEventArgs( - Platform::String^ feedback, CalculatorApp::NumbersAndOperatorsEnum operation) : - m_AuditoryFeedback(feedback), m_Operation(operation) {} + CalculatorButtonPressedEventArgs(Platform::String ^ feedback, CalculatorApp::NumbersAndOperatorsEnum operation) + : m_AuditoryFeedback(feedback) + , m_Operation(operation) + { + } - static CalculatorApp::NumbersAndOperatorsEnum GetOperationFromCommandParameter(_In_ Platform::Object^ commandParameter); - static Platform::String^ GetAuditoryFeedbackFromCommandParameter(_In_ Platform::Object^ commandParameter); + static CalculatorApp::NumbersAndOperatorsEnum GetOperationFromCommandParameter(_In_ Platform::Object ^ commandParameter); + static Platform::String ^ GetAuditoryFeedbackFromCommandParameter(_In_ Platform::Object ^ commandParameter); }; } } diff --git a/src/CalcViewModel/Common/CalculatorButtonUser.h b/src/CalcViewModel/Common/CalculatorButtonUser.h index cc6647d9..a7b72189 100644 --- a/src/CalcViewModel/Common/CalculatorButtonUser.h +++ b/src/CalcViewModel/Common/CalculatorButtonUser.h @@ -9,205 +9,200 @@ namespace CalculatorApp { namespace CM = CalculationManager; - public enum class NumbersAndOperatorsEnum +public + enum class NumbersAndOperatorsEnum { - Zero = (int) CM::Command::Command0, - One = (int) CM::Command::Command1, - Two = (int) CM::Command::Command2, - Three = (int) CM::Command::Command3, - Four = (int) CM::Command::Command4, - Five = (int) CM::Command::Command5, - Six = (int) CM::Command::Command6, - Seven = (int) CM::Command::Command7, - Eight = (int) CM::Command::Command8, - Nine = (int) CM::Command::Command9, - Add = (int) CM::Command::CommandADD, - Subtract = (int) CM::Command::CommandSUB, - Multiply = (int) CM::Command::CommandMUL, - Divide = (int) CM::Command::CommandDIV, - Invert = (int) CM::Command::CommandREC, - Equals = (int) CM::Command::CommandEQU, - Decimal = (int) CM::Command::CommandPNT, - Sqrt = (int) CM::Command::CommandSQRT, - Percent = (int) CM::Command::CommandPERCENT, - Negate = (int) CM::Command::CommandSIGN, - Backspace = (int) CM::Command::CommandBACK, - ClearEntry = (int) CM::Command::CommandCENTR, - Clear = (int) CM::Command::CommandCLEAR, - Degree = (int) CM::Command::CommandDEG, - Radians = (int) CM::Command::CommandRAD, - Grads = (int) CM::Command::CommandGRAD, - Degrees = (int) CM::Command::CommandDegrees, - OpenParenthesis = (int) CM::Command::CommandOPENP, - CloseParenthesis = (int) CM::Command::CommandCLOSEP, - Pi = (int) CM::Command::CommandPI, - Sin = (int) CM::Command::CommandSIN, - Cos = (int) CM::Command::CommandCOS, - Tan = (int) CM::Command::CommandTAN, - Factorial = (int) CM::Command::CommandFAC, - XPower2 = (int) CM::Command::CommandSQR, - Mod = (int) CM::Command::CommandMOD, - FToE = (int) CM::Command::CommandFE, - LogBaseE = (int) CM::Command::CommandLN, - InvSin = (int) CM::Command::CommandASIN, - InvCos = (int) CM::Command::CommandACOS, - InvTan = (int) CM::Command::CommandATAN, - LogBase10 = (int) CM::Command::CommandLOG, - XPowerY = (int) CM::Command::CommandPWR, - YRootX = (int) CM::Command::CommandROOT, - TenPowerX = (int) CM::Command::CommandPOW10, - EPowerX = (int) CM::Command::CommandPOWE, - Exp = (int) CM::Command::CommandEXP, - IsScientificMode = (int) CM::Command::ModeScientific, - IsStandardMode = (int) CM::Command::ModeBasic, - None = (int) CM::Command::CommandNULL, - IsProgrammerMode = (int) CM::Command::ModeProgrammer, - DecButton = (int) CM::Command::CommandDec, - OctButton = (int) CM::Command::CommandOct, - HexButton = (int) CM::Command::CommandHex, - BinButton = (int) CM::Command::CommandBin, - And = (int) CM::Command::CommandAnd, - Ror = (int) CM::Command::CommandROR, - Rol = (int) CM::Command::CommandROL, - Or = (int) CM::Command::CommandOR, - Lsh = (int) CM::Command::CommandLSHF, - Rsh = (int) CM::Command::CommandRSHF, - Xor = (int) CM::Command::CommandXor, - Not = (int) CM::Command::CommandNot, - A = (int) CM::Command::CommandA, - B = (int) CM::Command::CommandB, - C = (int) CM::Command::CommandC, - D = (int) CM::Command::CommandD, - E = (int) CM::Command::CommandE, - F = (int) CM::Command::CommandF, + Zero = (int)CM::Command::Command0, + One = (int)CM::Command::Command1, + Two = (int)CM::Command::Command2, + Three = (int)CM::Command::Command3, + Four = (int)CM::Command::Command4, + Five = (int)CM::Command::Command5, + Six = (int)CM::Command::Command6, + Seven = (int)CM::Command::Command7, + Eight = (int)CM::Command::Command8, + Nine = (int)CM::Command::Command9, + Add = (int)CM::Command::CommandADD, + Subtract = (int)CM::Command::CommandSUB, + Multiply = (int)CM::Command::CommandMUL, + Divide = (int)CM::Command::CommandDIV, + Invert = (int)CM::Command::CommandREC, + Equals = (int)CM::Command::CommandEQU, + Decimal = (int)CM::Command::CommandPNT, + Sqrt = (int)CM::Command::CommandSQRT, + Percent = (int)CM::Command::CommandPERCENT, + Negate = (int)CM::Command::CommandSIGN, + Backspace = (int)CM::Command::CommandBACK, + ClearEntry = (int)CM::Command::CommandCENTR, + Clear = (int)CM::Command::CommandCLEAR, + Degree = (int)CM::Command::CommandDEG, + Radians = (int)CM::Command::CommandRAD, + Grads = (int)CM::Command::CommandGRAD, + Degrees = (int)CM::Command::CommandDegrees, + OpenParenthesis = (int)CM::Command::CommandOPENP, + CloseParenthesis = (int)CM::Command::CommandCLOSEP, + Pi = (int)CM::Command::CommandPI, + Sin = (int)CM::Command::CommandSIN, + Cos = (int)CM::Command::CommandCOS, + Tan = (int)CM::Command::CommandTAN, + Factorial = (int)CM::Command::CommandFAC, + XPower2 = (int)CM::Command::CommandSQR, + Mod = (int)CM::Command::CommandMOD, + FToE = (int)CM::Command::CommandFE, + LogBaseE = (int)CM::Command::CommandLN, + InvSin = (int)CM::Command::CommandASIN, + InvCos = (int)CM::Command::CommandACOS, + InvTan = (int)CM::Command::CommandATAN, + LogBase10 = (int)CM::Command::CommandLOG, + XPowerY = (int)CM::Command::CommandPWR, + YRootX = (int)CM::Command::CommandROOT, + TenPowerX = (int)CM::Command::CommandPOW10, + EPowerX = (int)CM::Command::CommandPOWE, + Exp = (int)CM::Command::CommandEXP, + IsScientificMode = (int)CM::Command::ModeScientific, + IsStandardMode = (int)CM::Command::ModeBasic, + None = (int)CM::Command::CommandNULL, + IsProgrammerMode = (int)CM::Command::ModeProgrammer, + DecButton = (int)CM::Command::CommandDec, + OctButton = (int)CM::Command::CommandOct, + HexButton = (int)CM::Command::CommandHex, + BinButton = (int)CM::Command::CommandBin, + And = (int)CM::Command::CommandAnd, + Ror = (int)CM::Command::CommandROR, + Rol = (int)CM::Command::CommandROL, + Or = (int)CM::Command::CommandOR, + Lsh = (int)CM::Command::CommandLSHF, + Rsh = (int)CM::Command::CommandRSHF, + Xor = (int)CM::Command::CommandXor, + Not = (int)CM::Command::CommandNot, + A = (int)CM::Command::CommandA, + B = (int)CM::Command::CommandB, + C = (int)CM::Command::CommandC, + D = (int)CM::Command::CommandD, + E = (int)CM::Command::CommandE, + F = (int)CM::Command::CommandF, Memory, // This is the memory button. Doesn't have a direct mapping to the CalcEngine. - Sinh = (int) CM::Command::CommandSINH, - Cosh = (int) CM::Command::CommandCOSH, - Tanh = (int) CM::Command::CommandTANH, - InvSinh = (int) CM::Command::CommandASINH, - InvCosh = (int) CM::Command::CommandACOSH, - InvTanh = (int) CM::Command::CommandATANH, - Qword = (int) CM::Command::CommandQword, - Dword = (int) CM::Command::CommandDword, - Word = (int) CM::Command::CommandWord, - Byte = (int) CM::Command::CommandByte, - Cube = (int) CM::Command::CommandCUB, - DMS = (int) CM::Command::CommandDMS, + Sinh = (int)CM::Command::CommandSINH, + Cosh = (int)CM::Command::CommandCOSH, + Tanh = (int)CM::Command::CommandTANH, + InvSinh = (int)CM::Command::CommandASINH, + InvCosh = (int)CM::Command::CommandACOSH, + InvTanh = (int)CM::Command::CommandATANH, + Qword = (int)CM::Command::CommandQword, + Dword = (int)CM::Command::CommandDword, + Word = (int)CM::Command::CommandWord, + Byte = (int)CM::Command::CommandByte, + Cube = (int)CM::Command::CommandCUB, + DMS = (int)CM::Command::CommandDMS, + Hyp = (int) CM::Command::CommandHYP, + Sec = (int) CM::Command::CommandSEC, + Csc = (int) CM::Command::CommandCSC, + Cot = (int) CM::Command::CommandCOT, + InvSec = (int) CM::Command::CommandASEC, + InvCsc = (int) CM::Command::CommandACSC, + InvCot = (int) CM::Command::CommandACOT, + Sech = (int) CM::Command::CommandSECH, + Csch = (int) CM::Command::CommandCSCH, + Coth = (int) CM::Command::CommandCOTH, + InvSech = (int) CM::Command::CommandASECH, + InvCsch = (int) CM::Command::CommandACSCH, + InvCoth = (int) CM::Command::CommandACOTH, + CubeRoot = (int) CM::Command::CommandCUBEROOT, + TwoPowerX = (int) CM::Command::CommandPOW2, + LogBaseY = (int) CM::Command::CommandLogBaseY, + Nand = (int) CM::Command::CommandNand, + Nor = (int) CM::Command::CommandNor, + Abs = (int) CM::Command::CommandAbs, + Floor = (int) CM::Command::CommandFloor, + Ceil = (int) CM::Command::CommandCeil, + Rand = (int) CM::Command::CommandRand, + Euler = (int) CM::Command::CommandEuler, + RshL = (int)CM::Command::CommandRSHFL, + RolC = (int)CM::Command::CommandROLC, + RorC = (int)CM::Command::CommandRORC, + + BINSTART = (int)CM::Command::CommandBINEDITSTART, + BINPOS0 = (int)CM::Command::CommandBINPOS0, + BINPOS1 = (int)CM::Command::CommandBINPOS1, + BINPOS2 = (int)CM::Command::CommandBINPOS2, + BINPOS3 = (int)CM::Command::CommandBINPOS3, + BINPOS4 = (int)CM::Command::CommandBINPOS4, + BINPOS5 = (int)CM::Command::CommandBINPOS5, + BINPOS6 = (int)CM::Command::CommandBINPOS6, + BINPOS7 = (int)CM::Command::CommandBINPOS7, + BINPOS8 = (int)CM::Command::CommandBINPOS8, + BINPOS9 = (int)CM::Command::CommandBINPOS9, + BINPOS10 = (int)CM::Command::CommandBINPOS10, + BINPOS11 = (int)CM::Command::CommandBINPOS11, + BINPOS12 = (int)CM::Command::CommandBINPOS12, + BINPOS13 = (int)CM::Command::CommandBINPOS13, + BINPOS14 = (int)CM::Command::CommandBINPOS14, + BINPOS15 = (int)CM::Command::CommandBINPOS15, + BINPOS16 = (int)CM::Command::CommandBINPOS16, + BINPOS17 = (int)CM::Command::CommandBINPOS17, + BINPOS18 = (int)CM::Command::CommandBINPOS18, + BINPOS19 = (int)CM::Command::CommandBINPOS19, + BINPOS20 = (int)CM::Command::CommandBINPOS20, + BINPOS21 = (int)CM::Command::CommandBINPOS21, + BINPOS22 = (int)CM::Command::CommandBINPOS22, + BINPOS23 = (int)CM::Command::CommandBINPOS23, + BINPOS24 = (int)CM::Command::CommandBINPOS24, + BINPOS25 = (int)CM::Command::CommandBINPOS25, + BINPOS26 = (int)CM::Command::CommandBINPOS26, + BINPOS27 = (int)CM::Command::CommandBINPOS27, + BINPOS28 = (int)CM::Command::CommandBINPOS28, + BINPOS29 = (int)CM::Command::CommandBINPOS29, + BINPOS30 = (int)CM::Command::CommandBINPOS30, + BINPOS31 = (int)CM::Command::CommandBINPOS31, + BINPOS32 = (int)CM::Command::CommandBINPOS32, + BINPOS33 = (int)CM::Command::CommandBINPOS33, + BINPOS34 = (int)CM::Command::CommandBINPOS34, + BINPOS35 = (int)CM::Command::CommandBINPOS35, + BINPOS36 = (int)CM::Command::CommandBINPOS36, + BINPOS37 = (int)CM::Command::CommandBINPOS37, + BINPOS38 = (int)CM::Command::CommandBINPOS38, + BINPOS39 = (int)CM::Command::CommandBINPOS39, + BINPOS40 = (int)CM::Command::CommandBINPOS40, + BINPOS41 = (int)CM::Command::CommandBINPOS41, + BINPOS42 = (int)CM::Command::CommandBINPOS42, + BINPOS43 = (int)CM::Command::CommandBINPOS43, + BINPOS44 = (int)CM::Command::CommandBINPOS44, + BINPOS45 = (int)CM::Command::CommandBINPOS45, + BINPOS46 = (int)CM::Command::CommandBINPOS46, + BINPOS47 = (int)CM::Command::CommandBINPOS47, + BINPOS48 = (int)CM::Command::CommandBINPOS48, + BINPOS49 = (int)CM::Command::CommandBINPOS49, + BINPOS50 = (int)CM::Command::CommandBINPOS50, + BINPOS51 = (int)CM::Command::CommandBINPOS51, + BINPOS52 = (int)CM::Command::CommandBINPOS52, + BINPOS53 = (int)CM::Command::CommandBINPOS53, + BINPOS54 = (int)CM::Command::CommandBINPOS54, + BINPOS55 = (int)CM::Command::CommandBINPOS55, + BINPOS56 = (int)CM::Command::CommandBINPOS56, + BINPOS57 = (int)CM::Command::CommandBINPOS57, + BINPOS58 = (int)CM::Command::CommandBINPOS58, + BINPOS59 = (int)CM::Command::CommandBINPOS59, + BINPOS60 = (int)CM::Command::CommandBINPOS60, + BINPOS61 = (int)CM::Command::CommandBINPOS61, + BINPOS62 = (int)CM::Command::CommandBINPOS62, + BINPOS63 = (int)CM::Command::CommandBINPOS63, + BINEND = (int)CM::Command::CommandBINEDITEND, - BINSTART = (int) CM::Command::CommandBINEDITSTART, - BINPOS0 = (int) CM::Command::CommandBINPOS0, - BINPOS1 = (int) CM::Command::CommandBINPOS1, - BINPOS2 = (int) CM::Command::CommandBINPOS2, - BINPOS3 = (int) CM::Command::CommandBINPOS3, - BINPOS4 = (int) CM::Command::CommandBINPOS4, - BINPOS5 = (int) CM::Command::CommandBINPOS5, - BINPOS6 = (int) CM::Command::CommandBINPOS6, - BINPOS7 = (int) CM::Command::CommandBINPOS7, - BINPOS8 = (int) CM::Command::CommandBINPOS8, - BINPOS9 = (int) CM::Command::CommandBINPOS9, - BINPOS10 = (int) CM::Command::CommandBINPOS10, - BINPOS11 = (int) CM::Command::CommandBINPOS11, - BINPOS12 = (int) CM::Command::CommandBINPOS12, - BINPOS13 = (int) CM::Command::CommandBINPOS13, - BINPOS14 = (int) CM::Command::CommandBINPOS14, - BINPOS15 = (int) CM::Command::CommandBINPOS15, - BINPOS16 = (int) CM::Command::CommandBINPOS16, - BINPOS17 = (int) CM::Command::CommandBINPOS17, - BINPOS18 = (int) CM::Command::CommandBINPOS18, - BINPOS19 = (int) CM::Command::CommandBINPOS19, - BINPOS20 = (int) CM::Command::CommandBINPOS20, - BINPOS21 = (int) CM::Command::CommandBINPOS21, - BINPOS22 = (int) CM::Command::CommandBINPOS22, - BINPOS23 = (int) CM::Command::CommandBINPOS23, - BINPOS24 = (int) CM::Command::CommandBINPOS24, - BINPOS25 = (int) CM::Command::CommandBINPOS25, - BINPOS26 = (int) CM::Command::CommandBINPOS26, - BINPOS27 = (int) CM::Command::CommandBINPOS27, - BINPOS28 = (int) CM::Command::CommandBINPOS28, - BINPOS29 = (int) CM::Command::CommandBINPOS29, - BINPOS30 = (int) CM::Command::CommandBINPOS30, - BINPOS31 = (int) CM::Command::CommandBINPOS31, - BINPOS32 = (int) CM::Command::CommandBINPOS32, - BINPOS33 = (int) CM::Command::CommandBINPOS33, - BINPOS34 = (int) CM::Command::CommandBINPOS34, - BINPOS35 = (int) CM::Command::CommandBINPOS35, - BINPOS36 = (int) CM::Command::CommandBINPOS36, - BINPOS37 = (int) CM::Command::CommandBINPOS37, - BINPOS38 = (int) CM::Command::CommandBINPOS38, - BINPOS39 = (int) CM::Command::CommandBINPOS39, - BINPOS40 = (int) CM::Command::CommandBINPOS40, - BINPOS41 = (int) CM::Command::CommandBINPOS41, - BINPOS42 = (int) CM::Command::CommandBINPOS42, - BINPOS43 = (int) CM::Command::CommandBINPOS43, - BINPOS44 = (int) CM::Command::CommandBINPOS44, - BINPOS45 = (int) CM::Command::CommandBINPOS45, - BINPOS46 = (int) CM::Command::CommandBINPOS46, - BINPOS47 = (int) CM::Command::CommandBINPOS47, - BINPOS48 = (int) CM::Command::CommandBINPOS48, - BINPOS49 = (int) CM::Command::CommandBINPOS49, - BINPOS50 = (int) CM::Command::CommandBINPOS50, - BINPOS51 = (int) CM::Command::CommandBINPOS51, - BINPOS52 = (int) CM::Command::CommandBINPOS52, - BINPOS53 = (int) CM::Command::CommandBINPOS53, - BINPOS54 = (int) CM::Command::CommandBINPOS54, - BINPOS55 = (int) CM::Command::CommandBINPOS55, - BINPOS56 = (int) CM::Command::CommandBINPOS56, - BINPOS57 = (int) CM::Command::CommandBINPOS57, - BINPOS58 = (int) CM::Command::CommandBINPOS58, - BINPOS59 = (int) CM::Command::CommandBINPOS59, - BINPOS60 = (int) CM::Command::CommandBINPOS60, - BINPOS61 = (int) CM::Command::CommandBINPOS61, - BINPOS62 = (int) CM::Command::CommandBINPOS62, - BINPOS63 = (int) CM::Command::CommandBINPOS63, - BINEND = (int) CM::Command::CommandBINEDITEND, - Hyp = (int) CM::Command::CommandHYP - }; + // Enum values below are used for Tracelogging and do not map to the Calculator engine + MemoryAdd = (int)CM::Command::CommandMPLUS, + MemorySubtract = (int)CM::Command::CommandMMINUS, + MemoryRecall = (int)CM::Command::CommandRECALL, + MemoryClear = (int)CM::Command::CommandMCLEAR, + BitflipButton = 1000, + FullKeypadButton = 1001, - // This contains list of functions whose usage we are tracelogging - public enum class FunctionLogEnum - { - Invert = (int) CM::Command::CommandREC, - Sqrt = (int) CM::Command::CommandSQRT, - Percent = (int) CM::Command::CommandPERCENT, - Negate = (int) CM::Command::CommandSIGN, - Degrees = (int) CM::Command::CommandDegrees, - Pi = (int) CM::Command::CommandPI, - Sin = (int) CM::Command::CommandSIN, - Cos = (int) CM::Command::CommandCOS, - Tan = (int) CM::Command::CommandTAN, - Factorial = (int) CM::Command::CommandFAC, - XPower2 = (int) CM::Command::CommandSQR, - Mod = (int) CM::Command::CommandMOD, - FToE = (int) CM::Command::CommandFE, - LogBaseE = (int) CM::Command::CommandLN, - InvSin = (int) CM::Command::CommandASIN, - InvCos = (int) CM::Command::CommandACOS, - InvTan = (int) CM::Command::CommandATAN, - LogBase10 = (int) CM::Command::CommandLOG, - XPowerY = (int) CM::Command::CommandPWR, - YRootX = (int) CM::Command::CommandROOT, - TenPowerX = (int) CM::Command::CommandPOW10, - EPowerX = (int) CM::Command::CommandPOWE, - Exp = (int) CM::Command::CommandEXP, - DecButton = (int) CM::Command::CommandDec, - OctButton = (int) CM::Command::CommandOct, - HexButton = (int) CM::Command::CommandHex, - BinButton = (int) CM::Command::CommandBin, - And = (int) CM::Command::CommandAnd, - Ror = (int) CM::Command::CommandROR, - Rol = (int) CM::Command::CommandROL, - Or = (int) CM::Command::CommandOR, - Lsh = (int) CM::Command::CommandLSHF, - Rsh = (int) CM::Command::CommandRSHF, - Xor = (int) CM::Command::CommandXor, - Not = (int) CM::Command::CommandNot, - Sinh = (int) CM::Command::CommandSINH, - Cosh = (int) CM::Command::CommandCOSH, - Tanh = (int) CM::Command::CommandTANH, - InvSinh = (int) CM::Command::CommandASINH, - InvCosh = (int) CM::Command::CommandACOSH, - InvTanh = (int) CM::Command::CommandATANH, - Cube = (int) CM::Command::CommandCUB, - DMS = (int) CM::Command::CommandDMS, + // Buttons used in graphing calculator + LessThan, + LessThanOrEqualTo, + GreaterThan, + GreaterThanOrEqualTo, + X, + Y, + Submit }; } diff --git a/src/CalcViewModel/Common/CalculatorDisplay.cpp b/src/CalcViewModel/Common/CalculatorDisplay.cpp index cd531e01..5cf8afbc 100644 --- a/src/CalcViewModel/Common/CalculatorDisplay.cpp +++ b/src/CalcViewModel/Common/CalculatorDisplay.cpp @@ -9,6 +9,7 @@ using namespace CalculatorApp; using namespace CalculationManager; +using namespace Platform; using namespace std; CalculatorDisplay::CalculatorDisplay() @@ -31,12 +32,12 @@ void CalculatorDisplay::SetPrimaryDisplay(_In_ const wstring& displayStringValue { if (auto calcVM = m_callbackReference.Resolve()) { - calcVM->SetPrimaryDisplay(displayStringValue, isError); + calcVM->SetPrimaryDisplay(StringReference(displayStringValue.c_str()), isError); } } } -void CalculatorDisplay::SetParenDisplayText(_In_ const std::wstring& parenthesisCount) +void CalculatorDisplay::SetParenthesisNumber(_In_ unsigned int parenthesisCount) { if (m_callbackReference != nullptr) { @@ -69,11 +70,13 @@ void CalculatorDisplay::SetIsInError(bool isError) } } -void CalculatorDisplay::SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) +void CalculatorDisplay::SetExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands) { if (m_callbackReference != nullptr) { - if(auto calcVM = m_callbackReference.Resolve()) + if (auto calcVM = m_callbackReference.Resolve()) { calcVM->SetExpressionDisplay(tokens, commands); } @@ -134,3 +137,14 @@ void CalculatorDisplay::MemoryItemChanged(unsigned int indexOfMemory) } } } + +void CalculatorDisplay::InputChanged() +{ + if (m_callbackReference != nullptr) + { + if (auto calcVM = m_callbackReference.Resolve()) + { + calcVM->OnInputChanged(); + } + } +} diff --git a/src/CalcViewModel/Common/CalculatorDisplay.h b/src/CalcViewModel/Common/CalculatorDisplay.h index 9ff06a6e..921260da 100644 --- a/src/CalcViewModel/Common/CalculatorDisplay.h +++ b/src/CalcViewModel/Common/CalculatorDisplay.h @@ -8,7 +8,7 @@ namespace CalculatorApp { // Callback interface to be implemented by the CalculatorManager - class CalculatorDisplay: public ICalcDisplay + class CalculatorDisplay : public ICalcDisplay { public: CalculatorDisplay(); @@ -18,14 +18,17 @@ namespace CalculatorApp private: void SetPrimaryDisplay(_In_ const std::wstring& displayString, _In_ bool isError) override; void SetIsInError(bool isError) override; - void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands) override; + void SetExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands) override; void SetMemorizedNumbers(_In_ const std::vector& memorizedNumbers) override; void OnHistoryItemAdded(_In_ unsigned int addedItemIndex) override; - void SetParenDisplayText(_In_ const std::wstring& parenthesisCount) override; + void SetParenthesisNumber(_In_ unsigned int parenthesisCount) override; void OnNoRightParenAdded() override; void MaxDigitsReached() override; void BinaryOperatorReceived() override; void MemoryItemChanged(unsigned int indexOfMemory) override; + void InputChanged() override; private: Platform::WeakReference m_callbackReference; diff --git a/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp b/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp deleted file mode 100644 index db943c5c..00000000 --- a/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp +++ /dev/null @@ -1,40 +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 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 ConversionResultTaskHelper::CompleteAfter(unsigned int timeout) -{ - co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{ std::chrono::duration(timeout) }); -}; -#pragma optimize("", on) diff --git a/src/CalcViewModel/Common/ConversionResultTaskHelper.h b/src/CalcViewModel/Common/ConversionResultTaskHelper.h deleted file mode 100644 index 2fe543c3..00000000 --- a/src/CalcViewModel/Common/ConversionResultTaskHelper.h +++ /dev/null @@ -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 functionToRun); - ~ConversionResultTaskHelper(); - - private: - concurrency::task CompleteAfter(unsigned int timeout); - - unsigned int m_delay; - concurrency::cancellation_token_source m_cts; - const std::function m_storedFunction; - }; - } -} diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index c11630c4..ae24aad1 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -11,23 +11,28 @@ using namespace concurrency; using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace Platform; +using namespace Platform::Collections; using namespace Windows::Foundation; using namespace Windows::System; using namespace Windows::ApplicationModel::DataTransfer; +using namespace Windows::Foundation::Collections; -unsigned long long maxOperandNumber; +StringReference PasteErrorString(L"NoOp"); -String^ CopyPasteManager::supportedFormats[] = -{ - StandardDataFormats::Text -}; +static const wstring c_validBasicCharacterSet = L"0123456789+-.e"; +static const wstring c_validStandardCharacterSet = c_validBasicCharacterSet + L"*/"; +static const wstring c_validScientificCharacterSet = c_validStandardCharacterSet + L"()^%"; +static const wstring c_validProgrammerCharacterSet = c_validStandardCharacterSet + L"()%abcdfABCDEF"; -constexpr wstring_view c_validCharacterSet{ L"0123456789()+-*/.abcdefABCDEF" }; +// The below values can not be "constexpr"-ed, +// as both wstring_view and wchar[] can not be concatenated // [\s\x85] means white-space characters static const wstring c_wspc = L"[\\s\\x85]*"; static const wstring c_wspcLParens = c_wspc + L"[(]*" + c_wspc; +static const wstring c_wspcLParenSigned = c_wspc + L"([-+]?[(])*" + c_wspc; static const wstring c_wspcRParens = c_wspc + L"[)]*" + c_wspc; -static const wstring c_signedDecFloat = L"[-+]?\\d*(\\d|[.])\\d*"; +static const wstring c_signedDecFloat = L"(?:[-+]?(?:\\d+(\\.\\d*)?|\\.\\d+))"; +static const wstring c_optionalENotation = L"(?:e[+-]?\\d+)?"; // Programmer Mode Integer patterns // Support digit separators ` (WinDbg/MASM), ' (C++), and _ (C# and other languages) @@ -38,43 +43,25 @@ static const wstring c_binProgrammerChars = L"[0-1]+((_|'|`)[0-1]+)*"; static const wstring c_uIntSuffixes = L"[uU]?[lL]{0,2}"; // RegEx Patterns used by various modes -static const array standardModePatterns = -{ - wregex(c_wspc + c_signedDecFloat + c_wspc) -}; -static const array scientificModePatterns = -{ - wregex(c_wspcLParens + c_signedDecFloat + c_wspcRParens), - wregex(c_wspcLParens + c_signedDecFloat + L"[e]([+]|[-])+\\d+" + c_wspcRParens) -}; -static const array, 4> programmerModePatterns = -{ { - // Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh - { - wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens), - wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens) - }, - // Decimal numbers like -145, 145, 0n145, 123ull etc - { - wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" +c_wspcRParens), - wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens) - }, - // Octal numbers like 06, 010, 0t77, 0o77, 077ull etc - { - wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens) - }, - // Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull - { - wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens), - wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens) - } - } }; -static const array unitConverterPatterns = -{ - wregex(c_wspc + L"[-+]?\\d*[.]?\\d*" + c_wspc) +static const array standardModePatterns = { wregex(c_wspc + c_signedDecFloat + c_optionalENotation + c_wspc) }; +static const array scientificModePatterns = { wregex( + L"(" + c_wspc + L"[-+]?)|(" + c_wspcLParenSigned + L")" + c_signedDecFloat + c_optionalENotation + c_wspcRParens) }; +static const array, 4> programmerModePatterns = { + { // Hex numbers like 5F, 4A0C, 0xa9, 0xFFull, 47CDh + { wregex(c_wspcLParens + L"(0[xX])?" + c_hexProgrammerChars + c_uIntSuffixes + c_wspcRParens), + wregex(c_wspcLParens + c_hexProgrammerChars + L"[hH]?" + c_wspcRParens) }, + // Decimal numbers like -145, 145, 0n145, 123ull etc + { wregex(c_wspcLParens + L"[-+]?" + c_decProgrammerChars + L"[lL]{0,2}" + c_wspcRParens), + wregex(c_wspcLParens + L"(0[nN])?" + c_decProgrammerChars + c_uIntSuffixes + c_wspcRParens) }, + // Octal numbers like 06, 010, 0t77, 0o77, 077ull etc + { wregex(c_wspcLParens + L"(0[otOT])?" + c_octProgrammerChars + c_uIntSuffixes + c_wspcRParens) }, + // Binary numbers like 011010110, 0010110, 10101001, 1001b, 0b1001, 0y1001, 0b1001ull + { wregex(c_wspcLParens + L"(0[byBY])?" + c_binProgrammerChars + c_uIntSuffixes + c_wspcRParens), + wregex(c_wspcLParens + c_binProgrammerChars + L"[bB]?" + c_wspcRParens) } } }; +static const array unitConverterPatterns = { wregex(c_wspc + c_signedDecFloat + c_wspc) }; -void CopyPasteManager::CopyToClipboard(String^ stringToCopy) +void CopyPasteManager::CopyToClipboard(String ^ stringToCopy) { // Copy the string to the clipboard auto dataPackage = ref new DataPackage(); @@ -82,7 +69,7 @@ void CopyPasteManager::CopyToClipboard(String^ stringToCopy) Clipboard::SetContent(dataPackage); } -task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +IAsyncOperation ^ CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, NumberBase programmerNumberBase, BitLength bitLengthType) { // Retrieve the text in the clipboard auto dataPackageView = Clipboard::GetContent(); @@ -92,54 +79,47 @@ task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupTyp //-- add support to allow pasting for expressions like .2 , -.2 //-- add support to allow pasting for expressions like 1.3e12(as of now we allow 1.3e+12) - return create_task((dataPackageView->GetTextAsync(::StandardDataFormats::Text))) - .then([mode, modeType, programmerNumberBase, bitLengthType](String^ pastedText) - { - return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType); - } - , task_continuation_context::use_arbitrary()); + return create_async([dataPackageView, mode, modeType, programmerNumberBase, bitLengthType] { + return create_task(dataPackageView->GetTextAsync(::StandardDataFormats::Text)) + .then( + [mode, modeType, programmerNumberBase, bitLengthType](String ^ pastedText) { + return ValidatePasteExpression(pastedText, mode, modeType, programmerNumberBase, bitLengthType); + }, + task_continuation_context::use_arbitrary()); + }); } -int CopyPasteManager::ClipboardTextFormat() +bool CopyPasteManager::HasStringToPaste() { - int result = -1; - - auto dataPackageView = Clipboard::GetContent(); - - for (int i = 0; i < RTL_NUMBER_OF(supportedFormats); i++) - { - if (dataPackageView->Contains(supportedFormats[i])) - { - result = i; - break; - } - } - return result; + return Clipboard::GetContent()->Contains(StandardDataFormats::Text); } -String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) +String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, NumberBase programmerNumberBase, BitLength bitLengthType) { - return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); + return ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); } // return "NoOp" if pastedText is invalid else return pastedText - -String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +String + ^ CopyPasteManager::ValidatePasteExpression( + String ^ pastedText, + ViewMode mode, + CategoryGroupType modeType, + NumberBase programmerNumberBase, + BitLength bitLengthType) { if (pastedText->Length() > MaxPasteableLength) { // return NoOp to indicate don't paste anything. - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"PastedExpressionSizeGreaterThanMaxAllowed", mode, programmerNumberBase, bitLengthType); - return StringReference(PasteErrorString); + TraceLogger::GetInstance()->LogError(mode, L"CopyPasteManager::ValidatePasteExpression", L"PastedExpressionSizeGreaterThanMaxAllowed"); + return PasteErrorString; } - wstring pasteExpression = pastedText->Data(); - // Get english translated expression - String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pasteExpression); + String ^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(pastedText); // Removing the spaces, comma separator from the pasteExpression to allow pasting of expressions like 1 + 2+1,333 - pasteExpression = Utils::RemoveUnwantedCharsFromWstring(englishString->Data()); + auto pasteExpression = wstring(RemoveUnwantedCharsFromString(englishString)->Data()); // If the last character is an = sign, remove it from the pasteExpression to allow evaluating the result on paste. if (!pasteExpression.empty() && pasteExpression.back() == L'=') @@ -149,31 +129,32 @@ String^ CopyPasteManager::ValidatePasteExpression(String^ pastedText, ViewMode m // Extract operands from the expression to make regex comparison easy and quick. For whole expression it was taking too much of time. // Operands vector will have the list of operands in the pasteExpression - vector operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType); - if (operands.empty()) + auto operands = ExtractOperands(StringReference(pasteExpression.c_str()), mode); + if (operands->Size == 0) { // return NoOp to indicate don't paste anything. - return StringReference(PasteErrorString); + return PasteErrorString; } if (modeType == CategoryGroupType::Converter) { - operands = { pasteExpression }; + operands->Clear(); + operands->Append(ref new String(pasteExpression.c_str())); } // validate each operand with patterns for different modes if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType)) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"InvalidExpressionForPresentMode", mode, programmerNumberBase, bitLengthType); - return StringReference(PasteErrorString); + TraceLogger::GetInstance()->LogError(mode, L"CopyPasteManager::ValidatePasteExpression", L"InvalidExpressionForPresentMode"); + return PasteErrorString; } - return ref new String(pastedText->Data()); + return pastedText; } -vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType) +IVector ^ CopyPasteManager::ExtractOperands(Platform::String ^ pasteExpression, ViewMode mode) { - vector operands{}; + auto operands = ref new Vector(); size_t lastIndex = 0; bool haveOperator = false; bool startExpCounting = false; @@ -181,50 +162,72 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression bool isPreviousOpenParen = false; bool isPreviousOperator = false; + wstring validCharacterSet; + switch (mode) + { + case ViewMode::Standard: + validCharacterSet = c_validStandardCharacterSet; + break; + case ViewMode::Scientific: + validCharacterSet = c_validScientificCharacterSet; + break; + case ViewMode::Programmer: + validCharacterSet = c_validProgrammerCharacterSet; + break; + default: + validCharacterSet = c_validBasicCharacterSet; + } + // This will have the exponent length size_t expLength = 0; - for (size_t i = 0; i < pasteExpression.length(); i++) + int i = -1; + for (auto currentChar : pasteExpression) { + ++i; // if the current character is not a valid one don't process it - if (c_validCharacterSet.find(pasteExpression.at(i)) == wstring_view::npos) + if (validCharacterSet.find(currentChar) == wstring_view::npos) { continue; } - if (operands.size() >= MaxOperandCount) + if (operands->Size >= MaxOperandCount) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"OperandCountGreaterThanMaxCount", mode, programmerNumberBase, bitLengthType); - operands.clear(); + TraceLogger::GetInstance()->LogError(mode, L"CopyPasteManager::ExtractOperands", L"OperandCountGreaterThanMaxCount"); + operands->Clear(); return operands; } - if (startExpCounting) + if (currentChar >= L'0' && currentChar <= L'9') { - if ((pasteExpression.at(i) >= L'0') && (pasteExpression.at(i) <= L'9')) + if (startExpCounting) { expLength++; // to disallow pasting of 1e+12345 as 1e+1234, max exponent that can be pasted is 9999. if (expLength > MaxExponentLength) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"ExponentLengthGreaterThanMaxLength", mode, programmerNumberBase, bitLengthType); - operands.clear(); + TraceLogger::GetInstance()->LogError(mode, L"CopyPasteManager::ExtractOperands", L"ExponentLengthGreaterThanMaxLength"); + operands->Clear(); return operands; } } + isPreviousOperator = false; } - - if ((mode != ViewMode::Programmer) && (pasteExpression.at(i) == L'e')) + else if (currentChar == L'e') { - startExpCounting = true; + if (mode != ViewMode::Programmer) + { + startExpCounting = true; + } + isPreviousOperator = false; } - - if (((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-') || (pasteExpression.at(i) == L'*') || (pasteExpression.at(i) == L'/'))) + else if (currentChar == L'+' || currentChar == L'-' || currentChar == L'*' || currentChar == L'/' || currentChar == L'^' || currentChar == L'%') { - if ((pasteExpression.at(i) == L'+') || (pasteExpression.at(i) == L'-')) + if (currentChar == L'+' || currentChar == L'-') { // don't break the expression into operands if the encountered character corresponds to sign command(+-) - if (isPreviousOpenParen || startOfExpression || isPreviousOperator || ((mode != ViewMode::Programmer) && !((i != 0) && (pasteExpression.at(i - 1) != L'e')))) + if (isPreviousOpenParen || startOfExpression || isPreviousOperator + || ((mode != ViewMode::Programmer) && !((i != 0) && pasteExpression->Data()[i - 1] != L'e'))) { isPreviousOperator = false; continue; @@ -235,7 +238,7 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression expLength = 0; haveOperator = true; isPreviousOperator = true; - operands.push_back(pasteExpression.substr(lastIndex, i - lastIndex)); + operands->Append(ref new String(wstring(pasteExpression->Data()).substr(lastIndex, i - lastIndex).c_str())); lastIndex = i + 1; } else @@ -243,37 +246,37 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression isPreviousOperator = false; } - isPreviousOpenParen = (pasteExpression.at(i) == L'('); + isPreviousOpenParen = (currentChar == L'('); startOfExpression = false; } if (!haveOperator) { - operands.clear(); - operands.push_back(pasteExpression); + operands->Clear(); + operands->Append(pasteExpression); } else { - operands.push_back(pasteExpression.substr(lastIndex, pasteExpression.length() - 1)); + operands->Append(ref new String(wstring(pasteExpression->Data()).substr(lastIndex, pasteExpression->Length() - 1).c_str())); } return operands; } -bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +bool CopyPasteManager::ExpressionRegExMatch( + IVector ^ operands, + ViewMode mode, + CategoryGroupType modeType, + NumberBase programmerNumberBase, + BitLength bitLengthType) { - if (operands.empty()) + if (operands->Size == 0) { return false; } - bool expMatched = true; vector patterns{}; - pair operandLimits = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType); - size_t maxOperandLength = operandLimits.first; - uint64_t maxOperandValue = operandLimits.second; - if (mode == ViewMode::Standard) { patterns.assign(standardModePatterns.begin(), standardModePatterns.end()); @@ -284,47 +287,59 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m } else if (mode == ViewMode::Programmer) { - patterns.assign(programmerModePatterns[programmerNumberBase - HexBase].begin(), programmerModePatterns[programmerNumberBase - HexBase].end()); + patterns.assign( + programmerModePatterns[(int)programmerNumberBase - (int)NumberBase::HexBase].begin(), + programmerModePatterns[(int)programmerNumberBase - (int)NumberBase::HexBase].end()); } else if (modeType == CategoryGroupType::Converter) { patterns.assign(unitConverterPatterns.begin(), unitConverterPatterns.end()); } - for (const wstring& operand : operands) + auto maxOperandLengthAndValue = GetMaxOperandLengthAndValue(mode, modeType, programmerNumberBase, bitLengthType); + bool expMatched = true; + + for (const auto& operand : operands) { // Each operand only needs to match one of the available patterns. bool operandMatched = false; - for (const wregex& pattern : patterns) + for (const auto& pattern : patterns) { - operandMatched = operandMatched || regex_match(operand, pattern); + operandMatched = operandMatched || regex_match(operand->Data(), pattern); } 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. - wstring operandValue = SanitizeOperand(operand); + auto operandValue = SanitizeOperand(operand); // If an operand exceeds the maximum length allowed, break and return. - if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLength) + if (OperandLength(operandValue, mode, modeType, programmerNumberBase) > maxOperandLengthAndValue.maxLength) { expMatched = false; break; } // If maxOperandValue is set and the operandValue exceeds it, break and return. - if (maxOperandValue != 0) + if (maxOperandLengthAndValue.maxValue != 0) { - unsigned long long int operandAsULL = 0; - if (!TryOperandToULL(operandValue, programmerNumberBase, operandAsULL)) + auto operandAsULL = TryOperandToULL(operandValue, programmerNumberBase); + if (operandAsULL == nullptr) { // Operand was empty, received invalid_argument, or received out_of_range. Input is invalid. expMatched = false; break; } - if (operandAsULL > maxOperandValue) + // 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; @@ -338,34 +353,39 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m return expMatched; } -pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +CopyPasteMaxOperandLengthAndValue +CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, NumberBase programmerNumberBase, BitLength bitLengthType) { - size_t maxLength = 0; - uint64_t maxValue = 0; - + constexpr size_t defaultMaxOperandLength = 0; + constexpr uint64_t defaultMaxValue = 0; + CopyPasteMaxOperandLengthAndValue res; if (mode == ViewMode::Standard) { - maxLength = MaxStandardOperandLength; + res.maxLength = MaxStandardOperandLength; + res.maxValue = defaultMaxValue; + return res; } else if (mode == ViewMode::Scientific) { - maxLength = MaxScientificOperandLength; + res.maxLength = MaxScientificOperandLength; + res.maxValue = defaultMaxValue; + return res; } else if (mode == ViewMode::Programmer) { unsigned int bitLength = 0; switch (bitLengthType) { - case QwordType: + case BitLength::BitLengthQWord: bitLength = 64; break; - case DwordType: + case BitLength::BitLengthDWord: bitLength = 32; break; - case WordType: + case BitLength::BitLengthWord: bitLength = 16; break; - case ByteType: + case BitLength::BitLengthByte: bitLength = 8; break; } @@ -373,63 +393,69 @@ pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mo double bitsPerDigit = 0; switch (programmerNumberBase) { - case BinBase: + case NumberBase::BinBase: bitsPerDigit = log2(2); break; - case OctBase: + case NumberBase::OctBase: bitsPerDigit = log2(8); break; - case DecBase: + case NumberBase::DecBase: bitsPerDigit = log2(10); break; - case HexBase: + case NumberBase::HexBase: bitsPerDigit = log2(16); break; } - unsigned int signBit = (programmerNumberBase == DecBase) ? 1 : 0; + unsigned int signBit = (programmerNumberBase == NumberBase::DecBase) ? 1 : 0; - maxLength = (size_t)ceil((bitLength - signBit) / bitsPerDigit); - maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + const auto maxLength = static_cast(ceil((bitLength - signBit) / bitsPerDigit)); + const uint64_t maxValue = UINT64_MAX >> (MaxProgrammerBitLength - (bitLength - signBit)); + + res.maxLength = maxLength; + res.maxValue = maxValue; + return res; } else if (modeType == CategoryGroupType::Converter) { - maxLength = MaxConverterInputLength; + res.maxLength = MaxConverterInputLength; + res.maxValue = defaultMaxValue; + return res; } - return make_pair(maxLength, maxValue); + res.maxLength = defaultMaxOperandLength; + res.maxValue = defaultMaxValue; + return res; } -wstring CopyPasteManager::SanitizeOperand(const wstring& operand) +Platform::String ^ CopyPasteManager::SanitizeOperand(Platform::String ^ operand) { - wchar_t unWantedChars[] = { L'\'', L'_', L'`', L'(', L')', L'-' }; + constexpr wchar_t unWantedChars[] = { L'\'', L'_', L'`', L'(', L')', L'-', L'+' }; - return Utils::RemoveUnwantedCharsFromWstring(operand, unWantedChars, static_cast(size(unWantedChars))); + return ref new String(Utils::RemoveUnwantedCharsFromString(operand->Data(), unWantedChars).c_str()); } -bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, unsigned long long int& result) +IBox ^ CopyPasteManager::TryOperandToULL(String ^ operand, NumberBase numberBase) { - result = 0; - - if (operand.length() == 0 || operand.front() == L'-') + if (operand->Length() == 0 || operand->Data()[0] == L'-') { - return false; + return nullptr; } - // Default to base10 - int intBase = 10; + int intBase; switch (numberBase) { - case HexBase: + case NumberBase::HexBase: intBase = 16; break; - case OctBase: + case NumberBase::OctBase: intBase = 8; break; - case BinBase: + case NumberBase::BinBase: intBase = 2; break; - case DecBase: + default: + case NumberBase::DecBase: intBase = 10; break; } @@ -437,106 +463,101 @@ bool CopyPasteManager::TryOperandToULL(const wstring& operand, int numberBase, u wstring::size_type size = 0; try { - result = stoull(operand, &size, intBase); - return true; + return stoull(operand->Data(), &size, intBase); } - catch (invalid_argument) + catch (const invalid_argument&) { // Do nothing } - catch (out_of_range) + catch (const out_of_range&) { // Do nothing } - return false; + return nullptr; } -size_t CopyPasteManager::OperandLength(wstring operand, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase) +ULONG32 CopyPasteManager::OperandLength(Platform::String ^ operand, ViewMode mode, CategoryGroupType modeType, NumberBase programmerNumberBase) { - size_t len = 0; - if (mode == ViewMode::Standard || mode == ViewMode::Scientific) + if (modeType == CategoryGroupType::Converter) { - len = StandardScientificOperandLength(operand); - } - else if (mode == ViewMode::Programmer) - { - len = ProgrammerOperandLength(operand, programmerNumberBase); - } - else if (modeType == CategoryGroupType::Converter) - { - len = operand.length(); + return operand->Length(); } - return len; + switch (mode) + { + case ViewMode::Standard: + case ViewMode::Scientific: + return StandardScientificOperandLength(operand); + + case ViewMode::Programmer: + return ProgrammerOperandLength(operand, programmerNumberBase); + + default: + return 0; + } } -size_t CopyPasteManager::StandardScientificOperandLength(wstring operand) +ULONG32 CopyPasteManager::StandardScientificOperandLength(Platform::String ^ operand) { - bool hasDecimal = false; - for (size_t i = 0; i < operand.length(); i++) - { - if (operand[i] == L'.') - { - hasDecimal = true; - } - } + auto operandWstring = wstring(operand->Data()); + const bool hasDecimal = operandWstring.find('.') != wstring::npos; if (hasDecimal) { - if (operand.length() >= 2) + if (operandWstring.length() >= 2) { - if ((operand[0] == L'0') && (operand[1] == L'.')) + if ((operandWstring[0] == L'0') && (operandWstring[1] == L'.')) { - return operand.length() - 2; + return static_cast(operandWstring.length() - 2); } else { - return operand.length() - 1; + return static_cast(operandWstring.length() - 1); } } } - return operand.length(); + return static_cast(operandWstring.length()); } -size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int numberBase) +ULONG32 CopyPasteManager::ProgrammerOperandLength(Platform::String ^ operand, NumberBase numberBase) { - size_t len = operand.length(); - vector prefixes{}; vector suffixes{}; switch (numberBase) { - case BinBase: + case NumberBase::BinBase: prefixes = { L"0B", L"0Y" }; suffixes = { L"B" }; break; - case DecBase: + case NumberBase::DecBase: prefixes = { L"-", L"0N" }; break; - case OctBase: + case NumberBase::OctBase: prefixes = { L"0T", L"0O" }; break; - case HexBase: + case NumberBase::HexBase: prefixes = { L"0X" }; suffixes = { L"H" }; break; default: // No defined prefixes/suffixes - break; + return 0; } // UInt suffixes are common across all modes const array uintSuffixes = { L"ULL", L"UL", L"LL", L"U", L"L" }; suffixes.insert(suffixes.end(), uintSuffixes.begin(), uintSuffixes.end()); - wstring operandUpper = operand; + wstring operandUpper = wstring(operand->Data()); transform(operandUpper.begin(), operandUpper.end(), operandUpper.begin(), towupper); + size_t len = operand->Length(); + // Detect if there is a suffix and subtract its length // Check suffixes first to allow e.g. "0b" to result in length 1 (value 0), rather than length 0 (no value). - for (const wstring& suffix : suffixes) + for (const auto& suffix : suffixes) { if (len < suffix.length()) { @@ -551,7 +572,7 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num } // Detect if there is a prefix and subtract its length - for (const wstring& prefix : prefixes) + for (const auto& prefix : prefixes) { if (len < prefix.length()) { @@ -565,5 +586,30 @@ size_t CopyPasteManager::ProgrammerOperandLength(const wstring& operand, int num } } - return len; + return static_cast(len); +} + +// return wstring after removing characters like space, comma, double quotes, and monetary prefix currency symbols supported by the Windows keyboard: +// yen or yuan(¥) - 165 +// unspecified currency sign(¤) - 164 +// Ghanaian cedi(₵) - 8373 +// dollar or peso($) - 36 +// colón(₡) - 8353 +// won(₩) - 8361 +// shekel(₪) - 8362 +// naira(₦) - 8358 +// 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, 160 }; + input = CalculatorApp::Common::LocalizationSettings::GetInstance().RemoveGroupSeparators(input); + return ref new String(Utils::RemoveUnwantedCharsFromString(input->Data(), unWantedChars).c_str()); +} + +bool CopyPasteManager::IsErrorMessage(Platform::String ^ message) +{ + return message == PasteErrorString; } diff --git a/src/CalcViewModel/Common/CopyPasteManager.h b/src/CalcViewModel/Common/CopyPasteManager.h index 1da4af4e..b8850d4c 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.h +++ b/src/CalcViewModel/Common/CopyPasteManager.h @@ -5,6 +5,8 @@ #include "AppResourceProvider.h" #include "NavCategory.h" +#include "BitLength.h" +#include "NumberBase.h" namespace CalculatorUnitTests { @@ -13,62 +15,119 @@ namespace CalculatorUnitTests namespace CalculatorApp { +public + value struct CopyPasteMaxOperandLengthAndValue + { + unsigned int maxLength; + unsigned long long maxValue; + }; -#define QwordType 1 -#define DwordType 2 -#define WordType 3 -#define ByteType 4 -#define HexBase 5 -#define DecBase 6 -#define OctBase 7 -#define BinBase 8 - - class CopyPasteManager + public ref class CopyPasteManager sealed { public: - static void CopyToClipboard(Platform::String^ stringToCopy); - static concurrency::task GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); - static bool HasStringToPaste() + static void CopyToClipboard(Platform::String ^ stringToCopy); + static Windows::Foundation::IAsyncOperation< + Platform::String + ^> ^ GetStringToPaste(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, CalculatorApp::Common::NumberBase programmerNumberBase, CalculatorApp::Common::BitLength bitLengthType); + static bool HasStringToPaste(); + static bool IsErrorMessage(Platform::String ^ message); + static property unsigned int MaxPasteableLength { - return ClipboardTextFormat() >= 0; + unsigned int get() + { + return MaxPasteableLengthValue; + } + } + static property unsigned int MaxOperandCount + { + unsigned int get() + { + return MaxOperandCountValue; + } + } + static property unsigned int MaxStandardOperandLength + { + unsigned int get() + { + return MaxStandardOperandLengthValue; + } + } + static property unsigned int MaxScientificOperandLength + { + unsigned int get() + { + return MaxScientificOperandLengthValue; + } } - static constexpr auto PasteErrorString = L"NoOp"; + static property unsigned int MaxConverterInputLength + { + unsigned int get() + { + return MaxConverterInputLengthValue; + } + } - private: - static int ClipboardTextFormat(); - static Platform::String^ ValidatePasteExpression( - Platform::String^ pastedText, - CalculatorApp::Common::ViewMode mode, - int programmerNumberBase, - int bitLengthType); - static Platform::String^ ValidatePasteExpression( - Platform::String^ pastedText, + static property unsigned int MaxExponentLength + { + unsigned int get() + { + return MaxExponentLengthValue; + } + } + + static property unsigned int MaxProgrammerBitLength + { + unsigned int get() + { + return MaxProgrammerBitLengthValue; + } + } + + static Platform::String + ^ ValidatePasteExpression( + Platform::String ^ pastedText, + CalculatorApp::Common::ViewMode mode, + CalculatorApp::Common::NumberBase programmerNumberBase, + CalculatorApp::Common::BitLength bitLengthType); + static Platform::String + ^ ValidatePasteExpression( + Platform::String ^ pastedText, + CalculatorApp::Common::ViewMode mode, + CalculatorApp::Common::CategoryGroupType modeType, + CalculatorApp::Common::NumberBase programmerNumberBase, + CalculatorApp::Common::BitLength bitLengthType); + static CopyPasteMaxOperandLengthAndValue GetMaxOperandLengthAndValue( CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, - int programmerNumberBase, - int bitLengthType); + CalculatorApp::Common::NumberBase programmerNumberBase, + CalculatorApp::Common::BitLength bitLengthType); + static Windows::Foundation::Collections::IVector< + Platform::String ^> ^ ExtractOperands(Platform::String ^ pasteExpression, CalculatorApp::Common::ViewMode mode); + static bool ExpressionRegExMatch( + Windows::Foundation::Collections::IVector ^ operands, + CalculatorApp::Common::ViewMode mode, + CalculatorApp::Common::CategoryGroupType modeType, + CalculatorApp::Common::NumberBase programmerNumberBase, + CalculatorApp::Common::BitLength bitLengthType); + static Platform::String ^ SanitizeOperand(Platform::String ^ operand); + static Platform::String ^ RemoveUnwantedCharsFromString(Platform::String ^ input); + static Platform::IBox ^ TryOperandToULL(Platform::String ^ operand, CalculatorApp::Common::NumberBase numberBase); + static ULONG32 StandardScientificOperandLength(Platform::String ^ operand); + static ULONG32 OperandLength( + Platform::String ^ operand, + CalculatorApp::Common::ViewMode mode, + CalculatorApp::Common::CategoryGroupType modeType, + CalculatorApp::Common::NumberBase programmerNumberBase); + static ULONG32 ProgrammerOperandLength(Platform::String ^ operand, CalculatorApp::Common::NumberBase numberBase); - static std::vector ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, int programmerNumberBase = -1, int bitLengthType = -1); - static bool ExpressionRegExMatch(std::vector operands, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); - - static std::pair GetMaxOperandLengthAndValue(CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, int bitLengthType = -1); - static std::wstring SanitizeOperand(const std::wstring& operand); - static bool TryOperandToULL(const std::wstring& operand, int numberBase, unsigned long long int& result); - static size_t OperandLength(std::wstring operand, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1); - static size_t StandardScientificOperandLength(std::wstring operand); - static size_t ProgrammerOperandLength(const std::wstring& operand, int numberBase); - - static constexpr size_t MaxStandardOperandLength = 16; - static constexpr size_t MaxScientificOperandLength = 32; - static constexpr size_t MaxConverterInputLength = 16; - static constexpr size_t MaxOperandCount = 100; - static constexpr size_t MaxPasteableLength = 512; - static constexpr size_t MaxExponentLength = 4; - static constexpr size_t MaxProgrammerBitLength = 64; - - static Platform::String^ supportedFormats[]; - - friend class CalculatorUnitTests::CopyPasteManagerTest; + private: + static constexpr size_t MaxStandardOperandLengthValue = 16; + static constexpr size_t MaxScientificOperandLengthValue = 32; + static constexpr size_t MaxConverterInputLengthValue = 16; + static constexpr size_t MaxOperandCountValue = 100; + static constexpr size_t MaxExponentLengthValue = 4; + static constexpr size_t MaxProgrammerBitLengthValue = 64; + static constexpr size_t MaxPasteableLengthValue = 512; }; } diff --git a/src/CalcViewModel/Common/DateCalculator.cpp b/src/CalcViewModel/Common/DateCalculator.cpp index f00ae3c1..35df7231 100644 --- a/src/CalcViewModel/Common/DateCalculator.cpp +++ b/src/CalcViewModel/Common/DateCalculator.cpp @@ -9,20 +9,38 @@ using namespace Windows::Foundation; using namespace Windows::Globalization; using namespace CalculatorApp::Common::DateCalculation; -DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier) +bool operator==(const DateDifference& l, const DateDifference& r) +{ + return l.year == r.year && l.month == r.month && l.week == r.week && l.day == r.day; +} + +DateCalculationEngine::DateCalculationEngine(_In_ String ^ calendarIdentifier) { m_calendar = ref new Calendar(); + m_calendar->ChangeTimeZone("UTC"); m_calendar->ChangeCalendarSystem(calendarIdentifier); } // Adding Duration to a Date // Returns: True if function succeeds to calculate the date else returns False -bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate) +IBox ^ DateCalculationEngine::AddDuration(DateTime startDate, DateDifference duration) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); try { m_calendar->SetDateTime(startDate); + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results + // in a date in Heisei 31. To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date + // math, and then convert back to the Japanese era system. This works because the Japanese era system maintains the same year/month boundaries and + // durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + if (duration.year != 0) { m_calendar->AddYears(duration.year); @@ -36,27 +54,42 @@ bool DateCalculationEngine::AddDuration(_In_ DateTime startDate, _In_ const Date m_calendar->AddDays(duration.day); } - *endDate = m_calendar->GetDateTime(); + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + return m_calendar->GetDateTime(); } - catch (Platform::InvalidArgumentException^ ex) + catch (Platform::InvalidArgumentException ^ ex) { - // Do nothing - return false; - } + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); - return true; + // Do nothing + return nullptr; + } } // Subtracting Duration from a Date // Returns: True if function succeeds to calculate the date else returns False -bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const DateDifference& duration, _Out_ DateTime *endDate) +IBox ^ DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ DateDifference duration) { + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + // For Subtract the Algorithm is different than Add. Here the smaller units are subtracted first // and then the larger units. try { m_calendar->SetDateTime(startDate); + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results + // in a date in Heisei 31. To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date + // math, and then convert back to the Japanese era system. This works because the Japanese era system maintains the same year/month boundaries and + // durations as the Gregorian system and is only different in display value. + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + if (duration.day != 0) { m_calendar->AddDays(-duration.day); @@ -69,21 +102,31 @@ bool DateCalculationEngine::SubtractDuration(_In_ DateTime startDate, _In_ const { m_calendar->AddYears(-duration.year); } + m_calendar->ChangeCalendarSystem(currentCalendarSystem); - *endDate = m_calendar->GetDateTime(); + auto dateTime = m_calendar->GetDateTime(); + // Check that the UniversalTime value is not negative + if (dateTime.UniversalTime >= 0) + { + return dateTime; + } + else + { + return nullptr; + } } - catch (Platform::InvalidArgumentException^ ex) + catch (Platform::InvalidArgumentException ^ ex) { - // Do nothing - return false; - } + // ensure that we revert to the correct calendar system + m_calendar->ChangeCalendarSystem(currentCalendarSystem); - // Check that the UniversalTime value is not negative - return (endDate->UniversalTime >= 0); + // Do nothing + return nullptr; + } } // Calculate the difference between two dates -void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference) +IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime date1, _In_ DateTime date2, _In_ DateUnit outputFormat) { DateTime startDate; DateTime endDate; @@ -115,8 +158,7 @@ void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime UINT approximateDaysInYear; // If we're unable to calculate the days-in-month or days-in-year, we'll leave the values at 0. - if (TryGetCalendarDaysInMonth(startDate, daysInMonth) - && TryGetCalendarDaysInYear(endDate, approximateDaysInYear)) + if (TryGetCalendarDaysInMonth(startDate, daysInMonth) && TryGetCalendarDaysInYear(endDate, approximateDaysInYear)) { UINT daysIn[c_unitsOfDate] = { approximateDaysInYear, daysInMonth, c_daysInWeek, 1 }; @@ -138,11 +180,11 @@ void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime { pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); } - catch (Platform::InvalidArgumentException^) + catch (Platform::InvalidArgumentException ^) { // Operation failed due to out of bound result - // Do nothing - differenceInDates[unitIndex] = 0; + // For example: 31st Dec, 9999 - last valid date + return nullptr; } } @@ -155,6 +197,11 @@ void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime if (tempDaysDiff < 0) { // pivotDate has gone over the end date; start from the beginning of this unit + if (differenceInDates[unitIndex] == 0) + { + // differenceInDates[unitIndex] is unsigned, the value can't be negative + return nullptr; + } differenceInDates[unitIndex] -= 1; pivotDate = tempPivotDate; pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); @@ -171,21 +218,28 @@ void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime // pivotDate is still below the end date try { - pivotDate = AdjustCalendarDate(pivotDate, dateUnit, 1); + pivotDate = AdjustCalendarDate(tempPivotDate, dateUnit, static_cast(differenceInDates[unitIndex] + 1)); differenceInDates[unitIndex] += 1; } - catch (Platform::InvalidArgumentException^) + catch (Platform::InvalidArgumentException ^) { - // handling for 31st Dec, 9999 last valid date - // Do nothing - break out - break; + // Operation failed due to out of bound result + // For example: 31st Dec, 9999 - last valid date + return nullptr; } } } while (tempDaysDiff != 0); // dates are the same - exit the loop tempPivotDate = AdjustCalendarDate(tempPivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); pivotDate = tempPivotDate; - daysDiff = GetDifferenceInDays(pivotDate, endDate); + int signedDaysDiff = GetDifferenceInDays(pivotDate, endDate); + if (signedDaysDiff < 0) + { + // daysDiff is unsigned, the value can't be negative + return nullptr; + } + + daysDiff = signedDaysDiff; } } } @@ -193,13 +247,14 @@ void DateCalculationEngine::GetDateDifference(_In_ DateTime date1, _In_ DateTime differenceInDates[3] = daysDiff; - difference->year = differenceInDates[0]; - difference->month = differenceInDates[1]; - difference->week = differenceInDates[2]; - difference->day = differenceInDates[3]; + DateDifference result; + result.year = differenceInDates[0]; + result.month = differenceInDates[1]; + result.week = differenceInDates[2]; + result.day = differenceInDates[3]; + return result; } - // Private Methods // Gets number of days between the two date time values @@ -273,34 +328,34 @@ bool DateCalculationEngine::TryGetCalendarDaysInYear(_In_ DateTime date, _Out_ U // Adds/Subtracts certain value for a particular date unit DateTime DateCalculationEngine::AdjustCalendarDate(Windows::Foundation::DateTime date, DateUnit dateUnit, int difference) { - m_calendar->SetDateTime(date); + // The Japanese Era system can have multiple year partitions within the same year. + // For example, April 30, 2019 is denoted April 30, Heisei 31; May 1, 2019 is denoted as May 1, Reiwa 1. + // The Calendar treats Heisei 31 and Reiwa 1 as separate years, which results in some unexpected behaviors where subtracting a year from Reiwa 1 results in + // a date in Heisei 31. To provide the expected result across era boundaries, we first convert the Japanese era system to a Gregorian system, do date math, + // and then convert back to the Japanese era system. This works because the Japanese era system maintains the same year/month boundaries and durations as + // the Gregorian system and is only different in display value. + auto currentCalendarSystem = m_calendar->GetCalendarSystem(); + if (currentCalendarSystem == CalendarIdentifiers::Japanese) + { + m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); + } + switch (dateUnit) { - case DateUnit::Year: - { - // In the Japanese calendar, transition years have 2 partial years. - // It is not guaranteed that adding 1 year will always add 365 days in the Japanese Calendar. - // To work around this quirk, we will change the calendar system to Gregorian before adding 1 year in the Japanese Calendar case only. - // We will then return the calendar system back to the Japanese Calendar. - auto currentCalendarSystem = m_calendar->GetCalendarSystem(); - if (currentCalendarSystem == CalendarIdentifiers::Japanese) - { - m_calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian); - } - - m_calendar->AddYears(difference); - m_calendar->ChangeCalendarSystem(currentCalendarSystem); - break; - } - case DateUnit::Month: - m_calendar->AddMonths(difference); - break; - case DateUnit::Week: - m_calendar->AddWeeks(difference); - break; + case DateUnit::Year: + m_calendar->AddYears(difference); + break; + case DateUnit::Month: + m_calendar->AddMonths(difference); + break; + case DateUnit::Week: + m_calendar->AddWeeks(difference); + break; } + m_calendar->ChangeCalendarSystem(currentCalendarSystem); + return m_calendar->GetDateTime(); } diff --git a/src/CalcViewModel/Common/DateCalculator.h b/src/CalcViewModel/Common/DateCalculator.h index b755e2ee..7c9b6c4d 100644 --- a/src/CalcViewModel/Common/DateCalculator.h +++ b/src/CalcViewModel/Common/DateCalculator.h @@ -1,16 +1,16 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once -const ULONGLONG c_millisecond = 10000; -const ULONGLONG c_second = 1000 * c_millisecond; -const ULONGLONG c_minute = 60 * c_second; -const ULONGLONG c_hour = 60 * c_minute; -const ULONGLONG c_day = 24 * c_hour; +const uint64_t c_millisecond = 10000; +const uint64_t c_second = 1000 * c_millisecond; +const uint64_t c_minute = 60 * c_second; +const uint64_t c_hour = 60 * c_minute; +const uint64_t c_day = 24 * c_hour; -const int c_unitsOfDate = 4; // Units Year,Month,Week,Day -const int c_unitsGreaterThanDays = 3; // Units Greater than Days (Year/Month/Week) 3 +const int c_unitsOfDate = 4; // Units Year,Month,Week,Day +const int c_unitsGreaterThanDays = 3; // Units Greater than Days (Year/Month/Week) 3 const int c_daysInWeek = 7; namespace CalculatorApp @@ -19,7 +19,8 @@ namespace CalculatorApp { namespace DateCalculation { - public enum class _Enum_is_bitflag_ DateUnit + public + enum class _Enum_is_bitflag_ DateUnit { Year = 0x01, Month = 0x02, @@ -28,28 +29,34 @@ namespace CalculatorApp }; // Struct to store the difference between two Dates in the form of Years, Months , Weeks - struct DateDifference + public + value struct DateDifference { - int year = 0; - int month = 0; - int week = 0; - int day = 0; + int year; + int month; + int week; + int day; }; - class DateCalculationEngine + const DateDifference DateDifferenceUnknown{ INT_MIN, INT_MIN, INT_MIN, INT_MIN }; + + public + ref class DateCalculationEngine sealed { public: // Constructor - DateCalculationEngine(_In_ Platform::String^ calendarIdentifier); + DateCalculationEngine(_In_ Platform::String ^ calendarIdentifier); // Public Methods - bool __nothrow AddDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate); - bool __nothrow SubtractDuration(_In_ Windows::Foundation::DateTime startDate, _In_ const DateDifference& duration, _Out_ Windows::Foundation::DateTime *endDate); - void __nothrow GetDateDifference(_In_ Windows::Foundation::DateTime date1, _In_ Windows::Foundation::DateTime date2, _In_ DateUnit outputFormat, _Out_ DateDifference *difference); + + Platform::IBox ^ AddDuration(_In_ Windows::Foundation::DateTime startDate, _In_ DateDifference duration); + Platform::IBox ^ SubtractDuration(_In_ Windows::Foundation::DateTime startDate, _In_ DateDifference duration); + Platform::IBox< + DateDifference> ^ TryGetDateDifference(_In_ Windows::Foundation::DateTime date1, _In_ Windows::Foundation::DateTime date2, _In_ DateUnit outputFormat); private: // Private Variables - Windows::Globalization::Calendar^ m_calendar; + Windows::Globalization::Calendar ^ m_calendar; // Private Methods int GetDifferenceInDays(Windows::Foundation::DateTime date1, Windows::Foundation::DateTime date2); @@ -60,3 +67,5 @@ namespace CalculatorApp } } } + +bool operator==(const CalculatorApp::Common::DateCalculation::DateDifference& l, const CalculatorApp::Common::DateCalculation::DateDifference& r); diff --git a/src/CalcViewModel/Common/DelegateCommand.h b/src/CalcViewModel/Common/DelegateCommand.h index ba29bb7c..3a341287 100644 --- a/src/CalcViewModel/Common/DelegateCommand.h +++ b/src/CalcViewModel/Common/DelegateCommand.h @@ -8,33 +8,33 @@ namespace CalculatorApp namespace Common { template - ref class DelegateCommand: public Windows::UI::Xaml::Input::ICommand + ref class DelegateCommand : public Windows::UI::Xaml::Input::ICommand { - internal: + internal : - typedef void (TTarget::*CommandHandlerFunc)(Platform::Object^); + typedef void (TTarget::*CommandHandlerFunc)(Platform::Object ^); - DelegateCommand(TTarget^ target, CommandHandlerFunc func): - m_weakTarget(target), - m_function(func) - { } + DelegateCommand(TTarget ^ target, CommandHandlerFunc func) + : m_weakTarget(target) + , m_function(func) + { + } private: - // Explicit, and private, implementation of ICommand, this way of programming makes it so // the ICommand methods will only be available if the ICommand interface is requested via a dynamic_cast // The ICommand interface is meant to be consumed by Xaml and not by the app, this is a defensive measure against // code in the app calling Execute. - virtual void ExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::Execute + virtual void ExecuteImpl(Platform::Object ^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::Execute { - TTarget^ target = m_weakTarget.Resolve(); + TTarget ^ target = m_weakTarget.Resolve(); if (target) { (target->*m_function)(parameter); } } - virtual bool CanExecuteImpl(Platform::Object^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::CanExecute + virtual bool CanExecuteImpl(Platform::Object ^ parameter) sealed = Windows::UI::Xaml::Input::ICommand::CanExecute { return true; } @@ -57,14 +57,12 @@ namespace CalculatorApp CommandHandlerFunc m_function; Platform::WeakReference m_weakTarget; - }; template - DelegateCommand^ MakeDelegate(TTarget^ target, TFuncPtr&& function) - { - return ref new DelegateCommand(target, std::forward(function)); - } + DelegateCommand ^ MakeDelegate(TTarget ^ target, TFuncPtr&& function) { + return ref new DelegateCommand(target, std::forward(function)); + } } } diff --git a/src/CalcViewModel/Common/DisplayExpressionToken.h b/src/CalcViewModel/Common/DisplayExpressionToken.h index 7ba811c5..aaaaf37c 100644 --- a/src/CalcViewModel/Common/DisplayExpressionToken.h +++ b/src/CalcViewModel/Common/DisplayExpressionToken.h @@ -7,30 +7,40 @@ namespace CalculatorApp::Common { - public enum class TokenType +public + enum class TokenType { Operator, Operand, Separator }; - [Windows::UI::Xaml::Data::Bindable] - public ref class DisplayExpressionToken sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class DisplayExpressionToken sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - internal: - DisplayExpressionToken(Platform::String^ token, int tokenPosition, bool fEditable, TokenType type) : - m_Token(token), m_TokenPosition(tokenPosition), m_IsTokenEditable(fEditable), m_Type(type), m_OriginalToken(token), m_InEditMode(false) - {} + internal : DisplayExpressionToken(Platform::String ^ token, int tokenPosition, bool fEditable, TokenType type) + : m_Token(token) + , m_TokenPosition(tokenPosition) + , m_IsTokenEditable(fEditable) + , m_Type(type) + , m_OriginalToken(token) + , m_InEditMode(false) + { + } + public: OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_RW(Platform::String^, Token); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Token); OBSERVABLE_PROPERTY_RW(int, TokenPosition); OBSERVABLE_PROPERTY_RW(bool, IsTokenEditable); OBSERVABLE_PROPERTY_RW(int, CommandIndex); - OBSERVABLE_PROPERTY_R(Platform::String^, OriginalToken); + OBSERVABLE_PROPERTY_R(Platform::String ^, OriginalToken); - property bool IsTokenInEditMode { - bool get() { return m_InEditMode; } + property bool IsTokenInEditMode + { + bool get() + { + return m_InEditMode; + } void set(bool val) { if (!val) @@ -40,8 +50,8 @@ namespace CalculatorApp::Common m_InEditMode = val; } } - internal: - OBSERVABLE_PROPERTY_RW(TokenType, Type); + internal : OBSERVABLE_PROPERTY_RW(TokenType, Type); + private: bool m_InEditMode; }; diff --git a/src/CalcViewModel/Common/EngineResourceProvider.cpp b/src/CalcViewModel/Common/EngineResourceProvider.cpp index 657a2637..05faff9f 100644 --- a/src/CalcViewModel/Common/EngineResourceProvider.cpp +++ b/src/CalcViewModel/Common/EngineResourceProvider.cpp @@ -17,7 +17,7 @@ namespace CalculatorApp m_resLoader = ResourceLoader::GetForViewIndependentUse("CEngineStrings"); } - wstring EngineResourceProvider::GetCEngineString(const wstring& id) + wstring EngineResourceProvider::GetCEngineString(wstring_view id) { const auto& localizationSettings = LocalizationSettings::GetInstance(); @@ -43,8 +43,8 @@ namespace CalculatorApp return numberGroupingString; } - StringReference idRef(id.c_str()); - String^ str = m_resLoader->GetString(idRef); + StringReference idRef(id.data(), id.length()); + String ^ str = m_resLoader->GetString(idRef); return str->Begin(); } } diff --git a/src/CalcViewModel/Common/EngineResourceProvider.h b/src/CalcViewModel/Common/EngineResourceProvider.h index 83b3f418..f4620c5c 100644 --- a/src/CalcViewModel/Common/EngineResourceProvider.h +++ b/src/CalcViewModel/Common/EngineResourceProvider.h @@ -11,9 +11,9 @@ namespace CalculatorApp { public: EngineResourceProvider(); - virtual std::wstring GetCEngineString(const std::wstring& id) override; + virtual std::wstring GetCEngineString(std::wstring_view id) override; private: - Windows::ApplicationModel::Resources::ResourceLoader^ m_resLoader; + Windows::ApplicationModel::Resources::ResourceLoader ^ m_resLoader; }; } diff --git a/src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp b/src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp index 09bdcc31..509a1659 100644 --- a/src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp +++ b/src/CalcViewModel/Common/ExpressionCommandDeserializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -7,7 +7,10 @@ using namespace CalculatorApp::Common; using namespace Windows::Storage::Streams; -CommandDeserializer::CommandDeserializer(_In_ DataReader^ dataReader) :m_dataReader(dataReader){} +CommandDeserializer::CommandDeserializer(_In_ DataReader ^ dataReader) + : m_dataReader(dataReader) +{ +} std::shared_ptr CommandDeserializer::Deserialize(_In_ CalculationManager::CommandType cmdType) { @@ -20,7 +23,7 @@ std::shared_ptr CommandDeserializer::Deserialize(_In_ Calcul case CalculationManager::CommandType::Parentheses: - return std::make_shared(DeserializeParentheses()); + return std::make_shared(DeserializeParentheses()); break; case CalculationManager::CommandType::UnaryCommand: @@ -44,13 +47,13 @@ COpndCommand CommandDeserializer::DeserializeOperand() bool fDecimal = m_dataReader->ReadBoolean(); bool fSciFmt = m_dataReader->ReadBoolean(); - std::shared_ptr> cmdVector = std::make_shared>(); + std::shared_ptr> cmdVector = std::make_shared>(); auto cmdVectorSize = m_dataReader->ReadUInt32(); for (unsigned int j = 0; j < cmdVectorSize; ++j) { int eachOpndcmd = m_dataReader->ReadInt32(); - cmdVector->Append(eachOpndcmd); + cmdVector->push_back(eachOpndcmd); } return COpndCommand(cmdVector, fNegative, fDecimal, fSciFmt); @@ -65,7 +68,6 @@ CParentheses CommandDeserializer::DeserializeParentheses() CUnaryCommand CommandDeserializer::DeserializeUnary() { auto cmdSize = m_dataReader->ReadUInt32(); - std::shared_ptr> cmdVector = std::make_shared>(); if (cmdSize == 1) { diff --git a/src/CalcViewModel/Common/ExpressionCommandDeserializer.h b/src/CalcViewModel/Common/ExpressionCommandDeserializer.h index d48f7e29..833b6bf6 100644 --- a/src/CalcViewModel/Common/ExpressionCommandDeserializer.h +++ b/src/CalcViewModel/Common/ExpressionCommandDeserializer.h @@ -12,11 +12,11 @@ namespace CalculatorApp class CommandDeserializer { public: - CommandDeserializer(_In_ Windows::Storage::Streams::DataReader^ dataReader); + CommandDeserializer(_In_ Windows::Storage::Streams::DataReader ^ dataReader); std::shared_ptr Deserialize(_In_ CalculationManager::CommandType cmdType); private: - Windows::Storage::Streams::DataReader^ m_dataReader; + Windows::Storage::Streams::DataReader ^ m_dataReader; COpndCommand DeserializeOperand(); CParentheses DeserializeParentheses(); CUnaryCommand DeserializeUnary(); diff --git a/src/CalcViewModel/Common/ExpressionCommandSerializer.cpp b/src/CalcViewModel/Common/ExpressionCommandSerializer.cpp index 38bae50f..fb0cd83a 100644 --- a/src/CalcViewModel/Common/ExpressionCommandSerializer.cpp +++ b/src/CalcViewModel/Common/ExpressionCommandSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -7,49 +7,44 @@ using namespace CalculatorApp::Common; using namespace Windows::Storage::Streams; -SerializeCommandVisitor::SerializeCommandVisitor(_In_ DataWriter^ dataWriter) :m_dataWriter(dataWriter) +SerializeCommandVisitor::SerializeCommandVisitor(_In_ DataWriter ^ dataWriter) + : m_dataWriter(dataWriter) { } -void SerializeCommandVisitor::Visit(_In_ COpndCommand &opndCmd) +void SerializeCommandVisitor::Visit(_In_ COpndCommand& opndCmd) { m_dataWriter->WriteBoolean(opndCmd.IsNegative()); m_dataWriter->WriteBoolean(opndCmd.IsDecimalPresent()); m_dataWriter->WriteBoolean(opndCmd.IsSciFmt()); - auto opndCmds = opndCmd.GetCommands(); - unsigned int opndCmdSize; - opndCmds->GetSize(&opndCmdSize); + const auto& opndCmds = opndCmd.GetCommands(); + unsigned int opndCmdSize = static_cast(opndCmds->size()); m_dataWriter->WriteUInt32(opndCmdSize); - for (unsigned int j = 0; j < opndCmdSize; ++j) + for (int eachOpndcmd : *opndCmds) { - int eachOpndcmd; - opndCmds->GetAt(j, &eachOpndcmd); m_dataWriter->WriteInt32(eachOpndcmd); } } -void SerializeCommandVisitor::Visit(_In_ CUnaryCommand &unaryCmd) +void SerializeCommandVisitor::Visit(_In_ CUnaryCommand& unaryCmd) { - auto cmds = unaryCmd.GetCommands(); - unsigned int cmdSize; - cmds->GetSize(&cmdSize); + const auto& cmds = unaryCmd.GetCommands(); + unsigned int cmdSize = static_cast(cmds->size()); m_dataWriter->WriteUInt32(cmdSize); - for (unsigned int j = 0; j < cmdSize; ++j) + for (int eachOpndcmd : *cmds) { - int eachOpndcmd; - cmds->GetAt(j, &eachOpndcmd); m_dataWriter->WriteInt32(eachOpndcmd); } } -void SerializeCommandVisitor::Visit(_In_ CBinaryCommand &binaryCmd) +void SerializeCommandVisitor::Visit(_In_ CBinaryCommand& binaryCmd) { int cmd = binaryCmd.GetCommand(); m_dataWriter->WriteInt32(cmd); } -void SerializeCommandVisitor::Visit(_In_ CParentheses ¶Cmd) +void SerializeCommandVisitor::Visit(_In_ CParentheses& paraCmd) { int parenthesisCmd = paraCmd.GetCommand(); m_dataWriter->WriteInt32(parenthesisCmd); diff --git a/src/CalcViewModel/Common/ExpressionCommandSerializer.h b/src/CalcViewModel/Common/ExpressionCommandSerializer.h index 36203cb7..23303fe7 100644 --- a/src/CalcViewModel/Common/ExpressionCommandSerializer.h +++ b/src/CalcViewModel/Common/ExpressionCommandSerializer.h @@ -12,15 +12,15 @@ namespace CalculatorApp class SerializeCommandVisitor : public ISerializeCommandVisitor { public: - SerializeCommandVisitor(_In_ Windows::Storage::Streams::DataWriter^ dataWriter); + SerializeCommandVisitor(_In_ Windows::Storage::Streams::DataWriter ^ dataWriter); - void Visit(_In_ COpndCommand &opndCmd); - void Visit(_In_ CUnaryCommand &unaryCmd); - void Visit(_In_ CBinaryCommand &binaryCmd); - void Visit(_In_ CParentheses ¶Cmd); + void Visit(_In_ COpndCommand& opndCmd); + void Visit(_In_ CUnaryCommand& unaryCmd); + void Visit(_In_ CBinaryCommand& binaryCmd); + void Visit(_In_ CParentheses& paraCmd); private: - Windows::Storage::Streams::DataWriter^ m_dataWriter; + Windows::Storage::Streams::DataWriter ^ m_dataWriter; }; } } diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp deleted file mode 100644 index d7b87c96..00000000 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp +++ /dev/null @@ -1,877 +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> s_CharacterForButtons; -static multimap> s_VirtualKeysForButtons; -static multimap> s_VirtualKeyControlChordsForButtons; -static multimap> s_VirtualKeyShiftChordsForButtons; -static multimap> s_VirtualKeyAltChordsForButtons; -static multimap> s_VirtualKeyControlShiftChordsForButtons; -static multimap> s_VirtualKeyInverseChordsForButtons; -static multimap> s_VirtualKeyControlInverseChordsForButtons; - -static multimap s_ShiftKeyPressed; -static multimap s_ControlKeyPressed; -static multimap s_ShiftButtonChecked; -static multimap 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 - void LightUpButtons(const T& buttons) - { - auto iterator = buttons.first; - for (; iterator != buttons.second; ++iterator) - { - auto button = iterator->second.Resolve(); - 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(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( - [buttonWeakReference, timerWeakReference](Object^, Object^) - { - auto button = buttonWeakReference.Resolve(); - if (button) - { - VisualStateManager::GoToState(button, "Normal", true); - } - - // Cancel the timer after we're done so it only fires once - auto timer = timerWeakReference.Resolve(); - 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 - void RunFirstEnabledButtonCommand(const T& buttons) - { - auto buttonIterator = buttons.first; - for (; buttonIterator != buttons.second; ++buttonIterator) - { - auto button = buttonIterator->second.Resolve(); - 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(button); - if (radio) - { - radio->IsChecked = true; - return; - } - - auto toggle = dynamic_cast(button); - if (toggle) - { - toggle->IsChecked = !toggle->IsChecked->Value; - return; - } - } - } - } -} - -static multimap s_ignoreNextEscape; -static multimap s_keepIgnoringEscape; -static multimap s_fHonorShortcuts; -static multimap s_fHandledEnter; -static multimap 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(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())); - - 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(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())); - 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(target); - - if (control == nullptr) - { - // Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case - control = safe_cast(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())); - 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(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())); - 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(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())); - 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(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())); - 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(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())); - 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(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())); - 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(args->KeyCode); - auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character); - - RunFirstEnabledButtonCommand(buttons); - - LightUpButtons(buttons); - } - } -} - -const std::multimap& 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(key)); - auto buttons = lookupMap.equal_range(static_cast(key)); - auto navView = buttons.first->second.Resolve(); - auto appViewModel = safe_cast(navView->DataContext); - appViewModel->Mode = ViewMode::Date; - auto categoryName = AppResourceProvider::GetInstance().GetResourceString(L"DateCalculationModeText"); - appViewModel->CategoryName = categoryName; - - auto menuItems = static_cast^>(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(key)); - auto buttons = lookupMap.equal_range(static_cast(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(key), altPressed); - auto listItems = lookupMap.equal_range(static_cast(key)); - for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator) - { - auto item = listIterator->second.Resolve(); - if (item != nullptr) - { - auto navView = safe_cast (item); - - auto menuItems = static_cast^>(navView->MenuItemsSource); - if (menuItems != nullptr) - { - auto vm = safe_cast(navView->DataContext); - if (nullptr != vm) - { - ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast(key)); - if (NavCategory::IsValidViewMode(toMode)) - { - vm->Mode = toMode; - navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode)); - } - } - } - break; - } - } - } - - if (args->VirtualKey == VirtualKey::Escape) - { - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_AboutFlyout.find(viewId); - - if ((iterViewMap != s_AboutFlyout.end()) && (iterViewMap->second != nullptr)) - { - iterViewMap->second->Hide(); - } - } -} - -void KeyboardShortcutManager::Initialize() -{ - auto coreWindow = Window::Current->CoreWindow; - coreWindow->CharacterReceived += - ref new TypedEventHandler(&KeyboardShortcutManager::OnCharacterReceivedHandler); - coreWindow->KeyDown += - ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyDownHandler); - coreWindow->KeyUp += - ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyUpHandler); - coreWindow->Dispatcher->AcceleratorKeyActivated += - ref new TypedEventHandler(&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())); - } - - if (s_VirtualKeysForButtons.find(appViewId) == s_VirtualKeysForButtons.end()) - { - s_VirtualKeysForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end()) - { - s_VirtualKeyControlChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end()) - { - s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end()) - { - s_VirtualKeyAltChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end()) - { - s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyInverseChordsForButtons.find(appViewId) == s_VirtualKeyInverseChordsForButtons.end()) - { - s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlInverseChordsForButtons.find(appViewId) == s_VirtualKeyControlInverseChordsForButtons.end()) - { - s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - 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); -} diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.h b/src/CalcViewModel/Common/KeyboardShortcutManager.h deleted file mode 100644 index 6e3f210b..00000000 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include "Utils.h" -#include "MyVirtualKey.h" - -namespace CalculatorApp -{ - namespace Common - { - public ref class KeyboardShortcutManager sealed - { - public: - KeyboardShortcutManager() {} - - DEPENDENCY_PROPERTY_OWNER(KeyboardShortcutManager); - - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(Platform::String^, Character); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKey); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyShiftChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyAltChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlShiftChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyInverseChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlInverseChord); - - internal: - - static void Initialize(); - - // Sometimes, like with popups, escape is treated as special and even - // though it is handled we get it passed through to us. In those cases - // we need to be able to ignore it (looking at e->Handled isn't sufficient - // because that always returns true). - // The onlyOnce flag is used to indicate whether we should only ignore the - // next escape, or keep ignoring until you explicitly HonorEscape. - static void IgnoreEscape(bool onlyOnce); - static void HonorEscape(); - static void HonorShortcuts(bool allow); - static void HandledEnter(bool ishandled); - static void UpdateDropDownState(bool); - static void ShiftButtonChecked(bool checked); - static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout^ aboutPageFlyout); - - static void RegisterNewAppViewId(); - static void OnWindowClosed(int viewId); - - private: - - static void OnCharacterPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - Platform::String^ oldValue, - Platform::String^ newValue); - - static void OnVirtualKeyPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyControlChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyShiftChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyInverseChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyControlInverseChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyAltChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnVirtualKeyControlShiftChordPropertyChanged( - Windows::UI::Xaml::DependencyObject^ target, - MyVirtualKey oldValue, - MyVirtualKey newValue); - - static void OnCharacterReceivedHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args); - static void OnKeyDownHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); - static void OnKeyUpHandler(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); - static void OnAcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher^, Windows::UI::Core::AcceleratorKeyEventArgs^ args); - }; - } -} diff --git a/src/CalcViewModel/Common/LocalizationService.cpp b/src/CalcViewModel/Common/LocalizationService.cpp index 25c2f8a6..c7fb9e0c 100644 --- a/src/CalcViewModel/Common/LocalizationService.cpp +++ b/src/CalcViewModel/Common/LocalizationService.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -33,16 +33,15 @@ DEPENDENCY_PROPERTY_INITIALIZATION(LocalizationService, FontSize); static reader_writer_lock s_locServiceInstanceLock; -LocalizationService^ LocalizationService::s_singletonInstance = nullptr; +LocalizationService ^ LocalizationService::s_singletonInstance = nullptr; // Resources for the engine use numbers as keys. It's inconvenient, but also difficult to // change given that the engine heavily relies on perfect ordering of certain elements. // The key for open parenthesis, '(', is "48". static constexpr auto s_openParenResourceKey = L"48"; -LocalizationService^ LocalizationService::GetInstance() +LocalizationService ^ LocalizationService::GetInstance() { - if (s_singletonInstance == nullptr) { // Writer lock for the static maps @@ -50,29 +49,61 @@ LocalizationService^ LocalizationService::GetInstance() if (s_singletonInstance == nullptr) { - s_singletonInstance = ref new LocalizationService(); + s_singletonInstance = ref new LocalizationService(nullptr); } } return s_singletonInstance; } -LocalizationService::LocalizationService() +/// +/// Replace (or create) the single instance of this singleton class by one with the language passed as parameter +/// +/// RFC-5646 identifier of the language to use +/// +/// Should only be used for test purpose +/// +void LocalizationService::OverrideWithLanguage(_In_ const wchar_t * const language) { - m_language = ApplicationLanguages::Languages->GetAt(0); - m_flowDirection = ResourceContext::GetForCurrentView()->QualifierValues->Lookup(L"LayoutDirection") + s_singletonInstance = ref new LocalizationService(language); +} + +/// +/// Constructor +/// +/// RFC-5646 identifier of the language to use, if null, will use the current language of the system +LocalizationService::LocalizationService(_In_ const wchar_t * const overridedLanguage) +{ + m_isLanguageOverrided = overridedLanguage != nullptr; + m_language = m_isLanguageOverrided ? ref new Platform::String(overridedLanguage) : ApplicationLanguages::Languages->GetAt(0); + m_flowDirection = ResourceContext::GetForViewIndependentUse()->QualifierValues->Lookup(L"LayoutDirection") != L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight; + wstring localeName = wstring(m_language->Data()); + localeName += L".UTF8"; + try + { + // Convert wstring to string for locale + int size_needed = WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), NULL, 0, NULL, NULL); + string localeNameStr(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), &localeNameStr[0], size_needed, NULL, NULL); + + m_locale = locale(localeNameStr.data()); + } + catch (...) + { + m_locale = locale(""); + } auto resourceLoader = AppResourceProvider::GetInstance(); - m_fontFamilyOverride = resourceLoader.GetResourceString(L"LocalizedFontFamilyOverride"); + m_fontFamilyOverride = resourceLoader->GetResourceString(L"LocalizedFontFamilyOverride"); - String^ reserved = L"RESERVED_FOR_FONTLOC"; + String ^ reserved = L"RESERVED_FOR_FONTLOC"; m_overrideFontApiValues = ((m_fontFamilyOverride != nullptr) && (m_fontFamilyOverride != reserved)); if (m_overrideFontApiValues) { - String^ localizedUICaptionFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUICaptionFontSizeFactorOverride"); - String^ localizedUITextFontSizeFactorOverride = resourceLoader.GetResourceString(L"LocalizedUITextFontSizeFactorOverride"); - String^ localizedFontWeightOverride = resourceLoader.GetResourceString(L"LocalizedFontWeightOverride"); + String ^ localizedUICaptionFontSizeFactorOverride = resourceLoader->GetResourceString(L"LocalizedUICaptionFontSizeFactorOverride"); + String ^ localizedUITextFontSizeFactorOverride = resourceLoader->GetResourceString(L"LocalizedUITextFontSizeFactorOverride"); + String ^ localizedFontWeightOverride = resourceLoader->GetResourceString(L"LocalizedFontWeightOverride"); // If any of the font overrides are modified then all of them need to be modified assert(localizedFontWeightOverride != reserved); @@ -87,7 +118,7 @@ LocalizationService::LocalizationService() m_fontGroup = ref new LanguageFontGroup(m_language); } -FontWeight LocalizationService::ParseFontWeight(String^ fontWeight) +FontWeight LocalizationService::ParseFontWeight(String ^ fontWeight) { wstring weight = fontWeight->Data(); transform(weight.begin(), weight.end(), weight.begin(), towlower); @@ -153,7 +184,7 @@ bool LocalizationService::IsRtlLayout() return m_flowDirection == FlowDirection::RightToLeft; } -String^ LocalizationService::GetLanguage() +String ^ LocalizationService::GetLanguage() { return m_language; } @@ -163,7 +194,7 @@ bool LocalizationService::GetOverrideFontApiValues() return m_overrideFontApiValues; } -FontFamily^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType fontType) +FontFamily ^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType fontType) { if (m_overrideFontApiValues) { @@ -175,7 +206,7 @@ FontFamily^ LocalizationService::GetLanguageFontFamilyForType(LanguageFontType f } } -LanguageFont^ LocalizationService::GetLanguageFont(LanguageFontType fontType) +LanguageFont ^ LocalizationService::GetLanguageFont(LanguageFontType fontType) { assert(!m_overrideFontApiValues); assert(m_fontGroup); @@ -191,7 +222,7 @@ LanguageFont^ LocalizationService::GetLanguageFont(LanguageFontType fontType) } } -String^ LocalizationService::GetFontFamilyOverride() +String ^ LocalizationService::GetFontFamilyOverride() { assert(m_overrideFontApiValues); return m_fontFamilyOverride; @@ -218,24 +249,24 @@ double LocalizationService::GetFontScaleFactorOverride(LanguageFontType fontType } } -void LocalizationService::OnFontTypePropertyChanged(DependencyObject^ target, LanguageFontType /*oldValue*/, LanguageFontType /*newValue*/) +void LocalizationService::OnFontTypePropertyChanged(DependencyObject ^ target, LanguageFontType /*oldValue*/, LanguageFontType /*newValue*/) { UpdateFontFamilyAndSize(target); } -void LocalizationService::OnFontWeightPropertyChanged(DependencyObject^ target, FontWeight /*oldValue*/, FontWeight /*newValue*/) +void LocalizationService::OnFontWeightPropertyChanged(DependencyObject ^ target, FontWeight /*oldValue*/, FontWeight /*newValue*/) { UpdateFontFamilyAndSize(target); } -void LocalizationService::OnFontSizePropertyChanged(DependencyObject^ target, double /*oldValue*/, double /*newValue*/) +void LocalizationService::OnFontSizePropertyChanged(DependencyObject ^ target, double /*oldValue*/, double /*newValue*/) { UpdateFontFamilyAndSize(target); } -void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) +void LocalizationService::UpdateFontFamilyAndSize(DependencyObject ^ target) { - FontFamily^ fontFamily; + FontFamily ^ fontFamily; FontWeight fontWeight; bool fOverrideFontWeight = false; double scaleFactor; @@ -259,7 +290,7 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) double sizeToUse = LocalizationService::GetFontSize(target) * scaleFactor; - auto control = dynamic_cast(target); + auto control = dynamic_cast(target); if (control) { control->FontFamily = fontFamily; @@ -278,7 +309,7 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) } else { - auto textBlock = dynamic_cast(target); + auto textBlock = dynamic_cast(target); if (textBlock) { textBlock->FontFamily = fontFamily; @@ -297,7 +328,7 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) } else { - RichTextBlock^ richTextBlock = dynamic_cast(target); + RichTextBlock ^ richTextBlock = dynamic_cast(target); if (richTextBlock) { richTextBlock->FontFamily = fontFamily; @@ -316,7 +347,7 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) } else { - TextElement^ textElement = dynamic_cast(target); + TextElement ^ textElement = dynamic_cast(target); if (textElement) { textElement->FontFamily = fontFamily; @@ -340,9 +371,9 @@ void LocalizationService::UpdateFontFamilyAndSize(DependencyObject^ target) // If successful, returns a formatter that respects the user's regional format settings, // as configured by running intl.cpl. -DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter() +DecimalFormatter ^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter() const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); if (languageIdentifiers != nullptr) { return ref new DecimalFormatter(languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion); @@ -355,9 +386,9 @@ DecimalFormatter^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter( // as configured by running intl.cpl. // // This helper function creates a DateTimeFormatter with a TwentyFour hour clock -DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format) +DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format) const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; @@ -368,41 +399,29 @@ DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatte // If successful, returns a formatter that respects the user's regional format settings, // as configured by running intl.cpl. -DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter( - _In_ String^ format, - _In_ String^ calendarIdentifier, - _In_ String^ clockIdentifier) +DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const { - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; } - return ref new DateTimeFormatter( - format, - languageIdentifiers, - GlobalizationPreferences::HomeGeographicRegion, - calendarIdentifier, - clockIdentifier); + return ref new DateTimeFormatter(format, languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion, calendarIdentifier, clockIdentifier); } -CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter() +CurrencyFormatter ^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatter() const { - String^ userCurrency = (GlobalizationPreferences::Currencies->Size > 0) - ? GlobalizationPreferences::Currencies->GetAt(0) - : StringReference(DefaultCurrencyCode.data()); + String ^ userCurrency = + (GlobalizationPreferences::Currencies->Size > 0) ? GlobalizationPreferences::Currencies->GetAt(0) : StringReference(DefaultCurrencyCode.data()); - IIterable^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); + IIterable ^ languageIdentifiers = GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) { languageIdentifiers = ApplicationLanguages::Languages; } - auto currencyFormatter = ref new CurrencyFormatter( - userCurrency, - languageIdentifiers, - GlobalizationPreferences::HomeGeographicRegion); + auto currencyFormatter = ref new CurrencyFormatter(userCurrency, languageIdentifiers, GlobalizationPreferences::HomeGeographicRegion); int fractionDigits = LocalizationSettings::GetInstance().GetCurrencyTrailingDigits(); currencyFormatter->FractionDigits = fractionDigits; @@ -410,10 +429,18 @@ CurrencyFormatter^ LocalizationService::GetRegionalSettingsAwareCurrencyFormatte return currencyFormatter; } -IIterable^ LocalizationService::GetLanguageIdentifiers() +IIterable ^ LocalizationService::GetLanguageIdentifiers() const { WCHAR currentLocale[LOCALE_NAME_MAX_LENGTH] = {}; int result = GetUserDefaultLocaleName(currentLocale, LOCALE_NAME_MAX_LENGTH); + + if (m_isLanguageOverrided) + { + auto overridedLanguageList = ref new Vector(); + overridedLanguageList->Append(m_language); + return overridedLanguageList; + } + if (result != 0) { // GetUserDefaultLocaleName may return an invalid bcp47 language tag with trailing non-BCP47 friendly characters, @@ -426,12 +453,12 @@ IIterable^ LocalizationService::GetLanguageIdentifiers() *underscore = L'\0'; } - String^ localeString = ref new String(currentLocale); + String ^ localeString = ref new String(currentLocale); // validate if the locale we have is valid // otherwise we fallback to the default. if (Language::IsWellFormed(localeString)) { - auto languageList = ref new Vector(); + auto languageList = ref new Vector(); languageList->Append(localeString); return languageList; } @@ -446,62 +473,93 @@ unordered_map LocalizationService::GetTokenToReadableNameMap() // change given that the engine heavily relies on perfect ordering of certain elements. // To compromise, we'll declare a map from engine resource key to automation name from the // standard project resources. - static vector> s_parenEngineKeyResourceMap = { - // Sine permutations - make_pair(L"67", L"SineDegrees"), - make_pair(L"73", L"SineRadians"), - make_pair(L"79", L"SineGradians"), - make_pair(L"70", L"InverseSineDegrees"), - make_pair(L"76", L"InverseSineRadians"), - make_pair(L"82", L"InverseSineGradians"), - make_pair(L"25", L"HyperbolicSine"), - make_pair(L"85", L"InverseHyperbolicSine"), + static vector> s_parenEngineKeyResourceMap = { // Sine permutations + make_pair(L"67", L"SineDegrees"), + make_pair(L"73", L"SineRadians"), + make_pair(L"79", L"SineGradians"), + make_pair(L"70", L"InverseSineDegrees"), + make_pair(L"76", L"InverseSineRadians"), + make_pair(L"82", L"InverseSineGradians"), + make_pair(L"25", L"HyperbolicSine"), + make_pair(L"85", L"InverseHyperbolicSine"), - // Cosine permutations - make_pair(L"68", L"CosineDegrees"), - make_pair(L"74", L"CosineRadians"), - make_pair(L"80", L"CosineGradians"), - make_pair(L"71", L"InverseCosineDegrees"), - make_pair(L"77", L"InverseCosineRadians"), - make_pair(L"83", L"InverseCosineGradians"), - make_pair(L"26", L"HyperbolicCosine"), - make_pair(L"86", L"InverseHyperbolicCosine"), + // Cosine permutations + make_pair(L"68", L"CosineDegrees"), + make_pair(L"74", L"CosineRadians"), + make_pair(L"80", L"CosineGradians"), + make_pair(L"71", L"InverseCosineDegrees"), + make_pair(L"77", L"InverseCosineRadians"), + make_pair(L"83", L"InverseCosineGradians"), + make_pair(L"26", L"HyperbolicCosine"), + make_pair(L"86", L"InverseHyperbolicCosine"), - // Tangent permutations - make_pair(L"69", L"TangentDegrees"), - make_pair(L"75", L"TangentRadians"), - make_pair(L"81", L"TangentGradians"), - make_pair(L"72", L"InverseTangentDegrees"), - make_pair(L"78", L"InverseTangentRadians"), - make_pair(L"84", L"InverseTangentGradians"), - make_pair(L"27", L"HyperbolicTangent"), - make_pair(L"87", L"InverseHyperbolicTangent"), + // Tangent permutations + make_pair(L"69", L"TangentDegrees"), + make_pair(L"75", L"TangentRadians"), + make_pair(L"81", L"TangentGradians"), + make_pair(L"72", L"InverseTangentDegrees"), + make_pair(L"78", L"InverseTangentRadians"), + make_pair(L"84", L"InverseTangentGradians"), + make_pair(L"27", L"HyperbolicTangent"), + make_pair(L"87", L"InverseHyperbolicTangent"), - // Miscellaneous Scientific functions - make_pair(L"94", L"Factorial"), - make_pair(L"35", L"DegreeMinuteSecond"), - make_pair(L"28", L"NaturalLog"), - make_pair(L"91", L"Square") + // Secant permutations + make_pair(L"SecDeg", L"SecantDegrees"), + make_pair(L"SecRad", L"SecantRadians"), + make_pair(L"SecGrad", L"SecantGradians"), + make_pair(L"InverseSecDeg", L"InverseSecantDegrees"), + make_pair(L"InverseSecRad", L"InverseSecantRadians"), + make_pair(L"InverseSecGrad", L"InverseSecantGradians"), + make_pair(L"Sech", L"HyperbolicSecant"), + make_pair(L"InverseSech", L"InverseHyperbolicSecant"), + + // Cosecant permutations + make_pair(L"CscDeg", L"CosecantDegrees"), + make_pair(L"CscRad", L"CosecantRadians"), + make_pair(L"CscGrad", L"CosecantGradians"), + make_pair(L"InverseCscDeg", L"InverseCosecantDegrees"), + make_pair(L"InverseCscRad", L"InverseCosecantRadians"), + make_pair(L"InverseCscGrad", L"InverseCosecantGradians"), + make_pair(L"Csch", L"HyperbolicCosecant"), + make_pair(L"InverseCsch", L"InverseHyperbolicCosecant"), + + // Cotangent permutations + make_pair(L"CotDeg", L"CotangentDegrees"), + make_pair(L"CotRad", L"CotangentRadians"), + make_pair(L"CotGrad", L"CotangentGradians"), + make_pair(L"InverseCotDeg", L"InverseCotangentDegrees"), + make_pair(L"InverseCotRad", L"InverseCotangentRadians"), + make_pair(L"InverseCotGrad", L"InverseCotangentGradians"), + make_pair(L"Coth", L"HyperbolicCotangent"), + make_pair(L"InverseCoth", L"InverseHyperbolicCotangent"), + + // Miscellaneous Scientific functions + make_pair(L"94", L"Factorial"), + make_pair(L"35", L"DegreeMinuteSecond"), + make_pair(L"28", L"NaturalLog"), + make_pair(L"91", L"Square"), + make_pair(L"CubeRoot", L"CubeRoot"), + make_pair(L"Abs", L"AbsoluteValue") }; - static vector> s_noParenEngineKeyResourceMap = { - // Programmer mode functions - make_pair(L"9", L"LeftShift"), - make_pair(L"10", L"RightShift"), + static vector> s_noParenEngineKeyResourceMap = { // Programmer mode functions + make_pair(L"9", L"LeftShift"), + make_pair(L"10", L"RightShift"), + make_pair(L"LogBaseY", L"Logy"), - // Y Root scientific function - make_pair(L"16", L"YRoot") + // Y Root scientific function + make_pair(L"16", L"YRoot") }; unordered_map tokenToReadableNameMap{}; auto resProvider = AppResourceProvider::GetInstance(); - static const wstring openParen = resProvider.GetCEngineString(StringReference(s_openParenResourceKey))->Data(); + static const wstring openParen = resProvider->GetCEngineString(StringReference(s_openParenResourceKey))->Data(); for (const auto& keyPair : s_parenEngineKeyResourceMap) { - wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data(); - wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data(); + wstring engineStr = resProvider->GetCEngineString(StringReference(keyPair.first.c_str()))->Data(); + wstring automationName = resProvider->GetResourceString(StringReference(keyPair.second.c_str()))->Data(); tokenToReadableNameMap.emplace(engineStr + openParen, automationName); } @@ -509,21 +567,21 @@ unordered_map LocalizationService::GetTokenToReadableNameMap() for (const auto& keyPair : s_noParenEngineKeyResourceMap) { - wstring engineStr = resProvider.GetCEngineString(StringReference(keyPair.first.c_str()))->Data(); - wstring automationName = resProvider.GetResourceString(StringReference(keyPair.second.c_str()))->Data(); + wstring engineStr = resProvider->GetCEngineString(StringReference(keyPair.first.c_str()))->Data(); + wstring automationName = resProvider->GetResourceString(StringReference(keyPair.second.c_str()))->Data(); tokenToReadableNameMap.emplace(engineStr, automationName); } s_noParenEngineKeyResourceMap.clear(); // Also replace hyphens with "minus" - wstring minusText = resProvider.GetResourceString(L"minus")->Data(); + wstring minusText = resProvider->GetResourceString(L"minus")->Data(); tokenToReadableNameMap.emplace(L"-", minusText); return tokenToReadableNameMap; } -String^ LocalizationService::GetNarratorReadableToken(String^ rawToken) +String ^ LocalizationService::GetNarratorReadableToken(String ^ rawToken) { static unordered_map s_tokenToReadableNameMap = GetTokenToReadableNameMap(); @@ -534,21 +592,28 @@ String^ LocalizationService::GetNarratorReadableToken(String^ rawToken) } else { - static const String^ openParen = AppResourceProvider::GetInstance().GetCEngineString(StringReference(s_openParenResourceKey)); + static const String ^ openParen = AppResourceProvider::GetInstance()->GetCEngineString(StringReference(s_openParenResourceKey)); return ref new String(itr->second.c_str()) + L" " + openParen; } } -String^ LocalizationService::GetNarratorReadableString(String^ rawString) +String ^ LocalizationService::GetNarratorReadableString(String ^ rawString) { - wstringstream readableString{}; - readableString << L""; + wstring readableString{}; wstring asWstring = rawString->Data(); for (const auto& c : asWstring) { - readableString << LocalizationService::GetNarratorReadableToken(L"" + c)->Data(); + readableString += LocalizationService::GetNarratorReadableToken(ref new String(&c, 1))->Data(); } - return ref new String(readableString.str().c_str()); + return ref new String(readableString.c_str()); +} + +void LocalizationService::Sort(std::vector& source) +{ + const collate& coll = use_facet>(m_locale); + sort(source.begin(), source.end(), [&coll](Platform::String ^ str1, Platform::String ^ str2) { + return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0; + }); } diff --git a/src/CalcViewModel/Common/LocalizationService.h b/src/CalcViewModel/Common/LocalizationService.h index 7560682d..d6921371 100644 --- a/src/CalcViewModel/Common/LocalizationService.h +++ b/src/CalcViewModel/Common/LocalizationService.h @@ -5,81 +5,102 @@ #include "Utils.h" -namespace CalculatorApp { namespace Common +namespace CalculatorApp { - namespace LocalizationServiceProperties + namespace Common { - static constexpr std::wstring_view DefaultCurrencyCode{ L"USD" }; + namespace LocalizationServiceProperties + { + static constexpr std::wstring_view DefaultCurrencyCode{ L"USD" }; + } + + public + enum class LanguageFontType + { + UIText, + UICaption, + }; + + public + ref class LocalizationService sealed + { + public: + DEPENDENCY_PROPERTY_OWNER(LocalizationService); + + DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(LanguageFontType, FontType, LanguageFontType::UIText); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize); + + internal: + static LocalizationService ^ GetInstance(); + static void OverrideWithLanguage(_In_ const wchar_t* const language); + + Windows::UI::Xaml::FlowDirection GetFlowDirection(); + bool IsRtlLayout(); + bool GetOverrideFontApiValues(); + Platform::String ^ GetLanguage(); + Windows::UI::Xaml::Media::FontFamily ^ GetLanguageFontFamilyForType(LanguageFontType fontType); + Platform::String ^ GetFontFamilyOverride(); + Windows::UI::Text::FontWeight GetFontWeightOverride(); + double GetFontScaleFactorOverride(LanguageFontType fontType); + + void Sort(std::vector& source); + + template + void Sort(std::vector& source, std::function func) + { + const collate& coll = use_facet>(m_locale); + sort(source.begin(), source.end(), [&coll, &func](T obj1, T obj2) { + Platform::String ^ str1 = func(obj1); + Platform::String ^ str2 = func(obj2); + return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0; + }); + } + + Windows::Globalization::NumberFormatting::DecimalFormatter ^ GetRegionalSettingsAwareDecimalFormatter() const; + Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String ^ format) const; + Windows::Globalization::DateTimeFormatting::DateTimeFormatter + ^ GetRegionalSettingsAwareDateTimeFormatter( + _In_ Platform::String ^ format, + _In_ Platform::String ^ calendarIdentifier, + _In_ Platform::String ^ clockIdentifier) const; + + Windows::Globalization::NumberFormatting::CurrencyFormatter ^ GetRegionalSettingsAwareCurrencyFormatter() const; + + static Platform::String ^ GetNarratorReadableToken(Platform::String ^ rawToken); + static Platform::String ^ GetNarratorReadableString(Platform::String ^ rawString); + + private: + LocalizationService(_In_ const wchar_t* const overridedLanguage); + Windows::Globalization::Fonts::LanguageFont ^ GetLanguageFont(LanguageFontType fontType); + Windows::UI::Text::FontWeight ParseFontWeight(Platform::String ^ fontWeight); + + Windows::Foundation::Collections::IIterable ^ GetLanguageIdentifiers() const; + + // Attached property callbacks + static void OnFontTypePropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, LanguageFontType oldValue, LanguageFontType newValue); + static void OnFontWeightPropertyChanged( + Windows::UI::Xaml::DependencyObject ^ target, + Windows::UI::Text::FontWeight oldValue, + Windows::UI::Text::FontWeight newValue); + static void OnFontSizePropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, double oldValue, double newValue); + + static void UpdateFontFamilyAndSize(Windows::UI::Xaml::DependencyObject ^ target); + + static std::unordered_map GetTokenToReadableNameMap(); + + static LocalizationService ^ s_singletonInstance; + + Windows::Globalization::Fonts::LanguageFontGroup ^ m_fontGroup; + Platform::String ^ m_language; + Windows::UI::Xaml::FlowDirection m_flowDirection; + bool m_overrideFontApiValues; + Platform::String ^ m_fontFamilyOverride; + bool m_isLanguageOverrided; + Windows::UI::Text::FontWeight m_fontWeightOverride; + double m_uiTextFontScaleFactorOverride; + double m_uiCaptionFontScaleFactorOverride; + std::locale m_locale; + }; + } - - public enum class LanguageFontType - { - UIText, - UICaption, - }; - - public ref class LocalizationService sealed - { - public: - - DEPENDENCY_PROPERTY_OWNER(LocalizationService); - - DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(LanguageFontType, FontType, LanguageFontType::UIText); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize); - - internal: - static LocalizationService^ GetInstance(); - - Windows::UI::Xaml::FlowDirection GetFlowDirection(); - bool IsRtlLayout(); - bool GetOverrideFontApiValues(); - Platform::String^ GetLanguage(); - Windows::UI::Xaml::Media::FontFamily^ GetLanguageFontFamilyForType(LanguageFontType fontType); - Platform::String^ GetFontFamilyOverride(); - Windows::UI::Text::FontWeight GetFontWeightOverride(); - double GetFontScaleFactorOverride(LanguageFontType fontType); - - static Windows::Globalization::NumberFormatting::DecimalFormatter^ GetRegionalSettingsAwareDecimalFormatter(); - static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String^ format); - static Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ GetRegionalSettingsAwareDateTimeFormatter( - _In_ Platform::String^ format, - _In_ Platform::String^ calendarIdentifier, - _In_ Platform::String^ clockIdentifier); - - static Windows::Globalization::NumberFormatting::CurrencyFormatter^ GetRegionalSettingsAwareCurrencyFormatter(); - - static Platform::String^ GetNarratorReadableToken(Platform::String^ rawToken); - static Platform::String^ GetNarratorReadableString(Platform::String^ rawString); - - private: - Windows::Globalization::Fonts::LanguageFont^ GetLanguageFont(LanguageFontType fontType); - Windows::UI::Text::FontWeight ParseFontWeight(Platform::String^ fontWeight); - - static Windows::Foundation::Collections::IIterable^ GetLanguageIdentifiers(); - - // Attached property callbacks - static void OnFontTypePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, LanguageFontType oldValue, LanguageFontType newValue); - static void OnFontWeightPropertyChanged(Windows::UI::Xaml::DependencyObject^ target, Windows::UI::Text::FontWeight oldValue, Windows::UI::Text::FontWeight newValue); - static void OnFontSizePropertyChanged(Windows::UI::Xaml::DependencyObject^ target, double oldValue, double newValue); - - static void UpdateFontFamilyAndSize(Windows::UI::Xaml::DependencyObject^ target); - - static std::unordered_map GetTokenToReadableNameMap(); - - private: - LocalizationService(); - - static LocalizationService^ s_singletonInstance; - - Windows::Globalization::Fonts::LanguageFontGroup^ m_fontGroup; - Platform::String^ m_language; - Windows::UI::Xaml::FlowDirection m_flowDirection; - bool m_overrideFontApiValues; - Platform::String^ m_fontFamilyOverride; - Windows::UI::Text::FontWeight m_fontWeightOverride; - double m_uiTextFontScaleFactorOverride; - double m_uiCaptionFontScaleFactorOverride; - }; - -}} - +} diff --git a/src/CalcViewModel/Common/LocalizationSettings.h b/src/CalcViewModel/Common/LocalizationSettings.h index d37e9647..86d45ab2 100644 --- a/src/CalcViewModel/Common/LocalizationSettings.h +++ b/src/CalcViewModel/Common/LocalizationSettings.h @@ -14,56 +14,48 @@ namespace CalculatorApp { private: LocalizationSettings() - { - int result = 0; - // Use DecimalFormatter as it respects the locale and the user setting - Windows::Globalization::NumberFormatting::DecimalFormatter^ formatter; - formatter = CalculatorApp::Common::LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + : LocalizationSettings(LocalizationService::GetInstance()->GetRegionalSettingsAwareDecimalFormatter()) + { + } + + public: + // This is only public for unit testing purposes. + LocalizationSettings(Windows::Globalization::NumberFormatting::DecimalFormatter ^ formatter) + { formatter->FractionDigits = 0; formatter->IsDecimalPointAlwaysDisplayed = false; - for (unsigned int i = 0; i < 10; i++) + for (unsigned int i = 0; i < m_digitSymbols.size(); i++) { - m_digitSymbols.at(i) = formatter->FormatUInt(i)->Data()[0]; + m_digitSymbols[i] = formatter->FormatUInt(i)->Data()[0]; } wchar_t resolvedName[LOCALE_NAME_MAX_LENGTH]; - result = ResolveLocaleName(formatter->ResolvedLanguage->Data(), - resolvedName, - LOCALE_NAME_MAX_LENGTH); + int result = ResolveLocaleName(formatter->ResolvedLanguage->Data(), resolvedName, LOCALE_NAME_MAX_LENGTH); if (result == 0) { throw std::runtime_error("Unexpected error resolving locale name"); } else { - m_resolvedName = resolvedName; + m_resolvedName = ref new Platform::String(resolvedName); wchar_t decimalString[LocaleSettingBufferSize] = L""; - result = GetLocaleInfoEx(m_resolvedName.c_str(), - LOCALE_SDECIMAL, - decimalString, - static_cast(std::size(decimalString))); + result = GetLocaleInfoEx(m_resolvedName->Data(), LOCALE_SDECIMAL, decimalString, static_cast(std::size(decimalString))); if (result == 0) { throw std::runtime_error("Unexpected error while getting locale info"); } wchar_t groupingSymbolString[LocaleSettingBufferSize] = L""; - result = GetLocaleInfoEx(m_resolvedName.c_str(), - LOCALE_STHOUSAND, - groupingSymbolString, - static_cast(std::size(groupingSymbolString))); + result = GetLocaleInfoEx(m_resolvedName->Data(), LOCALE_STHOUSAND, groupingSymbolString, static_cast(std::size(groupingSymbolString))); if (result == 0) { throw std::runtime_error("Unexpected error while getting locale info"); } wchar_t numberGroupingString[LocaleSettingBufferSize] = L""; - result = GetLocaleInfoEx(m_resolvedName.c_str(), - LOCALE_SGROUPING, - numberGroupingString, - static_cast(std::size(numberGroupingString))); + result = GetLocaleInfoEx(m_resolvedName->Data(), LOCALE_SGROUPING, numberGroupingString, static_cast(std::size(numberGroupingString))); if (result == 0) { throw std::runtime_error("Unexpected error while getting locale info"); @@ -71,7 +63,8 @@ namespace CalculatorApp // Get locale info for List Separator, eg. comma is used in many locales wchar_t listSeparatorString[4] = L""; - result = ::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, + result = ::GetLocaleInfoEx( + m_resolvedName->Data(), LOCALE_SLIST, listSeparatorString, static_cast(std::size(listSeparatorString))); // Max length of the expected return value is 4 @@ -81,7 +74,8 @@ namespace CalculatorApp } int currencyTrailingDigits = 0; - result = GetLocaleInfoEx(m_resolvedName.c_str(), + result = GetLocaleInfoEx( + m_resolvedName->Data(), LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, (LPWSTR)¤cyTrailingDigits, sizeof(currencyTrailingDigits) / sizeof(WCHAR)); @@ -93,7 +87,8 @@ namespace CalculatorApp // Currency symbol precedence is either 0 or 1. // A value of 0 indicates the symbol follows the currency value. int currencySymbolPrecedence = 1; - result = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, + result = GetLocaleInfoEx( + m_resolvedName->Data(), LOCALE_IPOSSYMPRECEDES | LOCALE_RETURN_NUMBER, (LPWSTR)¤cySymbolPrecedence, sizeof(currencySymbolPrecedence) / sizeof(WCHAR)); @@ -112,27 +107,24 @@ namespace CalculatorApp // Note: This function returns 0 on failure. // We'll ignore the failure in that case and the CalendarIdentifier would get set to GregorianCalendar. CALID calId; - ::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, - LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, - reinterpret_cast(&calId), - sizeof(calId)); + ::GetLocaleInfoEx(m_resolvedName->Data(), LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, reinterpret_cast(&calId), sizeof(calId)); m_calendarIdentifier = GetCalendarIdentifierFromCalid(calId); // Get FirstDayOfWeek Date and Time setting wchar_t day[80] = L""; - ::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, - LOCALE_IFIRSTDAYOFWEEK, // The first day in a week - reinterpret_cast(day), // Argument is of type PWSTR - static_cast(std::size(day))); // Max return size are 80 characters + ::GetLocaleInfoEx( + m_resolvedName->Data(), + LOCALE_IFIRSTDAYOFWEEK, // The first day in a week + reinterpret_cast(day), // Argument is of type PWSTR + static_cast(std::size(day))); // Max return size are 80 characters // The LOCALE_IFIRSTDAYOFWEEK integer value varies from 0, 1, .. 6 for Monday, Tuesday, ... Sunday // DayOfWeek enum value varies from 0, 1, .. 6 for Sunday, Monday, ... Saturday // Hence, DayOfWeek = (valueof(LOCALE_IFIRSTDAYOFWEEK) + 1) % 7 - m_firstDayOfWeek = static_cast((_wtoi(day) + 1) % 7); // static cast int to DayOfWeek enum + m_firstDayOfWeek = static_cast((_wtoi(day) + 1) % 7); // static cast int to DayOfWeek enum } - public: // A LocalizationSettings object is not copyable. LocalizationSettings(const LocalizationSettings&) = delete; LocalizationSettings& operator=(const LocalizationSettings&) = delete; @@ -149,18 +141,14 @@ namespace CalculatorApp return localizationSettings; } - Platform::String^ GetLocaleName() const + Platform::String ^ GetLocaleName() const { - return ref new Platform::String(m_resolvedName.c_str()); + return m_resolvedName; } bool IsDigitEnUsSetting() const { - if (this->GetDigitSymbolFromEnUsDigit('0') == L'0') - { - return true; - } - return false; + return (this->GetDigitSymbolFromEnUsDigit('0') == L'0'); } void LocalizeDisplayValue(_Inout_ std::wstring* stringToLocalize) const @@ -179,62 +167,46 @@ namespace CalculatorApp } } - Platform::String^ GetEnglishValueFromLocalizedDigits(const std::wstring& localizedString) const + Platform::String ^ GetEnglishValueFromLocalizedDigits(Platform::String ^ localizedString) const { if (m_resolvedName == L"en-US") { - return ref new Platform::String(localizedString.c_str()); + return localizedString; } - size_t i = 0; - size_t length = localizedString.size(); - std::unique_ptr englishString(new wchar_t[length + 1]); // +1 for the null termination + std::wstring englishString; + englishString.reserve(localizedString->Length()); - for (; i < length; ++i) + for (wchar_t ch : localizedString) { - wchar_t ch = localizedString[i]; if (!IsEnUsDigit(ch)) { - for (int j = 0; j < 10; ++j) + auto it = std::find(m_digitSymbols.begin(), m_digitSymbols.end(), ch); + + if (it != m_digitSymbols.end()) { - if (ch == m_digitSymbols[j]) - { - ch = j.ToString()->Data()[0]; - break; - //ch = val - L'0'; - } + auto index = std::distance(m_digitSymbols.begin(), it); + ch = index.ToString()->Data()[0]; } } if (ch == m_decimalSeparator) { ch = L'.'; } - englishString[i] = ch; + englishString += ch; } - englishString[i] = '\0'; - return ref new Platform::String(englishString.get()); + return ref new Platform::String(englishString.c_str()); } bool IsEnUsDigit(const wchar_t digit) const { - if (digit >= L'0' && digit <= L'9') - { - return true; - } - return false; + return (digit >= L'0' && digit <= L'9'); } bool IsLocalizedDigit(const wchar_t digit) const { - for (auto dig : m_digitSymbols) - { - if (digit == dig) - { - return true; - } - } - return false; + return std::find(m_digitSymbols.begin(), m_digitSymbols.end(), digit) != m_digitSymbols.end(); } bool IsLocalizedHexDigit(const wchar_t digit) const @@ -244,15 +216,7 @@ namespace CalculatorApp return true; } - for (auto dig : s_hexSymbols) - { - if (digit == dig) - { - return true; - } - } - - return false; + return std::find(s_hexSymbols.begin(), s_hexSymbols.end(), digit) != s_hexSymbols.end(); } wchar_t GetDigitSymbolFromEnUsDigit(wchar_t digitSymbol) const @@ -274,16 +238,12 @@ namespace CalculatorApp std::wstring GetDecimalSeparatorStr() const { - std::wstring result; - result.push_back(m_decimalSeparator); - return result; + return std::wstring(1, m_decimalSeparator); } std::wstring GetNumberGroupingSeparatorStr() const { - std::wstring result; - result.push_back(m_numberGroupSeparator); - return result; + return std::wstring(1, m_numberGroupSeparator); } std::wstring GetNumberGroupingStr() const @@ -291,21 +251,16 @@ namespace CalculatorApp return m_numberGrouping; } - void RemoveGroupSeparators(const wchar_t* value, const size_t length, std::wstring* rawValue) const + Platform::String ^ RemoveGroupSeparators(Platform::String ^ source) const { - rawValue->clear(); - rawValue->reserve(length); - - for (size_t i = 0; i < length; i++) - { - if (value[i] != L' ' && value[i] != m_numberGroupSeparator) - { - rawValue->append(1, value[i]); - } - } + std::wstring destination; + std::copy_if( + begin(source), end(source), std::back_inserter(destination), [this](auto const c) { return c != L' ' && c != m_numberGroupSeparator; }); + + return ref new Platform::String(destination.c_str()); } - Platform::String^ GetCalendarIdentifier() const + Platform::String ^ GetCalendarIdentifier() const { return m_calendarIdentifier; } @@ -378,10 +333,10 @@ namespace CalculatorApp // Hexadecimal characters are not currently localized static constexpr std::array s_hexSymbols{ L'A', L'B', L'C', L'D', L'E', L'F' }; std::wstring m_listSeparator; - Platform::String^ m_calendarIdentifier; + Platform::String ^ m_calendarIdentifier; Windows::Globalization::DayOfWeek m_firstDayOfWeek; int m_currencySymbolPrecedence; - std::wstring m_resolvedName; + Platform::String ^ m_resolvedName; int m_currencyTrailingDigits; static const unsigned int LocaleSettingBufferSize = 16; }; diff --git a/src/CalcViewModel/Common/LocalizationStringUtil.h b/src/CalcViewModel/Common/LocalizationStringUtil.h index 0516ae5a..73da6d0d 100644 --- a/src/CalcViewModel/Common/LocalizationStringUtil.h +++ b/src/CalcViewModel/Common/LocalizationStringUtil.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once @@ -9,56 +9,90 @@ namespace CalculatorApp { namespace Common { - class LocalizationStringUtil + class LocalizationStringUtilInternal { public: - static std::wstring GetLocalizedString(const wchar_t* pMessage, ...) + static Platform::String ^ GetLocalizedString(Platform::String ^ pMessage, ...) { std::wstring returnString = L""; const UINT32 length = 1024; std::unique_ptr spBuffer = std::unique_ptr(new wchar_t[length]); va_list args = NULL; va_start(args, pMessage); - DWORD fmtReturnVal = FormatMessage(FORMAT_MESSAGE_FROM_STRING, - pMessage, - 0, - 0, - spBuffer.get(), - length, - &args); + DWORD fmtReturnVal = FormatMessage(FORMAT_MESSAGE_FROM_STRING, pMessage->Data(), 0, 0, spBuffer.get(), length, &args); va_end(args); if (fmtReturnVal != 0) { - returnString = spBuffer.get(); + return ref new Platform::String(spBuffer.get()); } - - return returnString; - } - - template - static Platform::String^ GetLocalizedNarratorAnnouncement(Platform::String^ resourceKey, Platform::String^& formatVariable, T*... params) - { - EnsureInitialization(resourceKey, formatVariable); - return StringReference(GetLocalizedString(formatVariable->Data(), params...).c_str()); - } - - private: - static void EnsureInitialization(Platform::String^ resourceKey, Platform::String^& formatVariable) - { - if (resourceKey == nullptr || resourceKey->IsEmpty()) + else { - return; + return ref new Platform::String(); } - - // If the formatVariable already has a value, we don't need to set it again. Simply return. - if (formatVariable != nullptr && !formatVariable->IsEmpty()) - { - return; - } - - formatVariable = AppResourceProvider::GetInstance().GetResourceString(resourceKey); } }; + + public + ref class LocalizationStringUtil sealed + { + public: + static Platform::String + ^ GetLocalizedString(Platform::String ^ pMessage) + { + return LocalizationStringUtilInternal::GetLocalizedString(pMessage); + } + + static Platform::String + ^ GetLocalizedString( + Platform::String ^ pMessage, + Platform::String ^ param1) + { + return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data()); + } + + static Platform::String + ^ GetLocalizedString( + Platform::String ^ pMessage, + Platform::String ^ param1, + Platform::String ^ param2) + { + return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data()); + } + + static Platform::String + ^ GetLocalizedString( + Platform::String ^ pMessage, + Platform::String ^ param1, + Platform::String ^ param2, + Platform::String + ^ param3) + { + return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data(), param3->Data()); + } + + static Platform::String + ^ GetLocalizedString( + Platform::String ^ pMessage, + Platform::String ^ param1, + Platform::String ^ param2, + Platform::String ^ param3, + Platform::String ^ param4) + { + 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()); + } + }; } } diff --git a/src/CalcViewModel/Common/MyVirtualKey.h b/src/CalcViewModel/Common/MyVirtualKey.h index 4a69b95c..c530e816 100644 --- a/src/CalcViewModel/Common/MyVirtualKey.h +++ b/src/CalcViewModel/Common/MyVirtualKey.h @@ -7,7 +7,8 @@ namespace CalculatorApp { namespace Common { - public enum class MyVirtualKey + public + enum class MyVirtualKey { None = 0, LeftButton = 1, diff --git a/src/CalcViewModel/Common/NavCategory.cpp b/src/CalcViewModel/Common/NavCategory.cpp index e5247cff..7c89c6a4 100644 --- a/src/CalcViewModel/Common/NavCategory.cpp +++ b/src/CalcViewModel/Common/NavCategory.cpp @@ -1,10 +1,11 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" #include "NavCategory.h" #include "AppResourceProvider.h" #include "Common/LocalizationStringUtil.h" +#include using namespace CalculatorApp; using namespace CalculatorApp::Common; @@ -22,85 +23,318 @@ 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 s_categoryGroupManifest = { - NavCategoryGroupInitializer { CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText"}, - NavCategoryGroupInitializer { CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" } -}; - // vvv THESE CONSTANTS SHOULD NEVER CHANGE vvv -static constexpr int STANDARD_ID = 0; -static constexpr int SCIENTIFIC_ID = 1; -static constexpr int PROGRAMMER_ID = 2; -static constexpr int DATE_ID = 3; -static constexpr int VOLUME_ID = 4; -static constexpr int LENGTH_ID = 5; -static constexpr int WEIGHT_ID = 6; +static constexpr int STANDARD_ID = 0; +static constexpr int SCIENTIFIC_ID = 1; +static constexpr int PROGRAMMER_ID = 2; +static constexpr int DATE_ID = 3; +static constexpr int VOLUME_ID = 4; +static constexpr int LENGTH_ID = 5; +static constexpr int WEIGHT_ID = 6; static constexpr int TEMPERATURE_ID = 7; -static constexpr int ENERGY_ID = 8; -static constexpr int AREA_ID = 9; -static constexpr int SPEED_ID = 10; -static constexpr int TIME_ID = 11; -static constexpr int POWER_ID = 12; -static constexpr int DATA_ID = 13; -static constexpr int PRESSURE_ID = 14; -static constexpr int ANGLE_ID = 15; -static constexpr int CURRENCY_ID = 16; +static constexpr int ENERGY_ID = 8; +static constexpr int AREA_ID = 9; +static constexpr int SPEED_ID = 10; +static constexpr int TIME_ID = 11; +static constexpr int POWER_ID = 12; +static constexpr int DATA_ID = 13; +static constexpr int PRESSURE_ID = 14; +static constexpr int ANGLE_ID = 15; +static constexpr int CURRENCY_ID = 16; +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()); +} + +extern "C" +{ + WINADVAPI LSTATUS APIENTRY RegGetValueW( + _In_ HKEY hkey, + _In_opt_ LPCWSTR lpSubKey, + _In_opt_ LPCWSTR lpValue, + _In_ DWORD dwFlags, + _Out_opt_ LPDWORD pdwType, + _When_( + (dwFlags & 0x7F) == RRF_RT_REG_SZ || (dwFlags & 0x7F) == RRF_RT_REG_EXPAND_SZ || (dwFlags & 0x7F) == (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ) + || *pdwType == REG_SZ || *pdwType == REG_EXPAND_SZ, + _Post_z_) _When_((dwFlags & 0x7F) == RRF_RT_REG_MULTI_SZ || *pdwType == REG_MULTI_SZ, _Post_ _NullNull_terminated_) + _Out_writes_bytes_to_opt_(*pcbData, *pcbData) PVOID pvData, + _Inout_opt_ LPDWORD pcbData); +} + +bool IsGraphingModeAvailable() +{ + static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"); + return supportGraph; +} + +Box ^ _isGraphingModeEnabledCached = nullptr; +bool IsGraphingModeEnabled() +{ + if (!IsGraphingModeAvailable()) + { + return false; + } + + if (_isGraphingModeEnabledCached != nullptr) + { + return _isGraphingModeEnabledCached->Value; + } + + DWORD allowGraphingCalculator{ 0 }; + DWORD bufferSize{ sizeof(allowGraphingCalculator) }; + // Make sure to call RegGetValueW only on Windows 10 1903+ + if (RegGetValueW( + HKEY_CURRENT_USER, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Calculator", + L"AllowGraphingCalculator", + RRF_RT_DWORD, // RRF_RT_DWORD == RRF_RT_REG_DWORD | RRF_RT_REG_BINARY + nullptr, + reinterpret_cast(&allowGraphingCalculator), + &bufferSize) + == ERROR_SUCCESS) + { + _isGraphingModeEnabledCached = allowGraphingCalculator != 0; + } + else + { + _isGraphingModeEnabledCached = true; + } + return _isGraphingModeEnabledCached->Value; +} + // The order of items in this list determines the order of items in the menu. -static constexpr array s_categoryManifest = { - NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL }, - NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL }, - NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL }, - NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL }, - NavCategoryInitializer { ViewMode::Currency, CURRENCY_ID, L"Currency", L"CategoryName_Currency", L"\uEB0D", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Volume, VOLUME_ID, L"Volume", L"CategoryName_Volume", L"\uF1AA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Length, LENGTH_ID, L"Length", L"CategoryName_Length", L"\uECC6", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Weight, WEIGHT_ID, L"Weight and Mass", L"CategoryName_Weight", L"\uF4C1", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Temperature, TEMPERATURE_ID, L"Temperature", L"CategoryName_Temperature", L"\uE7A3", CategoryGroupType::Converter, MyVirtualKey::None, SUPPORTS_NEGATIVE }, - NavCategoryInitializer { ViewMode::Energy, ENERGY_ID, L"Energy", L"CategoryName_Energy", L"\uECAD", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Area, AREA_ID, L"Area", L"CategoryName_Area", L"\uE809", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Speed, SPEED_ID, L"Speed", L"CategoryName_Speed", L"\uEADA", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Time, TIME_ID, L"Time", L"CategoryName_Time", L"\uE917", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Power, POWER_ID, L"Power", L"CategoryName_Power", L"\uE945", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Data, DATA_ID, L"Data", L"CategoryName_Data", L"\uF20F", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Pressure, PRESSURE_ID, L"Pressure", L"CategoryName_Pressure", L"\uEC4A", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY }, - NavCategoryInitializer { ViewMode::Angle, ANGLE_ID, L"Angle", L"CategoryName_Angle", L"\uF515", CategoryGroupType::Converter, MyVirtualKey::None, POSITIVE_ONLY } -}; +static const list s_categoryManifest = [] { + auto res = list{ NavCategoryInitializer{ ViewMode::Standard, + STANDARD_ID, + L"Standard", + L"StandardMode", + L"\uE8EF", + CategoryGroupType::Calculator, + MyVirtualKey::Number1, + L"1", + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Scientific, + SCIENTIFIC_ID, + L"Scientific", + L"ScientificMode", + L"\uF196", + CategoryGroupType::Calculator, + MyVirtualKey::Number2, + 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, + supportGraphingCalculator ? MyVirtualKey::Number4 : MyVirtualKey::Number3, + towchar_t(currentIndex++), + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Date, + DATE_ID, + L"Date", + L"DateCalculationMode", + L"\uE787", + CategoryGroupType::Calculator, + supportGraphingCalculator ? MyVirtualKey::Number5 : MyVirtualKey::Number4, + towchar_t(currentIndex++), + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Currency, + CURRENCY_ID, + L"Currency", + L"CategoryName_Currency", + L"\uEB0D", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Volume, + VOLUME_ID, + L"Volume", + L"CategoryName_Volume", + L"\uF1AA", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Length, + LENGTH_ID, + L"Length", + L"CategoryName_Length", + L"\uECC6", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Weight, + WEIGHT_ID, + L"Weight and Mass", + L"CategoryName_Weight", + L"\uF4C1", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Temperature, + TEMPERATURE_ID, + L"Temperature", + L"CategoryName_Temperature", + L"\uE7A3", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true }, + NavCategoryInitializer{ ViewMode::Energy, + ENERGY_ID, + L"Energy", + L"CategoryName_Energy", + L"\uECAD", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Area, + AREA_ID, + L"Area", + L"CategoryName_Area", + L"\uE809", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Speed, + SPEED_ID, + L"Speed", + L"CategoryName_Speed", + L"\uEADA", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Time, + TIME_ID, + L"Time", + L"CategoryName_Time", + L"\uE917", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Power, + POWER_ID, + L"Power", + L"CategoryName_Power", + L"\uE945", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true }, + NavCategoryInitializer{ ViewMode::Data, + DATA_ID, + L"Data", + L"CategoryName_Data", + L"\uF20F", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Pressure, + PRESSURE_ID, + L"Pressure", + L"CategoryName_Pressure", + L"\uEC4A", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Angle, + ANGLE_ID, + L"Angle", + L"CategoryName_Angle", + L"\uF515", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true } }); + return res; +}(); // This function should only be used when storing the mode to app data. int NavCategory::Serialize(ViewMode mode) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode](const NavCategoryInitializer& initializer) - { - return initializer.viewMode == mode; - }); + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? iter->serializationId - : -1; + return (iter != s_categoryManifest.end()) ? iter->serializationId : -1; } // This function should only be used when restoring the mode from app data. -ViewMode NavCategory::Deserialize(Platform::Object^ obj) +ViewMode NavCategory::Deserialize(Platform::Object ^ obj) { // If we cast directly to ViewMode we will fail // because we technically store an int. // Need to cast to int, then ViewMode. - auto boxed = dynamic_cast^>(obj); + auto boxed = dynamic_cast ^>(obj); if (boxed != nullptr) { int serializationId = boxed->Value; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [serializationId](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [serializationId](const NavCategoryInitializer& initializer) { return initializer.serializationId == serializationId; }); 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; } } @@ -110,20 +344,29 @@ ViewMode NavCategory::Deserialize(Platform::Object^ obj) bool NavCategory::IsValidViewMode(ViewMode mode) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode](const NavCategoryInitializer& initializer) - { - return initializer.viewMode == mode; - }); + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); + + return iter != s_categoryManifest.end(); +} + +bool NavCategory::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) @@ -138,65 +381,44 @@ bool NavCategory::IsConverterViewMode(ViewMode mode) bool NavCategory::IsModeInCategoryGroup(ViewMode mode, CategoryGroupType type) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode, type](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, type](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode && initializer.groupType == type; }); return iter != s_categoryManifest.end(); } -String^ NavCategory::GetFriendlyName(ViewMode mode) +String ^ NavCategory::GetFriendlyName(ViewMode mode) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode](const NavCategoryInitializer& initializer) - { - return initializer.viewMode == mode; - }); + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? StringReference(iter->friendlyName) - : L"None"; + return (iter != s_categoryManifest.end()) ? StringReference(iter->friendlyName) : L"None"; } -ViewMode NavCategory::GetViewModeForFriendlyName(String^ name) +ViewMode NavCategory::GetViewModeForFriendlyName(String ^ name) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [name](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [name](const NavCategoryInitializer& initializer) { return wcscmp(initializer.friendlyName, name->Data()) == 0; }); - return (iter != s_categoryManifest.end()) - ? iter->viewMode - : ViewMode::None; + return (iter != s_categoryManifest.end()) ? iter->viewMode : ViewMode::None; } -String^ NavCategory::GetNameResourceKey(ViewMode mode) +String ^ NavCategory::GetNameResourceKey(ViewMode mode) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode](const NavCategoryInitializer& initializer) - { - return initializer.viewMode == mode; - }); + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? StringReference(iter->nameResourceKey) + "Text" - : nullptr; + return (iter != s_categoryManifest.end()) ? StringReference(iter->nameResourceKey) + "Text" : nullptr; } CategoryGroupType NavCategory::GetGroupType(ViewMode mode) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode](const NavCategoryInitializer& initializer) - { - return initializer.viewMode == mode; - }); + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? iter->groupType - : CategoryGroupType::None; + return (iter != s_categoryManifest.end()) ? iter->groupType : CategoryGroupType::None; } // GetIndex is 0-based, GetPosition is 1-based @@ -210,9 +432,7 @@ int NavCategory::GetFlatIndex(ViewMode mode) { int index = -1; CategoryGroupType type = CategoryGroupType::None; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode, &type, &index](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, &type, &index](const NavCategoryInitializer& initializer) { index++; if (initializer.groupType != type) { @@ -223,18 +443,14 @@ int NavCategory::GetFlatIndex(ViewMode mode) return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? index - : -1; + return (iter != s_categoryManifest.end()) ? index : -1; } // GetIndex is 0-based, GetPosition is 1-based int NavCategory::GetIndexInGroup(ViewMode mode, CategoryGroupType type) { int index = -1; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode, type, &index](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, type, &index](const NavCategoryInitializer& initializer) { if (initializer.groupType == type) { index++; @@ -244,38 +460,28 @@ int NavCategory::GetIndexInGroup(ViewMode mode, CategoryGroupType type) return false; }); - return (iter != s_categoryManifest.end()) - ? index - : -1; + return (iter != s_categoryManifest.end()) ? index : -1; } // GetIndex is 0-based, GetPosition is 1-based int NavCategory::GetPosition(ViewMode mode) { int position = 0; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [mode, &position](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, &position](const NavCategoryInitializer& initializer) { position++; return initializer.viewMode == mode; }); - return (iter != s_categoryManifest.end()) - ? position - : -1; + return (iter != s_categoryManifest.end()) ? position : -1; } ViewMode NavCategory::GetViewModeForVirtualKey(MyVirtualKey virtualKey) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), - [virtualKey](const NavCategoryInitializer& initializer) - { + auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [virtualKey](const NavCategoryInitializer& initializer) { return initializer.virtualKey == virtualKey; }); - return (iter != s_categoryManifest.end()) - ? iter->viewMode - : ViewMode::None; + return (iter != s_categoryManifest.end()) ? iter->viewMode : ViewMode::None; } vector NavCategory::GetCategoryAcceleratorKeys() @@ -292,88 +498,59 @@ vector NavCategory::GetCategoryAcceleratorKeys() return accelerators; } -NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer) : - m_Categories(ref new Vector()) +NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer) + : m_Categories(ref new Vector()) { m_GroupType = groupInitializer.type; auto resProvider = AppResourceProvider::GetInstance(); - String^ headerResourceKey = StringReference(groupInitializer.headerResourceKey); - String^ modeResourceKey = StringReference(groupInitializer.modeResourceKey); - String^ automationResourceKey = StringReference(groupInitializer.automationResourceKey); - m_Name = resProvider.GetResourceString(headerResourceKey); - String^ groupMode = resProvider.GetResourceString(modeResourceKey); - String^ automationName = resProvider.GetResourceString(automationResourceKey); + m_Name = resProvider->GetResourceString(StringReference(groupInitializer.headerResourceKey)); + String ^ groupMode = resProvider->GetResourceString(StringReference(groupInitializer.modeResourceKey)); + String ^ automationName = resProvider->GetResourceString(StringReference(groupInitializer.automationResourceKey)); - String^ navCategoryHeaderAutomationNameFormat = resProvider.GetResourceString(L"NavCategoryHeader_AutomationNameFormat"); - m_AutomationName = ref new String(LocalizationStringUtil::GetLocalizedString( - navCategoryHeaderAutomationNameFormat->Data(), - automationName->Data()).c_str()); + String ^ navCategoryHeaderAutomationNameFormat = resProvider->GetResourceString(L"NavCategoryHeader_AutomationNameFormat"); + m_AutomationName = LocalizationStringUtil::GetLocalizedString(navCategoryHeaderAutomationNameFormat, automationName); - String^ navCategoryItemAutomationNameFormat = resProvider.GetResourceString(L"NavCategoryItem_AutomationNameFormat"); + String ^ navCategoryItemAutomationNameFormat = resProvider->GetResourceString(L"NavCategoryItem_AutomationNameFormat"); for (const NavCategoryInitializer& categoryInitializer : s_categoryManifest) { if (categoryInitializer.groupType == groupInitializer.type) { - String^ nameResourceKey = StringReference(categoryInitializer.nameResourceKey); - String^ categoryName = resProvider.GetResourceString(nameResourceKey + "Text"); - String^ categoryAutomationName = ref new String(LocalizationStringUtil::GetLocalizedString( - navCategoryItemAutomationNameFormat->Data(), - categoryName->Data(), - m_Name->Data()).c_str()); + String ^ nameResourceKey = StringReference(categoryInitializer.nameResourceKey); + String ^ categoryName = resProvider->GetResourceString(nameResourceKey + "Text"); + String ^ categoryAutomationName = LocalizationStringUtil::GetLocalizedString(navCategoryItemAutomationNameFormat, categoryName, m_Name); m_Categories->Append(ref new NavCategory( 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)); } } } -IObservableVector^ NavCategoryGroup::CreateMenuOptions() +IObservableVector ^ NavCategoryGroup::CreateMenuOptions() { - auto menuOptions = ref new Vector(); + auto menuOptions = ref new Vector(); menuOptions->Append(CreateCalculatorCategory()); menuOptions->Append(CreateConverterCategory()); return menuOptions; } -NavCategoryGroup^ NavCategoryGroup::CreateCalculatorCategory() +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() +NavCategoryGroup ^ NavCategoryGroup::CreateConverterCategory() { - return ref new NavCategoryGroup(s_categoryGroupManifest.at(1)); -} - -vector NavCategoryGroup::GetInitializerCategoryGroup(CategoryGroupType groupType) -{ - vector 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" }); } diff --git a/src/CalcViewModel/Common/NavCategory.h b/src/CalcViewModel/Common/NavCategory.h index 33e6915a..b7c46057 100644 --- a/src/CalcViewModel/Common/NavCategory.h +++ b/src/CalcViewModel/Common/NavCategory.h @@ -24,7 +24,8 @@ namespace CalculatorApp // Don't change the order of these enums // and definitely don't use int arithmetic // to change modes. - public enum class ViewMode + public + enum class ViewMode { None = -1, Standard = 0, @@ -43,131 +44,104 @@ namespace CalculatorApp Data = 13, Pressure = 14, Angle = 15, - Currency = 16 + Currency = 16, + Graphing = 17 }; - public enum class CategoryGroupType + public + enum class CategoryGroupType { None = -1, Calculator = 0, Converter = 1 }; - private struct NavCategoryInitializer + private + struct NavCategoryInitializer { constexpr NavCategoryInitializer( ViewMode mode, int id, - wchar_t const * name, - wchar_t const * nameKey, - wchar_t const * glyph, + wchar_t const* name, + wchar_t const* nameKey, + wchar_t const* glyph, CategoryGroupType group, MyVirtualKey vKey, - bool categorySupportsNegative) - : - viewMode(mode), - serializationId(id), - friendlyName(name), - nameResourceKey(nameKey), - glyph(glyph), - groupType(group), - virtualKey(vKey), - supportsNegative(categorySupportsNegative) - {} + wchar_t const* aKey, + bool categorySupportsNegative, + bool enabled) + : viewMode(mode) + , serializationId(id) + , friendlyName(name) + , nameResourceKey(nameKey) + , glyph(glyph) + , groupType(group) + , virtualKey(vKey) + , accessKey(aKey) + , supportsNegative(categorySupportsNegative) + , isEnabled(enabled) + { + } const ViewMode viewMode; const int serializationId; - const wchar_t * const friendlyName; - const wchar_t * const nameResourceKey; - const wchar_t * const glyph; + const wchar_t* const friendlyName; + const wchar_t* const nameResourceKey; + const wchar_t* const glyph; const CategoryGroupType groupType; const MyVirtualKey virtualKey; + const wchar_t* const accessKey; const bool supportsNegative; + const bool isEnabled; }; - private struct NavCategoryGroupInitializer + private + struct NavCategoryGroupInitializer { - constexpr NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const * h, wchar_t const * n, wchar_t const * a) : - type(t), headerResourceKey(h), modeResourceKey(n), automationResourceKey(a) - {} + constexpr NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const* h, wchar_t const* n, wchar_t const* a) + : type(t) + , headerResourceKey(h) + , modeResourceKey(n) + , automationResourceKey(a) + { + } const CategoryGroupType type; - const wchar_t *headerResourceKey; - const wchar_t *modeResourceKey; - const wchar_t *automationResourceKey; + const wchar_t* headerResourceKey; + const wchar_t* modeResourceKey; + const wchar_t* automationResourceKey; }; - [Windows::UI::Xaml::Data::Bindable] - public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { 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; } - } + property Platform::String + ^ 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); - static ViewMode Deserialize(Platform::Object^ obj); - static ViewMode GetViewModeForFriendlyName(Platform::String^ name); + static ViewMode Deserialize(Platform::Object ^ obj); + 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); - static Platform::String^ GetFriendlyName(ViewMode mode); - static Platform::String^ GetNameResourceKey(ViewMode mode); + static Platform::String ^ GetFriendlyName(ViewMode mode); + static Platform::String ^ GetNameResourceKey(ViewMode mode); static CategoryGroupType GetGroupType(ViewMode mode); // GetIndex is 0-based, GetPosition is 1-based @@ -178,17 +152,24 @@ namespace CalculatorApp static ViewMode GetViewModeForVirtualKey(MyVirtualKey virtualKey); - internal: - NavCategory(Platform::String^ name, Platform::String^ automationName, Platform::String^ glyph, Platform::String^ accessKey, Platform::String^ mode, ViewMode viewMode, bool supportsNegative) : - m_name(name), - m_automationName(automationName), - m_glyph(glyph), - m_accessKey(accessKey), - m_mode(mode), - m_viewMode(viewMode), - m_supportsNegative(supportsNegative) + internal : NavCategory( + Platform::String ^ name, + Platform::String ^ automationName, + Platform::String ^ glyph, + Platform::String ^ accessKey, + Platform::String ^ mode, + ViewMode viewMode, + 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 GetCategoryAcceleratorKeys(); @@ -196,39 +177,25 @@ 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 + [Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - public: OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_R(Platform::String^, Name); - OBSERVABLE_PROPERTY_R(Platform::String^, AutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, Name); + OBSERVABLE_PROPERTY_R(Platform::String ^, AutomationName); OBSERVABLE_PROPERTY_R(CategoryGroupType, GroupType); - OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector^, Categories); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Categories); - static Windows::Foundation::Collections::IObservableVector^ CreateMenuOptions(); + static Windows::Foundation::Collections::IObservableVector ^ CreateMenuOptions(); - static Platform::String^ GetHeaderResourceKey(CategoryGroupType type); - - internal: - static NavCategoryGroup^ CreateCalculatorCategory(); - static NavCategoryGroup^ CreateConverterCategory(); + internal : static NavCategoryGroup ^ CreateCalculatorCategory(); + static NavCategoryGroup ^ CreateConverterCategory(); private: NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer); - - static std::vector GetInitializerCategoryGroup(CategoryGroupType groupType); }; } } diff --git a/src/CalcViewModel/Common/NetworkManager.cpp b/src/CalcViewModel/Common/NetworkManager.cpp index 5e37a05d..8bf3835a 100644 --- a/src/CalcViewModel/Common/NetworkManager.cpp +++ b/src/CalcViewModel/Common/NetworkManager.cpp @@ -10,9 +10,8 @@ using namespace Windows::Networking::Connectivity; NetworkManager::NetworkManager() { - m_NetworkStatusChangedToken = - NetworkInformation::NetworkStatusChanged += ref new NetworkStatusChangedEventHandler( - this, &NetworkManager::OnNetworkStatusChange, CallbackContext::Same); + m_NetworkStatusChangedToken = NetworkInformation::NetworkStatusChanged += + ref new NetworkStatusChangedEventHandler(this, &NetworkManager::OnNetworkStatusChange, CallbackContext::Same); } NetworkManager::~NetworkManager() @@ -23,14 +22,13 @@ NetworkManager::~NetworkManager() NetworkAccessBehavior NetworkManager::GetNetworkAccessBehavior() { NetworkAccessBehavior behavior = NetworkAccessBehavior::Offline; - ConnectionProfile^ connectionProfile = NetworkInformation::GetInternetConnectionProfile(); + ConnectionProfile ^ connectionProfile = NetworkInformation::GetInternetConnectionProfile(); if (connectionProfile != nullptr) { NetworkConnectivityLevel connectivityLevel = connectionProfile->GetNetworkConnectivityLevel(); - if (connectivityLevel == NetworkConnectivityLevel::InternetAccess - || connectivityLevel == NetworkConnectivityLevel::ConstrainedInternetAccess) + if (connectivityLevel == NetworkConnectivityLevel::InternetAccess || connectivityLevel == NetworkConnectivityLevel::ConstrainedInternetAccess) { - ConnectionCost^ connectionCost = connectionProfile->GetConnectionCost(); + ConnectionCost ^ connectionCost = connectionProfile->GetConnectionCost(); behavior = ConvertCostInfoToBehavior(connectionCost); } } @@ -38,16 +36,15 @@ NetworkAccessBehavior NetworkManager::GetNetworkAccessBehavior() return behavior; } -void NetworkManager::OnNetworkStatusChange(_In_ Object^ /*sender*/) +void NetworkManager::OnNetworkStatusChange(_In_ Object ^ /*sender*/) { NetworkBehaviorChanged(GetNetworkAccessBehavior()); } // See app behavior guidelines at https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj835821(v=win.10).aspx -NetworkAccessBehavior NetworkManager::ConvertCostInfoToBehavior(_In_ ConnectionCost^ connectionCost) +NetworkAccessBehavior NetworkManager::ConvertCostInfoToBehavior(_In_ ConnectionCost ^ connectionCost) { - if (connectionCost->Roaming || connectionCost->OverDataLimit - || connectionCost->NetworkCostType == NetworkCostType::Variable + if (connectionCost->Roaming || connectionCost->OverDataLimit || connectionCost->NetworkCostType == NetworkCostType::Variable || connectionCost->NetworkCostType == NetworkCostType::Fixed) { return NetworkAccessBehavior::OptIn; diff --git a/src/CalcViewModel/Common/NetworkManager.h b/src/CalcViewModel/Common/NetworkManager.h index cb34ab9a..51903d94 100644 --- a/src/CalcViewModel/Common/NetworkManager.h +++ b/src/CalcViewModel/Common/NetworkManager.h @@ -5,29 +5,32 @@ namespace CalculatorApp { - public enum class NetworkAccessBehavior +public + enum class NetworkAccessBehavior { Normal = 0, OptIn = 1, Offline = 2 }; - public delegate void NetworkBehaviorChangedHandler(NetworkAccessBehavior behavior); +public + delegate void NetworkBehaviorChangedHandler(NetworkAccessBehavior behavior); - public ref class NetworkManager sealed +public + ref class NetworkManager sealed { public: NetworkManager(); static NetworkAccessBehavior GetNetworkAccessBehavior(); - event NetworkBehaviorChangedHandler^ NetworkBehaviorChanged; + event NetworkBehaviorChangedHandler ^ NetworkBehaviorChanged; private: ~NetworkManager(); - void OnNetworkStatusChange(_In_ Platform::Object^ sender); - static NetworkAccessBehavior ConvertCostInfoToBehavior(_In_ Windows::Networking::Connectivity::ConnectionCost^ connectionCost); + void OnNetworkStatusChange(_In_ Platform::Object ^ sender); + static NetworkAccessBehavior ConvertCostInfoToBehavior(_In_ Windows::Networking::Connectivity::ConnectionCost ^ connectionCost); private: Windows::Foundation::EventRegistrationToken m_NetworkStatusChangedToken; diff --git a/src/CalcViewModel/Common/NumberBase.h b/src/CalcViewModel/Common/NumberBase.h new file mode 100644 index 00000000..4e15efa1 --- /dev/null +++ b/src/CalcViewModel/Common/NumberBase.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +namespace CalculatorApp::Common +{ +public + enum class NumberBase + { + Unknown = -1, + HexBase = 5, + DecBase = 6, + OctBase = 7, + BinBase = 8 + }; +}; diff --git a/src/CalcViewModel/Common/TraceActivity.h b/src/CalcViewModel/Common/TraceActivity.h deleted file mode 100644 index 120dc25c..00000000 --- a/src/CalcViewModel/Common/TraceActivity.h +++ /dev/null @@ -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; - }; -} diff --git a/src/CalcViewModel/Common/TraceLogger.cpp b/src/CalcViewModel/Common/TraceLogger.cpp index bbf6e605..f58513de 100644 --- a/src/CalcViewModel/Common/TraceLogger.cpp +++ b/src/CalcViewModel/Common/TraceLogger.cpp @@ -8,981 +8,319 @@ using namespace CalculatorApp; using namespace CalculatorApp::Common; +using namespace TraceLogging; using namespace Concurrency; using namespace std; -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Foundation::Diagnostics; -using namespace winrt::Windows::Globalization; -using namespace winrt::Windows::Globalization::DateTimeFormatting; -using namespace winrt::Windows::System::UserProfile; +using namespace Platform; +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 { - static multimap> s_memoryMap; - - static constexpr array s_programmerType{ - L"N/A", L"QwordType", L"DwordType", - L"WordType", L"ByteType", L"HexBase", - L"DecBase", L"OctBase", L"BinBase" }; + static constexpr array s_programmerType{ L"N/A", L"QwordType", L"DwordType", L"WordType", L"ByteType", + L"HexBase", L"DecBase", L"OctBase", L"BinBase" }; static reader_writer_lock s_traceLoggerLock; - // Telemetry events. Uploaded to asimov. - constexpr auto EVENT_NAME_DEBUG = L"Debug"; - constexpr auto EVENT_NAME_ERROR = L"ErrorMessage"; - constexpr auto EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM = L"AppPrelaunchedBySystem"; - constexpr auto EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER = L"PrelaunchedAppActivatedByUser"; - constexpr auto EVENT_NAME_APP_LAUNCH_BEGIN = L"AppLaunchBegin"; - constexpr auto EVENT_NAME_APP_LAUNCH_END = L"AppLaunchEnd"; - constexpr auto EVENT_NAME_APP_RESUME_END = L"AppResumeEnd"; - constexpr auto EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION = L"PreviousStateOnWindowCreation"; - constexpr auto EVENT_NAME_SIZE_ON_SUSPENSION = L"CalculatorSizeOnSuspension"; - constexpr auto EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION = L"CalculatorViewedInSession"; - constexpr auto EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION = L"DateCalculatorViewedInSession"; - constexpr auto EVENT_NAME_CONVERTER_VIEWED_IN_SESSION = L"ConverterViewedInSession"; - constexpr auto EVENT_NAME_MODE_CHANGE_BEGIN = L"ModeChangeBegin"; - constexpr auto EVENT_NAME_MODE_CHANGE_END = L"ModeChangeEnd"; - constexpr auto EVENT_NAME_HISTORY_BODY_OPENED = L"HistoryBodyOpened"; - constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN = L"HistoryItemLoadBegin"; - constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_END = L"HistoryItemLoadEnd"; - constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN = L"HistoryFlyoutOpenBegin"; - constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_END = L"HistoryFlyoutOpenEnd"; - constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_BEGIN = L"NewWindowCreationBegin"; - constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_END = L"NewWindowCreationEnd"; - constexpr auto EVENT_NAME_HISTORY_CLEAR = L"HistoryClearBegin"; - constexpr auto EVENT_NAME_MULTIPLE_MEMORY_USED = L"MultipleMemoryUsed"; - constexpr auto EVENT_NAME_SINGLE_MEMORY_USED = L"SingleMemoryUsed"; - constexpr auto EVENT_NAME_SHARED_MEMORY_USED = L"SharedMemoryUsed"; - constexpr auto EVENT_NAME_MEMORY_BODY_OPENED = L"MemoryBodyOpened"; - constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN = L"MemoryFlyoutOpenBegin"; - constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_END = L"MemoryFlyoutOpenEnd"; - constexpr auto EVENT_NAME_MEMORY_CLEAR_ALL = L"MemoryClearAll"; - constexpr auto EVENT_NAME_INVALID_PASTED_INPUT_OCCURRED = L"InvalidPastedInputOccurred"; - constexpr auto EVENT_NAME_VALID_INPUT_PASTED = L"ValidInputPasted"; - constexpr auto EVENT_NAME_BITFLIP_PANE_CLICKED = L"BitFlipPaneClicked"; - constexpr auto EVENT_NAME_BITFLIP_BUTTONS_USED = L"BitFlipToggleButtonUsed"; - constexpr auto EVENT_NAME_ANGLE_BUTTONS_USED = L"AngleButtonUsedInSession"; - constexpr auto EVENT_NAME_HYP_BUTTON_USED = L"HypButtonUsedInSession"; - constexpr auto EVENT_NAME_FUNCTION_USAGE = L"FunctionUsageInSession"; - constexpr auto EVENT_NAME_BITLENGTH_BUTTON_USED = L"BitLengthButtonUsed"; - constexpr auto EVENT_NAME_RADIX_BUTTON_USED = L"RadixButtonUsed"; - constexpr auto EVENT_NAME_MAX_WINDOW_COUNT = L"MaxWindowCountInSession"; - constexpr auto EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL = L"WindowActivatedThroughProtocol"; - constexpr auto EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH = L"WindowLaunchedThroughTile"; - constexpr auto EVENT_NAME_DATE_DIFFERENCE_USED = L"DateDifferenceModeUsed"; - constexpr auto EVENT_NAME_DATE_ADD_SUBTRACT_USED = L"DateAddSubtractModeUsed"; - constexpr auto EVENT_NAME_DATE_DIFFERENCE_FOUND = L"DateDifferenceFound"; - constexpr auto EVENT_NAME_HIDE_IF_SHOWN = L"HideIfShown"; - constexpr auto EVENT_NAME_ABOUT_FLYOUT_OPENED = L"AboutFlyoutOpened"; - constexpr auto EVENT_NAME_NAV_BAR_OPENED = L"NavBarOpened"; - constexpr auto EVENT_NAME_CORE_WINDOW_WAS_NULL = L"CoreWindowWasNull"; + // Diagnostics events. Uploaded to asimov. + constexpr auto EVENT_NAME_WINDOW_ON_CREATED = L"WindowCreated"; + constexpr auto EVENT_NAME_BUTTON_USAGE = L"ButtonUsageInSession"; + constexpr auto EVENT_NAME_NAV_BAR_OPENED = L"NavigationViewOpened"; + constexpr auto EVENT_NAME_MODE_CHANGED = L"ModeChanged"; + constexpr auto EVENT_NAME_DATE_CALCULATION_MODE_USED = L"DateCalculationModeUsed"; + constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD = L"HistoryItemLoad"; + constexpr auto EVENT_NAME_MEMORY_ITEM_LOAD = L"MemoryItemLoad"; + 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 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_TELEMETRY - // c.f. WINEVENT_KEYWORD_RESERVED_63-56 0xFF00000000000000 // Bits 63-56 - channel keywords - // c.f. WINEVENT_KEYWORD_* 0x00FF000000000000 // Bits 55-48 - system-reserved keywords - constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0x0000800000000000; // Bit 47 - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0x0000400000000000; // Bit 46 - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0x0000200000000000; // Bit 45 - constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment) -#else - // define all Keyword options as 0 when we do not want to upload app telemetry - constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0; - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0; - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0; - constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0; -#endif + 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 }), // Microsoft Telemetry group - GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 }), // Unique providerID {0905CA09-610E-401E-B650-2F212980B9E0} - m_appLaunchActivity{ nullptr } - { - // initialize the function array - InitFunctionLogArray(); - } - - TraceLogger::~TraceLogger() + TraceLogger::TraceLogger() { } - TraceLogger& TraceLogger::GetInstance() + TraceLogger ^ TraceLogger::GetInstance() { - static TraceLogger s_selfInstance; + static TraceLogger ^ s_selfInstance = ref new TraceLogger(); return s_selfInstance; } - bool TraceLogger::GetTraceLoggingProviderEnabled() const - { - return g_calculatorProvider.Enabled(); - } - -#pragma region Tracing methods - void TraceLogger::LogTelemetryEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY)); - } - - void TraceLogger::LogMeasureEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_MEASURES)); - } - - void TraceLogger::LogCriticalDataEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_CRITICAL_DATA)); - } - - void TraceLogger::LogPerformanceEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(WINEVENT_KEYWORD_RESPONSE_TIME)); - } - - void TraceLogger::LogInfoEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Information); - } - - unique_ptr TraceLogger::CreateTraceActivity(wstring_view eventName, LoggingFields fields) const - { - return make_unique(g_calculatorProvider, eventName, fields); - } -#pragma endregion - - void TraceLogger::InsertIntoMemoryMap(int windowId, bool isStandard, bool isScientific, bool isProgrammer) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - auto iterMap = s_memoryMap.find(windowId); - if (iterMap == s_memoryMap.end()) - { - s_memoryMap.insert(std::make_pair(windowId, vector())); - iterMap = s_memoryMap.find(windowId); - } - - if (isScientific) - { - iterMap->second.insert(iterMap->second.begin(), L"Scientific"); - } - else if (isProgrammer) - { - iterMap->second.insert(iterMap->second.begin(), L"Programmer"); - } - else if (isStandard) - { - iterMap->second.insert(iterMap->second.begin(), L"Standard"); - } - } - - void TraceLogger::UpdateMemoryMap(int windowId, int memoryPosition, bool isStandard, bool isScientific, bool isProgrammer) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - auto iterMap = s_memoryMap.find(windowId); - assert(iterMap != s_memoryMap.end()); - assert(iterMap->second.size() >= (unsigned int)memoryPosition); - - if (isScientific) - { - iterMap->second[memoryPosition] = L"Scientific"; - } - else if (isProgrammer) - { - iterMap->second[memoryPosition] = L"Programmer"; - } - else if (isStandard) - { - iterMap->second[memoryPosition] = L"Standard"; - } - } - - void TraceLogger::DeleteFromMemoryMap(int windowId, int memoryPosition) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - assert(iterMap != s_memoryMap.end()); - - iterMap->second.erase(iterMap->second.begin() + memoryPosition); - } - // return true if windowId is logged once else return false - bool TraceLogger::UpdateWindowIdLog(int windowId) + bool TraceLogger::IsWindowIdInLog(int windowId) { - // Writer lock for the static resources + // Writer lock for the windowIdLog resource reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - return false; - } - if (windowIdLog[windowId] == false) - { - windowIdLog[windowId] = true; - return true; - } - else + if (find(windowIdLog.begin(), windowIdLog.end(), windowId) == windowIdLog.end()) { return false; } + + return true; } - // call comes here at the time of ApplicationViewModel initialisation - void TraceLogger::LogCalculatorModeViewed(ViewMode mode, int windowId) + void TraceLogger::LogVisualStateChanged(ViewMode mode, String ^ state, bool isAlwaysOnTop) { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); + auto fields = ref new LoggingFields(); + 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) + { // store windowId in windowIdLog which says we have logged mode for the present windowId. - if (windowIdLog.find(windowId) == windowIdLog.end()) + if (!IsWindowIdInLog(windowId)) { - windowIdLog.insert(pair(windowId, false)); + windowIdLog.push_back(windowId); } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - LoggingFields fields{}; - fields.AddString(L"CalculatorMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION, fields); + 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 (NavCategory::IsValidViewMode(mode)) + { + auto fields = ref new LoggingFields(); + ; + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MODE_CHANGED), fields); } } - // call comes here at the time of ApplicationViewModel initialization - void TraceLogger::LogDateCalculatorModeViewed(ViewMode mode, int windowId) + void TraceLogger::LogHistoryItemLoad(ViewMode mode, int historyListSize, int loadedIndex) { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - // store windowId in windowIdLog which says we have logged mode for the present windowId. - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - windowIdLog.insert(pair(windowId, false)); - } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - - LoggingFields fields{}; - fields.AddString(L"DateCalculatorMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION, fields); - } + 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); } - // call comes here at the time of ApplicationViewModel initialization - void TraceLogger::LogConverterModeViewed(ViewMode mode, int windowId) + void TraceLogger::LogMemoryItemLoad(ViewMode mode, int memoryListSize, int loadedIndex) { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - windowIdLog.insert(pair(windowId, false)); - } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - - LoggingFields fields{}; - fields.AddString(L"ConverterMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_CONVERTER_VIEWED_IN_SESSION, fields); - } + 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::LogSharedMemoryUsed(wstring_view fromMode, wstring_view toMode, unsigned int memorySize) const + void TraceLogger::LogError(ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"FromMode", fromMode); - fields.AddString(L"ToMode", toMode); - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_SHARED_MEMORY_USED, fields); + 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::LogBitFlipPaneClicked() const + void TraceLogger::LogStandardException(ViewMode mode, wstring_view functionName, const exception& e) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_BITFLIP_PANE_CLICKED, fields); - } - - void TraceLogger::LogBitFlipUsed() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_BITFLIP_BUTTONS_USED, fields); - } - - void TraceLogger::LogAppLaunchStart() - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if (!isAppLaunchBeginLogged) - { - m_appLaunchActivity = g_calculatorProvider.StartActivity( - EVENT_NAME_APP_LAUNCH_BEGIN, - nullptr, - LoggingLevel::Verbose, - LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY)); - - isAppLaunchBeginLogged = true; - } - } - - void TraceLogger::LogAppLaunchComplete(/*Windows::ApplicationModel::Activation::ActivationKind activationKind, Windows::ApplicationModel::Activation::ApplicationExecutionState executionState*/) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if ((m_appLaunchActivity != nullptr) && (!isAppLaunchEndLogged)) - { - m_appLaunchActivity.StopActivity(EVENT_NAME_APP_LAUNCH_END); - - isAppLaunchEndLogged = true; - } - - m_appLaunchActivity = nullptr; - } - - void TraceLogger::LogAppResumeComplete() - { - if (m_appLaunchActivity != nullptr) - { - m_appLaunchActivity.StopActivity(EVENT_NAME_APP_RESUME_END); - } - - m_appLaunchActivity = nullptr; - } - - void TraceLogger::LogDebug(wstring_view debugData) - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"DebugData", debugData); - LogTelemetryEvent(EVENT_NAME_DEBUG, fields); - } - - void TraceLogger::LogOnAppLaunch(wstring_view previousExecutionState) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"PreviousExecutionState", previousExecutionState); - LogTelemetryEvent(EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION, fields); - } - - void TraceLogger::LogAboutFlyoutOpened() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_ABOUT_FLYOUT_OPENED, fields); - } - - void TraceLogger::LogNavBarOpened() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_NAV_BAR_OPENED, fields); - } - - void TraceLogger::LogClearHistory() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_CLEAR, fields); - } - - void TraceLogger::LogHistoryFlyoutOpenBegin(unsigned int historyItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - // we want to record the event only when history item count is atleast 20 - if (historyItemCount >= 20) - { - LoggingFields fields{}; - fields.AddUInt32(L"HistoryItemCount", historyItemCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN, fields); - } - } - - void TraceLogger::LogHistoryFlyoutOpenEnd(int historyItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - if (historyItemCount >= 20) - { - LoggingFields fields{}; - fields.AddUInt32(L"HistoryItemCount", historyItemCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_END, fields); - } - } - - void TraceLogger::LogHistoryBodyOpened() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_BODY_OPENED, fields); - } - - void TraceLogger::LogMemoryFlyoutOpenBegin(unsigned int memoryItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - // we want to record the event only when memory item count is atleast 4 - if (memoryItemCount >= 4) - { - LoggingFields fields{}; - fields.AddUInt32(L"MemoryItemCount", memoryItemCount); - LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN, fields); - } - } - - void TraceLogger::LogMemoryFlyoutOpenEnd(unsigned int memoryItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - if (memoryItemCount >= 4) - { - LoggingFields fields{}; - fields.AddUInt32(L"MemoryItemCount", memoryItemCount); - LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_END, fields); - } - } - - void TraceLogger::LogMemoryBodyOpened() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_MEMORY_BODY_OPENED, fields); - } - - // If calculator is launched in any mode other than standard then this call will come which is not intended. But there is no way to avoid it. - // So don't use this function to analyze the count of mode change in session instead use CalculatorViewedInSession and ConverterViewedInSession to do that. - // Use of this function is to analyze perf of mode change. - void TraceLogger::LogModeChangeBegin(ViewMode fromMode, ViewMode toMode, int windowId) - { - if (!GetTraceLoggingProviderEnabled()) return; - - if (NavCategory::IsValidViewMode(fromMode) && NavCategory::IsValidViewMode(toMode)) - { - LoggingFields fields{}; - fields.AddString(L"FromMode", NavCategory::GetFriendlyName(fromMode)->Data()); - fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data()); - fields.AddInt32(L"WindowId", windowId); - LogMeasureEvent(EVENT_NAME_MODE_CHANGE_BEGIN, fields); - } - } - - // comment: same as LogModeChangeBegin - void TraceLogger::LogModeChangeEnd(ViewMode toMode, int windowId) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - if (NavCategory::IsValidViewMode(toMode)) - { - LoggingFields fields{}; - fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data()); - fields.AddInt32(L"WindowId", windowId); - LogMeasureEvent(EVENT_NAME_MODE_CHANGE_END, fields); - } - } - - void TraceLogger::LogHistoryItemLoadBegin() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN, fields); - } - - void TraceLogger::LogHistoryItemLoadEnd(unsigned int historyTokenCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"HistoryTokenCount", historyTokenCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_END, fields); - } - - void TraceLogger::LogNewWindowCreationBegin(int windowId) - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_BEGIN, fields); - } - - void TraceLogger::LogNewWindowCreationEnd(int windowId) - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_END, fields); - } - - void TraceLogger::LogError(wstring_view errorString) - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"ErrorString", errorString); - LogTelemetryEvent(EVENT_NAME_ERROR, fields); - } - - void TraceLogger::LogPrelaunchedAppActivatedByUser() - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER, fields); - } - - void TraceLogger::LogAppPrelaunchedBySystem() - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM, fields); - } - - void TraceLogger::LogMemoryClearAll(int windowId) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_MEMORY_CLEAR_ALL, fields); - - if (iterMap != s_memoryMap.end()) - { - iterMap->second.clear(); - } - } - - void TraceLogger::LogMemoryUsed(int windowId, unsigned int slotPosition, bool isStandard, bool isScientific, bool isProgrammer, unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - // Reader lock for the static resources - reader_writer_lock::scoped_lock_read lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - - if (slotPosition == 0) - { - LogSingleMemoryUsed(memorySize); - } - else - { - LogMultipleMemoryUsed(slotPosition, memorySize); - } - - if (iterMap != s_memoryMap.end()) - { - // if current mode is not equal to mode of memoryMap[slotPosition] then LogSharedMemoryUsed - if (isStandard && (iterMap->second[slotPosition] != L"Standard")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Standard", memorySize); - } - else if (isScientific && (iterMap->second[slotPosition] != L"Scientific")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Scientific", memorySize); - } - else if (isProgrammer && (iterMap->second[slotPosition] != L"Programmer")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Programmer", memorySize); - } - } - } - - void TraceLogger::LogMultipleMemoryUsed(unsigned int slotPosition, unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"MemoryIndex", slotPosition); - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_MULTIPLE_MEMORY_USED, fields); - } - - void TraceLogger::LogSingleMemoryUsed(unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_SINGLE_MEMORY_USED, fields); - } - - void TraceLogger::LogInvalidPastedInputOccurred(wstring_view reason, ViewMode mode, int programmerNumberBase, int bitLengthType) - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddString(L"Reason", reason); - fields.AddString(L"ProgrammerNumberBase", GetProgrammerType(programmerNumberBase).c_str()); - fields.AddString(L"BitLengthType", GetProgrammerType(bitLengthType).c_str()); - fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogTelemetryEvent(EVENT_NAME_INVALID_PASTED_INPUT_OCCURRED, fields); - } - - void TraceLogger::LogValidInputPasted(ViewMode mode) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data()); - LogTelemetryEvent(EVENT_NAME_VALID_INPUT_PASTED, fields); - } - - void TraceLogger::LogStandardException(wstring_view functionName, const exception& e) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"FunctionName", functionName); + 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"ExceptionMessage", exceptionMessage.str()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + fields->AddString(StringReference(L"Message"), StringReference(exceptionMessage.str().data())); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); } - void TraceLogger::LogWinRTException(wstring_view functionName, hresult_error const& e) const + void TraceLogger::LogPlatformException(ViewMode mode, wstring_view functionName, Platform::Exception ^ e) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddString(L"FunctionName", functionName); - fields.AddInt32(L"HRESULT", e.code()); - fields.AddString(L"ExceptionMessage", e.message()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + 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::LogPlatformException(wstring_view functionName, Platform::Exception^ e) const + void TraceLogger::UpdateButtonUsage(NumbersAndOperatorsEnum button, ViewMode mode) { - if (!GetTraceLoggingProviderEnabled()) return; + // IsProgrammerMode, IsScientificMode, IsStandardMode and None are not actual buttons, so ignore them + if (button == NumbersAndOperatorsEnum::IsProgrammerMode || button == NumbersAndOperatorsEnum::IsScientificMode + || button == NumbersAndOperatorsEnum::IsStandardMode || button == NumbersAndOperatorsEnum::None) + { + return; + } - LoggingFields fields{}; - fields.AddString(L"FunctionName", functionName); - fields.AddInt32(L"HRESULT", e->HResult); - fields.AddString(L"ExceptionMessage", e->Message->Data()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + { + // Writer lock for the buttonLog resource + reader_writer_lock::scoped_lock lock(s_traceLoggerLock); + + vector::iterator it = std::find_if( + buttonLog.begin(), buttonLog.end(), [button, mode](const ButtonLog& bLog) -> bool { return bLog.button == button && bLog.mode == mode; }); + if (it != buttonLog.end()) + { + it->count++; + } + else + { + buttonLog.push_back(ButtonLog(button, mode)); + } + } + + // Periodically log the button usage so that we do not lose all button data if the app is foricibly closed or crashes + if (buttonLog.size() >= 10) + { + LogButtonUsage(); + } } - void TraceLogger::UpdateFunctionUsage(int funcIndex) + void TraceLogger::UpdateWindowCount(uint64 windowCount) { - // Writer lock for the static resources + if (windowCount == 0) + { + currentWindowCount--; + return; + } + currentWindowCount = windowCount; + } + + void TraceLogger::DecreaseWindowCount() + { + currentWindowCount = 0; + } + + void TraceLogger::LogButtonUsage() + { + // Writer lock for the buttonLog resource reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - if (GetIndex(funcIndex)) + if (buttonLog.size() == 0) { - // funcIndex is passed by reference and will be having the returned index - funcLog[funcIndex].count++; + return; } - } - void TraceLogger::InitFunctionLogArray() - { - int i = -1; - for (int funcIndex = 0; funcIndex != maxFunctionSize; funcIndex++) + Platform::String ^ buttonUsageString; + for (size_t i = 0; i < buttonLog.size(); i++) { - FunctionLogEnum func = safe_cast(funcIndex); - wstring functionName = func.ToString()->Data(); - if (functionName.compare(L"CalculatorApp.FunctionLogEnum") != 0) + buttonUsageString += NavCategory::GetFriendlyName(buttonLog[i].mode); + buttonUsageString += "|"; + buttonUsageString += buttonLog[i].button.ToString(); + buttonUsageString += "|"; + buttonUsageString += buttonLog[i].count; + if (i != buttonLog.size() - 1) { - findIndex[funcIndex] = ++i; - funcLog.push_back(FuncLog(functionName)); + buttonUsageString += ","; } } - // update the functionCount with total function count which we are tracking through tracelog. - functionCount = i; + + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(L"ButtonUsage"), buttonUsageString); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_BUTTON_USAGE), fields); + + buttonLog.clear(); } - wstring TraceLogger::GetProgrammerType(int index) + void TraceLogger::LogDateCalculationModeUsed(bool AddSubtractMode) { - if (index >= 0) - { - return s_programmerType[index]; - } - //return "N/A" - return s_programmerType[0]; + const wchar_t* calculationType = AddSubtractMode ? L"AddSubtractMode" : L"DateDifferenceMode"; + 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); } - bool TraceLogger::GetIndex(int &index) + void TraceLogger::LogConverterInputReceived(ViewMode mode) { - if (findIndex[index] > 0) - { - index = findIndex[index]; - return true; - } - return false; + 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::UpdateWindowCount(size_t windowCount) + void TraceLogger::LogNavBarOpened() { - maxWindowCount = (maxWindowCount > windowCount) ? maxWindowCount : windowCount; - windowLaunchCount++; + auto fields = ref new LoggingFields(); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_NAV_BAR_OPENED), fields); } - void TraceLogger::LogMaxWindowCount() + void TraceLogger::LogInputPasted(ViewMode mode) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowCount", (unsigned int)maxWindowCount); - LogTelemetryEvent(EVENT_NAME_MAX_WINDOW_COUNT, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_INPUT_PASTED), fields); } - void TraceLogger::LogWindowActivated() const + void TraceLogger::LogShowHideButtonClicked(bool isHideButton) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL, fields); + 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::LogWindowLaunched() const + void TraceLogger::LogGraphButtonClicked(GraphButton buttonName, GraphButtonValue buttonValue) { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(StringReference(L"ButtonName"), static_cast(buttonName)); + fields->AddInt16(StringReference(L"ButtonValue"), static_cast(buttonValue)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_BUTTON_CLICKED), fields); } - void TraceLogger::LogBitLengthButtonUsed(int windowId) + void TraceLogger::LogGraphLineStyleChanged(LineStyleType style) { - if (bitLengthButtonUsage.find(windowId) == bitLengthButtonUsage.end()) - { - bitLengthButtonUsage.insert(pair(windowId, 1)); - } - else - { - bitLengthButtonUsage[windowId]++; - } - if ((bitLengthButtonUsage[windowId] == 5) && !bitLengthButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_BITLENGTH_BUTTON_USED, fields); - - bitLengthButtonLoggedInSession = true; - } + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(StringReference(L"StyleType"), static_cast(style)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_LINE_STYLE_CHANGED), fields); } - void TraceLogger::LogRadixButtonUsed(int windowId) + void TraceLogger::LogVariableChanged(String ^ inputChangedType, String ^ variableName) { - if (radixButtonUsage.find(windowId) == radixButtonUsage.end()) - { - radixButtonUsage.insert(pair(windowId, 1)); - } - else - { - radixButtonUsage[windowId]++; - } - if ((radixButtonUsage[windowId] == 2) && !radixButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_RADIX_BUTTON_USED, fields); - - radixButtonLoggedInSession = true; - } + 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::LogAngleButtonUsed(int windowId) + void TraceLogger::LogGraphSettingsChanged(GraphSettingsType settingType, String ^ settingValue) { - if (angleButtonUsage.find(windowId) == angleButtonUsage.end()) - { - angleButtonUsage.insert(pair(windowId, 1)); - } - else - { - angleButtonUsage[windowId]++; - } - if ((angleButtonUsage[windowId] == 2) && !angleButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_ANGLE_BUTTONS_USED, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(L"SettingType", static_cast(settingType)); + fields->AddString(L"SettingValue", settingValue); - angleButtonLoggedInSession = true; - } + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_SETTINGS_CHANGED), fields); } - void TraceLogger::LogFunctionUsage(int windowId) + void TraceLogger::LogGraphTheme(String ^ graphTheme) { - if (!GetTraceLoggingProviderEnabled()) return; + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddString(L"GraphTheme", graphTheme); - for (int i = 0; i < functionCount; i++) - { - // log only those functions which are used - if (funcLog[i].count > 0) - { - LoggingFields fields{}; - fields.AddString(L"FunctionName", funcLog[i].funcName.data()); - fields.AddUInt32(L"UsageCount", funcLog[i].count); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_FUNCTION_USAGE, fields); - } - } - } - - void TraceLogger::LogHypButtonUsed(int windowId) - { - if (!isHypButtonLogged) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_HYP_BUTTON_USED, fields); - - isHypButtonLogged = true; - } - } - - void TraceLogger::LogDateDifferenceModeUsed(int windowId) - { - if (!m_dateDiffUsageLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_USED, fields); - - m_dateDiffUsageLoggedInSession = true; - } - } - - void TraceLogger::LogDateAddSubtractModeUsed(int windowId, bool isAddMode) - { - auto usageMap = isAddMode ? &m_dateAddModeUsage : &m_dateSubtractModeUsage; - auto isLoggedInSession = isAddMode ? &m_dateAddUsageLoggedInSession : &m_dateSubtractUsageLoggedInSession; - - if (usageMap->find(windowId) == usageMap->end()) - { - usageMap->insert(pair(windowId, 1)); - } - else - { - (*usageMap)[windowId]++; - } - - // Ignore first 3 calls during the initialization of the combo box selected items for Add mode - int firstChangeEventCount = isAddMode ? 4 : 1; - - if (((*usageMap)[windowId] == firstChangeEventCount) - && (!(*isLoggedInSession))) - { - LoggingFields fields{}; - fields.AddString(L"AddSubtractMode", isAddMode ? L"Add" : L"Subtract"); - LogTelemetryEvent(EVENT_NAME_DATE_ADD_SUBTRACT_USED, fields); - - *isLoggedInSession = true; - } - } - - void TraceLogger::LogDateClippedTimeDifferenceFound(Calendar const& today, DateTime const& clippedTime) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - auto calendarSystem = today.GetCalendarSystem(); - auto clock = today.GetClock(); - DateTimeFormatter dtFormatter{ - L"longdate shorttime", - { L"en-US" }, - GlobalizationPreferences::HomeGeographicRegion(), - calendarSystem, - clock }; - - LoggingFields fields{}; - fields.AddString(L"ResolvedCalendarLanguage", today.ResolvedLanguage()); - fields.AddString(L"Timezone", today.GetTimeZone()); - fields.AddString(L"CalendarSystem", calendarSystem); - fields.AddString(L"Clock", clock); - fields.AddBoolean(L"IsDaylightSavingTime", today.IsDaylightSavingTime()); - fields.AddString(L"TodayDate", dtFormatter.Format(today.GetDateTime())); - fields.AddString(L"ClippedDate", dtFormatter.Format(clippedTime)); - LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_FOUND, fields); - } - - void TraceLogger::LogUserRequestedRefreshFailed() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(L"UserRequestedRefreshFailed", fields); - } - - void TraceLogger::LogConversionResult(wstring_view fromValue, wstring_view fromUnit, wstring_view toValue, wstring_view toUnit) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - wstring behaviorString{}; - NetworkAccessBehavior behavior = NetworkManager::GetNetworkAccessBehavior(); - switch (behavior) - { - case NetworkAccessBehavior::Offline: - behaviorString = L"Offline"; - break; - - case NetworkAccessBehavior::OptIn: - behaviorString = L"Metered"; - break; - - case NetworkAccessBehavior::Normal: - default: - behaviorString = L"Online"; - break; - } - - LoggingFields fields{}; - fields.AddString(L"NetworkAccess", behaviorString); - fields.AddString(L"FromValue", fromValue); - fields.AddString(L"FromUnit", fromUnit); - fields.AddString(L"ToValue", toValue); - fields.AddString(L"ToUnit", toUnit); - LogTelemetryEvent(L"CurrencyConverterInputReceived", fields); - } - - void TraceLogger::LogViewClosingTelemetry(int windowId) - { - LogFunctionUsage(windowId); - LogMaxWindowCount(); - } - - void TraceLogger::LogCoreWindowWasNull() const - { - if (!GetTraceLoggingProviderEnabled()) return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_CORE_WINDOW_WAS_NULL, fields); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_THEME), fields); } } - diff --git a/src/CalcViewModel/Common/TraceLogger.h b/src/CalcViewModel/Common/TraceLogger.h index 2b659047..cef592e8 100644 --- a/src/CalcViewModel/Common/TraceLogger.h +++ b/src/CalcViewModel/Common/TraceLogger.h @@ -3,155 +3,97 @@ #pragma once -#include "CalcManager/Command.h" -#include "TraceActivity.h" #include "NavCategory.h" - -static const int maxFunctionSize = (int)CalculationManager::Command::CommandBINEDITEND; +#include "CalculatorButtonUser.h" // A trace logging provider can only be instantiated and registered once per module. // This class implements a singleton model ensure that only one instance is created. namespace CalculatorApp { - struct FuncLog + struct ButtonLog { public: int count; - std::wstring funcName; - FuncLog() { count = 0; } - FuncLog(std::wstring fName) + CalculatorApp::NumbersAndOperatorsEnum button; + CalculatorApp::Common::ViewMode mode; + ButtonLog(CalculatorApp::NumbersAndOperatorsEnum btn, CalculatorApp::Common::ViewMode vMode) { - funcName = fName; - count = 0; + button = btn; + mode = vMode; + count = 1; } }; - class TraceLogger + 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: - TraceLogger(_In_ TraceLogger const&) = delete; - TraceLogger const & operator= (_In_ TraceLogger const&) = delete; - ~TraceLogger(); - static TraceLogger& GetInstance(); - bool GetTraceLoggingProviderEnabled() const; + static TraceLogger ^ GetInstance(); - void LogAppLaunchStart(); - void LogAppLaunchComplete(); - void LogAppResumeComplete(); - void LogOnAppLaunch(std::wstring_view previousExecutionState) const; - void LogMemoryClearAll(int); - void LogBitFlipPaneClicked() const; - void LogBitFlipUsed() const; - void LogHistoryBodyOpened() const; - void LogHistoryItemLoadBegin() const; - void LogHistoryItemLoadEnd(unsigned int) const; - void LogHistoryFlyoutOpenBegin(unsigned int) const; - void LogHistoryFlyoutOpenEnd(int) const; - void LogCalculatorModeViewed(CalculatorApp::Common::ViewMode, int); - void LogDateCalculatorModeViewed(CalculatorApp::Common::ViewMode, int); - void LogConverterModeViewed(CalculatorApp::Common::ViewMode, int); - void LogModeChangeBegin(CalculatorApp::Common::ViewMode, CalculatorApp::Common::ViewMode, int); - void LogModeChangeEnd(CalculatorApp::Common::ViewMode, int) const; - void LogClearHistory() const; - void InsertIntoMemoryMap(int, bool, bool, bool); - void UpdateMemoryMap(int, int, bool, bool, bool); - void DeleteFromMemoryMap(int, int); - void LogMemoryUsed(int, unsigned int, bool, bool, bool, unsigned int) const; - void LogMultipleMemoryUsed(unsigned int, unsigned int) const; - void LogSingleMemoryUsed(unsigned int) const; - void LogSharedMemoryUsed(std::wstring_view, std::wstring_view, unsigned int) const; - void LogMemoryBodyOpened() const; - void LogMemoryFlyoutOpenBegin(unsigned int) const; - void LogDebug(std::wstring_view debugData); - void LogMemoryFlyoutOpenEnd(unsigned int) const; - void LogInvalidPastedInputOccurred(std::wstring_view reason, CalculatorApp::Common::ViewMode mode, int ProgrammerNumberBase, int bitLengthType); - void LogValidInputPasted(CalculatorApp::Common::ViewMode mode) const; - void UpdateFunctionUsage(int func); - void LogFunctionUsage(int); - void InitFunctionLogArray(); - void LogBitLengthButtonUsed(int windowId); - void LogRadixButtonUsed(int windowId); - void LogAngleButtonUsed(int windowId); - void LogHypButtonUsed(int windowId); - void LogNewWindowCreationBegin(int windowId); - void LogNewWindowCreationEnd(int windowId); - void LogError(std::wstring_view errorString); - void LogPrelaunchedAppActivatedByUser(); - void LogAppPrelaunchedBySystem(); - void UpdateWindowCount(size_t windowCount); - bool UpdateWindowIdLog(int windowId); - void LogMaxWindowCount(); - void LogWindowActivated() const; - void LogWindowLaunched() const; - void LogUserRequestedRefreshFailed() const; - void LogConversionResult(std::wstring_view fromValue, std::wstring_view fromUnit, std::wstring_view toValue, std::wstring_view toUnit) const; - void LogAboutFlyoutOpened() const; - void LogNavBarOpened() const; - void LogViewClosingTelemetry(int); - void LogCoreWindowWasNull() const; - - // Trace methods for Date Calculator usage - void LogDateDifferenceModeUsed(int windowId); - void LogDateAddSubtractModeUsed(int windowId, bool isAddMode); - void LogDateClippedTimeDifferenceFound(winrt::Windows::Globalization::Calendar const& today, winrt::Windows::Foundation::DateTime const& clippedTime) const; - - void LogStandardException(std::wstring_view functionName, _In_ const std::exception& e) const; - void LogWinRTException(std::wstring_view functionName, _In_ winrt::hresult_error const& e) const; - void LogPlatformException(std::wstring_view functionName, _In_ Platform::Exception^ e) const; + 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); + void UpdateButtonUsage(CalculatorApp::NumbersAndOperatorsEnum button, CalculatorApp::Common::ViewMode mode); + void LogButtonUsage(); + void LogDateCalculationModeUsed(bool AddSubtractMode); + void UpdateWindowCount(uint64 windowCount); + void DecreaseWindowCount(); + bool IsWindowIdInLog(int windowId); + void LogVisualStateChanged(CalculatorApp::Common::ViewMode mode, Platform::String ^ state, bool isAlwaysOnTop); + void LogWindowCreated(CalculatorApp::Common::ViewMode mode, int windowId); + 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 LogPlatformException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ Platform::Exception ^ e); + void LogInputPasted(CalculatorApp::Common::ViewMode mode); private: // Create an instance of TraceLogger TraceLogger(); - // Any new Log method should - // a) decide the level of logging. This will help us in limiting recording of events only up to a certain level. See this link for guidance https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx - // We're using Verbose level for events that are called frequently and needed only for debugging or capturing perf for specific scenarios - // b) should decide whether or not to log to telemetry and pass TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY) accordingly - // c) Should accept a variable number of additional data arguments if needed - void LogTelemetryEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogMeasureEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogCriticalDataEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogPerformanceEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogInfoEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - - std::unique_ptr CreateTraceActivity(std::wstring_view activityName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - - winrt::Windows::Foundation::Diagnostics::LoggingChannel g_calculatorProvider; - - bool isSizeChangeLogged = false; - bool isHideIfShownLogged = false; - bool isSizeChangedFirstTime = true; // to track the first size changed event which is fired on the launch of app - bool isAutoConversionBeginLoggedInSession = false; - bool isAutoConversionEndLoggedInSession = false; - bool angleButtonLoggedInSession = false; - bool radixButtonLoggedInSession = false; - bool bitLengthButtonLoggedInSession = false; - GUID sessionGuid; - CalculatorApp::Common::ViewMode currentMode = CalculatorApp::Common::ViewMode::None; - std::vector funcLog; - int functionCount = 0; - bool isHypButtonLogged = false; - bool isAngleButtonInitialized = false; - unsigned int findIndex[maxFunctionSize] = { 0 }; - bool GetIndex(int &index); - std::wstring GetProgrammerType(int index); - size_t maxWindowCount = 0; - bool isAppLaunchBeginLogged = false; - bool isAppLaunchEndLogged = false; - std::map bitLengthButtonUsage; - std::map angleButtonUsage; - std::map radixButtonUsage; - std::map windowIdLog; - - // Private variables for Date Calculator usage - bool m_dateDiffUsageLoggedInSession = false; - bool m_dateAddUsageLoggedInSession = false; - bool m_dateSubtractUsageLoggedInSession = false; - std::map m_dateAddModeUsage; - std::map m_dateSubtractModeUsage; - - size_t windowLaunchCount = 0; - - winrt::Windows::Foundation::Diagnostics::LoggingActivity m_appLaunchActivity; + std::vector buttonLog; + std::vector windowIdLog; + uint64 currentWindowCount = 0; }; } diff --git a/src/CalcViewModel/Common/Utils.cpp b/src/CalcViewModel/Common/Utils.cpp index 0ab05b1d..6bcc527d 100644 --- a/src/CalcViewModel/Common/Utils.cpp +++ b/src/CalcViewModel/Common/Utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // @@ -10,19 +10,21 @@ #include "Common/AppResourceProvider.h" #include "Common/ExpressionCommandSerializer.h" #include "Common/ExpressionCommandDeserializer.h" -#include "ViewState.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace concurrency; +using namespace 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; @@ -30,28 +32,16 @@ void Utils::IFTPlatformException(HRESULT hr) { if (FAILED(hr)) { - Platform::Exception^ exception = ref new Platform::Exception(hr); + Platform::Exception ^ exception = ref new Platform::Exception(hr); throw(exception); } } -String^ Utils::GetStringValue(String^ input) -{ - // Remove first and last " characters - if (input->Length() >= 3) - { - wstring out(input->Begin() + 1, input->End() - 1); - return ref new String(out.c_str()); - } - return input; -} - double Utils::GetDoubleFromWstring(wstring input) { - wchar_t unWantedChars[] = { L' ', L',', 8234, 8235, 8236, 8237 }; - wstring ws = RemoveUnwantedCharsFromWstring(input, unWantedChars, 6); - string inputString(ws.begin(), ws.end()); - return ::atof(inputString.c_str()); + constexpr wchar_t unWantedChars[] = { L' ', L',', 8234, 8235, 8236, 8237 }; + wstring ws = RemoveUnwantedCharsFromString(input, unWantedChars); + return stod(ws); } // Returns windowId for the current view @@ -68,115 +58,20 @@ int Utils::GetWindowId() return windowId; } -void Utils::RunOnUIThreadNonblocking(std::function&& function, _In_ CoreDispatcher^ currentDispatcher) +void Utils::RunOnUIThreadNonblocking(std::function&& function, _In_ CoreDispatcher ^ currentDispatcher) { if (currentDispatcher != nullptr) { - auto task = create_task(currentDispatcher->RunAsync(CoreDispatcherPriority::Normal, - ref new DispatchedHandler([function]() - { - function(); - }))); + auto task = create_task(currentDispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([function]() { function(); }))); } } // Returns if the last character of a wstring is the target wchar_t -bool Utils::IsLastCharacterTarget(_In_ wstring const &input, _In_ wchar_t target) +bool Utils::IsLastCharacterTarget(_In_ wstring const& input, _In_ wchar_t target) { return !input.empty() && input.back() == target; } -// Returns wstring after removing characters like space, comma, and double quotes -wstring Utils::RemoveUnwantedCharsFromWstring(wstring input) -{ - wchar_t unWantedChars[] = { L' ', L',', L'"', 8234, 8235, 8236, 8237 }; - return RemoveUnwantedCharsFromWstring(input, unWantedChars, 6); -} - -// Returns wstring after removing characters specified by unwantedChars array -wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedChars, unsigned int size) -{ - for (unsigned int i = 0; i < size; ++i) - { - input.erase(std::remove(input.begin(), input.end(), unwantedChars[i]), input.end()); - } - return input; -} - -void Utils::SerializeCommandsAndTokens(_In_ shared_ptr>> const &tokens, - _In_ shared_ptr>> const &commands, - DataWriter^ writer) -{ - unsigned int commandsSize; - IFTPlatformException(commands->GetSize(&commandsSize)); - - // Save the size of the commands vector - writer->WriteUInt32(commandsSize); - - SerializeCommandVisitor cmdVisitor(writer); - for (unsigned int i = 0; i < commandsSize; ++i) - { - shared_ptr exprCmd; - IFTPlatformException(commands->GetAt(i, &exprCmd)); - - CalculationManager::CommandType commandType = exprCmd->GetCommandType(); - writer->WriteInt32(static_cast(commandType)); - exprCmd->Accept(cmdVisitor); - } - - unsigned int tokensSize; - IFTPlatformException(tokens->GetSize(&tokensSize)); - writer->WriteUInt32(tokensSize); - - for (unsigned int i = 0; i < tokensSize; ++i) - { - pair eachToken; - IFTPlatformException(tokens->GetAt(i, &eachToken)); - - auto stringData = ref new Platform::String(eachToken.first.c_str()); - auto intData = eachToken.second; - writer->WriteUInt32(writer->MeasureString(stringData)); - writer->WriteString(stringData); - writer->WriteInt32(intData); - } -} - -const shared_ptr>> Utils::DeserializeCommands(DataReader^ reader) -{ - shared_ptr>> commandVector = make_shared>>(); - auto commandVectorSize = reader->ReadUInt32(); - - CommandDeserializer cmdDeserializer(reader); - for (unsigned int i = 0; i < commandVectorSize; ++i) - { - auto commandTypeInt = reader->ReadInt32(); - CalculationManager::CommandType commandType = static_cast(commandTypeInt); - shared_ptr exprCmd = cmdDeserializer.Deserialize(commandType); - commandVector->Append(exprCmd); - } - - return commandVector; -} - -const shared_ptr>> Utils::DeserializeTokens(DataReader^ reader) -{ - shared_ptr>> tokenVector = make_shared>>(); - auto tokensSize = reader->ReadUInt32(); - - for (unsigned int i = 0; i < tokensSize; ++i) - { - pair eachToken; - auto stringDataLen = reader->ReadUInt32(); - auto stringData = reader->ReadString(stringDataLen); - auto intData = reader->ReadInt32(); - eachToken.first = stringData->Data(); - eachToken.second = intData; - tokenVector->Append(eachToken); - } - - return tokenVector; -} - DateTime Utils::GetUniversalSystemTime() { SYSTEMTIME sysTime = {}; @@ -200,14 +95,14 @@ bool Utils::IsDateTimeOlderThan(DateTime dateTime, const long long duration) return dateTime.UniversalTime + duration < now.UniversalTime; } -task Utils::WriteFileToFolder(IStorageFolder^ folder, String^ fileName, String^ contents, CreationCollisionOption collisionOption) +task Utils::WriteFileToFolder(IStorageFolder ^ folder, String ^ fileName, String ^ contents, CreationCollisionOption collisionOption) { if (folder == nullptr) { co_return; } - StorageFile^ file = co_await folder->CreateFileAsync(fileName, collisionOption); + StorageFile ^ file = co_await folder->CreateFileAsync(fileName, collisionOption); if (file == nullptr) { co_return; @@ -216,19 +111,149 @@ task Utils::WriteFileToFolder(IStorageFolder^ folder, String^ fileName, St co_await FileIO::WriteTextAsync(file, contents); } -task Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName) +task Utils::ReadFileFromFolder(IStorageFolder ^ folder, String ^ fileName) { if (folder == nullptr) { co_return nullptr; } - StorageFile^ file = co_await folder->GetFileAsync(fileName); + StorageFile ^ file = co_await folder->GetFileAsync(fileName); if (file == nullptr) { co_return nullptr; } - String^ contents = co_await FileIO::ReadTextAsync(file); + 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> specialCharacters) +{ + // Construct a default special characters if not provided. + if (specialCharacters == nullptr) + { + specialCharacters = make_shared>(); + 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"&"; + break; + case L'\"': + buffer << L"""; + break; + case L'\'': + buffer << L"'"; + break; + case L'<': + buffer << L"<"; + break; + case L'>': + buffer << L">"; + 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()(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(Application::Current->Resources->Lookup(L"WhiteBrush")); + } + + return static_cast(Application::Current->Resources->Lookup(L"BlackBrush")); +} diff --git a/src/CalcViewModel/Common/Utils.h b/src/CalcViewModel/Common/Utils.h index 37d6c6ba..553e43f3 100644 --- a/src/CalcViewModel/Common/Utils.h +++ b/src/CalcViewModel/Common/Utils.h @@ -3,92 +3,196 @@ #pragma once -#include "CalcManager/CalculatorVector.h" #include "CalcManager/ExpressionCommandInterface.h" #include "DelegateCommand.h" +#include "GraphingInterfaces/GraphingEnums.h" // Utility macros to make Models easier to write // generates a member variable called m_ -#define PROPERTY_R(t, n)\ - property t n {\ - t get() { return m_##n; }\ - private: void set(t value) { m_##n = value; }\ - } private: t m_##n; public: -#define PROPERTY_RW(t, n)\ - property t n {\ - t get() { return m_##n; }\ - void set(t value) { m_##n = value; }\ - } private: t m_##n; public: +#define SINGLE_ARG(...) __VA_ARGS__ -#define OBSERVABLE_PROPERTY_R(t, n)\ - property t n {\ - t get() { return m_##n; }\ - private: void set(t value) {\ - if (m_##n != value) {\ - m_##n = value;\ - RaisePropertyChanged(L#n);\ - }}\ - } private: t m_##n; public: +#define PROPERTY_R(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + \ + private: \ + void set(t value) \ + { \ + m_##n = value; \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: -#define OBSERVABLE_PROPERTY_RW(t, n)\ - property t n {\ - t get() { return m_##n; }\ - void set(t value) {\ - if (m_##n != value) {\ - m_##n = value;\ - RaisePropertyChanged(L#n);\ - }\ - }\ - } private: t m_##n; public: +#define PROPERTY_RW(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + void set(t value) \ + { \ + m_##n = value; \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: -#define NAMED_OBSERVABLE_PROPERTY_RW(t, n)\ - OBSERVABLE_PROPERTY_RW(t, n)\ - private: property Platform::StringReference n##_PropertyName {\ - Platform::StringReference get() { return Platform::StringReference(L#n); }\ - } public: +#define OBSERVABLE_PROPERTY_R(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + \ + private: \ + void set(t value) \ + { \ + if (m_##n != value) \ + { \ + m_##n = value; \ + RaisePropertyChanged(L#n); \ + } \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define OBSERVABLE_PROPERTY_RW(t, n) \ + property t n \ + { \ + t get() \ + { \ + return m_##n; \ + } \ + void set(t value) \ + { \ + if (m_##n != value) \ + { \ + m_##n = value; \ + RaisePropertyChanged(L#n); \ + } \ + } \ + } \ + \ +private: \ + t m_##n; \ + \ +public: + +#define OBSERVABLE_NAMED_PROPERTY_R(t, n) \ + OBSERVABLE_PROPERTY_R(t, n) \ +public: \ + static property Platform::String ^ n##PropertyName \ + { \ + Platform::String ^ get() { return Platform::StringReference(L#n); } \ + } \ + \ +public: + +#define OBSERVABLE_NAMED_PROPERTY_RW(t, n) \ + OBSERVABLE_PROPERTY_RW(t, n) \ +public: \ + static property Platform::String ^ n##PropertyName \ + { \ + Platform::String ^ get() { return Platform::StringReference(L#n); } \ + } \ + \ +public: #define OBSERVABLE_PROPERTY_FIELD(n) m_##n // This variant of the observable object is for objects that don't want to react to property changes #ifndef UNIT_TESTS -#define OBSERVABLE_OBJECT() virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\ - internal: void RaisePropertyChanged(Platform::String^ p) {\ - PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); } public: +#define OBSERVABLE_OBJECT() \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); \ + } \ + \ +public: #else -#define OBSERVABLE_OBJECT() virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\ - internal: void RaisePropertyChanged(Platform::String^ p) {\ - } public: +#define OBSERVABLE_OBJECT() \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + } \ + \ +public: #endif // The callback specified in the macro is a method in the class that will be called every time the object changes // the callback is supposed to be have a single parameter of type Platform::String^ #ifndef UNIT_TESTS -#define OBSERVABLE_OBJECT_CALLBACK(c) virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\ - internal: void RaisePropertyChanged(Platform::String^ p) {\ - PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p));\ - c(p);\ - } public: +#define OBSERVABLE_OBJECT_CALLBACK(c) \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(p)); \ + c(p); \ + } \ + \ +public: #else -#define OBSERVABLE_OBJECT_CALLBACK(c) virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\ - internal: void RaisePropertyChanged(Platform::String^ p) {\ - c(p);\ - } public: +#define OBSERVABLE_OBJECT_CALLBACK(c) \ + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; \ + internal: \ + void RaisePropertyChanged(Platform::String ^ p) \ + { \ + c(p); \ + } \ + \ +public: #endif // The variable member generated by this macro should not be used in the class code, use the // property getter instead. -#define COMMAND_FOR_METHOD(p, m) property Windows::UI::Xaml::Input::ICommand^ p {\ +#define COMMAND_FOR_METHOD(p, m) \ + property Windows::UI::Xaml::Input::ICommand^ p {\ Windows::UI::Xaml::Input::ICommand^ get() {\ if (!donotuse_##p) {\ donotuse_##p = CalculatorApp::Common::MakeDelegate(this, &m);\ - } return donotuse_##p; }} private: Windows::UI::Xaml::Input::ICommand^ donotuse_##p; public: + } return donotuse_##p; }} private: Windows::UI::Xaml::Input::ICommand^ donotuse_##p; \ + \ +public: -#define DEPENDENCY_PROPERTY_DECLARATION(t, n)\ - property t n {\ - t get() { return safe_cast(GetValue(s_##n##Property)); }\ - void set(t value) { SetValue(s_##n##Property, value); } }\ - private: static Windows::UI::Xaml::DependencyProperty^ s_##n##Property; public: +#define DEPENDENCY_PROPERTY_DECLARATION(t, n) \ + property t n \ + { \ + t get() \ + { \ + return safe_cast(GetValue(s_##n##Property)); \ + } \ + void set(t value) \ + { \ + SetValue(s_##n##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##n##Property; \ + \ +private: // Utilities for DependencyProperties namespace Utils @@ -108,13 +212,13 @@ namespace Utils }; template - struct RemoveHat + struct RemoveHat { typedef T type; }; template - typename std::enable_if::value, T^>::type MakeDefault() + typename std::enable_if::value, T ^>::type MakeDefault() { return nullptr; } @@ -137,9 +241,6 @@ namespace Utils }; } - const wchar_t LRE = 0x202a; // Left-to-Right Embedding - const wchar_t PDF = 0x202c; // Pop Directional Formatting - const wchar_t LRO = 0x202d; // Left-to-Right Override // Regular DependencyProperty template @@ -268,151 +369,348 @@ namespace Utils ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); } - template - void Swap(T *ref1, T *ref2) + void IFTPlatformException(HRESULT hr); + bool IsLastCharacterTarget(std::wstring const& input, wchar_t target); + + // Return wstring after removing characters specified by unwantedChars array + template + std::wstring RemoveUnwantedCharsFromString(std::wstring inputString, const wchar_t (&unwantedChars)[N]) { - T temp = *ref1; - *ref1 = *ref2; - *ref2 = temp; + for (const wchar_t unwantedChar : unwantedChars) + { + inputString.erase(std::remove(inputString.begin(), inputString.end(), unwantedChar), inputString.end()); + } + return inputString; } - void IFTPlatformException(HRESULT hr); - Platform::String^ GetStringValue(Platform::String^ input); - bool IsLastCharacterTarget(std::wstring const &input, wchar_t target); - std::wstring RemoveUnwantedCharsFromWstring(std::wstring inputString, wchar_t* unwantedChars, unsigned int size); - std::wstring RemoveUnwantedCharsFromWstring(std::wstring input); double GetDoubleFromWstring(std::wstring input); int GetWindowId(); - void RunOnUIThreadNonblocking(std::function&& function, _In_ Windows::UI::Core::CoreDispatcher^ currentDispatcher); - void SerializeCommandsAndTokens(_In_ std::shared_ptr>> const &tokens, - _In_ std::shared_ptr>> const &commands, - Windows::Storage::Streams::DataWriter^ writer); - - const std::shared_ptr>> DeserializeCommands(Windows::Storage::Streams::DataReader^ reader); - const std::shared_ptr>> DeserializeTokens(Windows::Storage::Streams::DataReader^ reader); + void RunOnUIThreadNonblocking(std::function&& function, _In_ Windows::UI::Core::CoreDispatcher ^ currentDispatcher); Windows::Foundation::DateTime GetUniversalSystemTime(); bool IsDateTimeOlderThan(Windows::Foundation::DateTime dateTime, const long long duration); concurrency::task WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption); concurrency::task ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName); + + bool AreColorsEqual(const Windows::UI::Color& color1, const Windows::UI::Color& color2); + + Platform::String^ Trim(Platform::String^ value); + void Trim(std::wstring& value); + void TrimFront(std::wstring& value); + void TrimBack(std::wstring& value); + + Platform::String ^ EscapeHtmlSpecialCharacters(Platform::String ^ originalString, std::shared_ptr> specialCharacters = nullptr); + + Windows::UI::Xaml::Media::SolidColorBrush ^ GetContrastColor(Windows::UI::Color backgroundColor); } // This goes into the header to define the property, in the public: section of the class -#define DEPENDENCY_PROPERTY_OWNER(owner)\ - private: typedef owner DependencyPropertiesOwner; public: +#define DEPENDENCY_PROPERTY_OWNER(owner) \ +private: \ + typedef owner DependencyPropertiesOwner; \ + \ +public: // Normal DependencyProperty -#define DEPENDENCY_PROPERTY(type, name)\ - property type name {\ - type get() { return safe_cast(GetValue(s_##name##Property)); }\ - void set(type value) { SetValue(s_##name##Property, value); }\ - } private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyProperty(L#name); } public: +#define DEPENDENCY_PROPERTY(type, name) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyProperty(L#name); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_WITH_DEFAULT(type, name, defaultValue)\ - property type name {\ - type get() { return safe_cast(GetValue(s_##name##Property)); }\ - void set(type value) { SetValue(s_##name##Property, value); }\ - } private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyProperty(L#name, defaultValue); } public: +#define DEPENDENCY_PROPERTY_WITH_DEFAULT(type, name, defaultValue) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyProperty(L#name, defaultValue); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_WITH_CALLBACK(type, name)\ - property type name {\ - type get() { return safe_cast(GetValue(s_##name##Property)); }\ - void set(type value) { SetValue(s_##name##Property, value); }\ - } private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyWithCallback(L#name, &On##name##PropertyChangedImpl); }\ - static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\ - auto self = safe_cast(sender);\ - self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); } public: +#define DEPENDENCY_PROPERTY_WITH_CALLBACK(type, name) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue)\ - property type name {\ - type get() { return safe_cast(GetValue(s_##name##Property)); }\ - void set(type value) { SetValue(s_##name##Property, value); }\ - } private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); }\ - static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\ - auto self = safe_cast(sender);\ - self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); } public: +#define DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue) \ + property type name \ + { \ + type get() \ + { \ + return safe_cast(GetValue(s_##name##Property)); \ + } \ + void set(type value) \ + { \ + SetValue(s_##name##Property, value); \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + auto self = safe_cast(sender); \ + self->On##name##PropertyChanged(safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: // Attached DependencyProperty -#define DEPENDENCY_PROPERTY_ATTACHED(type, name)\ - static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast(target->GetValue(s_##name##Property)); }\ - static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\ - private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyAttached(L#name); } public: +#define DEPENDENCY_PROPERTY_ATTACHED(type, name) \ + static type Get##name(Windows::UI::Xaml::DependencyObject ^ target) \ + { \ + return safe_cast(target->GetValue(s_##name##Property)); \ + } \ + static void Set##name(Windows::UI::Xaml::DependencyObject ^ target, type value) \ + { \ + target->SetValue(s_##name##Property, value); \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyAttached(L#name); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT(type, name, defaultValue)\ - static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast(target->GetValue(s_##name##Property)); }\ - static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\ - private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyAttached(L#name, defaultValue); } public: +#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT(type, name, defaultValue) \ + static type Get##name(Windows::UI::Xaml::DependencyObject ^ target) \ + { \ + return safe_cast(target->GetValue(s_##name##Property)); \ + } \ + static void Set##name(Windows::UI::Xaml::DependencyObject ^ target, type value) \ + { \ + target->SetValue(s_##name##Property, value); \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyAttached(L#name, defaultValue); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(type, name)\ - static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast(target->GetValue(s_##name##Property)); }\ - static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\ - private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyAttachedWithCallback(L#name, &On##name##PropertyChangedImpl); }\ - static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\ - On##name##PropertyChanged(sender, safe_cast(args->OldValue), safe_cast(args->NewValue)); } public: +#define DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(type, name) \ + static type Get##name(Windows::UI::Xaml::DependencyObject ^ target) \ + { \ + return safe_cast(target->GetValue(s_##name##Property)); \ + } \ + static void Set##name(Windows::UI::Xaml::DependencyObject ^ target, type value) \ + { \ + target->SetValue(s_##name##Property, value); \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyAttachedWithCallback(L#name, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + On##name##PropertyChanged(sender, safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: -#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue)\ - static type Get##name(Windows::UI::Xaml::DependencyObject^ target) { return safe_cast(target->GetValue(s_##name##Property)); }\ - static void Set##name(Windows::UI::Xaml::DependencyObject^ target, type value) { target->SetValue(s_##name##Property, value); }\ - private: static Windows::UI::Xaml::DependencyProperty^ s_##name##Property;\ - public: static property Windows::UI::Xaml::DependencyProperty^ name##Property {\ - Windows::UI::Xaml::DependencyProperty^ get() { assert(s_##name##Property); return s_##name##Property; }\ - }\ - private: static Windows::UI::Xaml::DependencyProperty^ Initialize##name##Property() {\ - return Utils::RegisterDependencyPropertyAttachedWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); }\ - static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ args) {\ - On##name##PropertyChanged(sender, safe_cast(args->OldValue), safe_cast(args->NewValue)); } public: +#define DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(type, name, defaultValue) \ + static type Get##name(Windows::UI::Xaml::DependencyObject ^ target) \ + { \ + return safe_cast(target->GetValue(s_##name##Property)); \ + } \ + static void Set##name(Windows::UI::Xaml::DependencyObject ^ target, type value) \ + { \ + target->SetValue(s_##name##Property, value); \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ s_##name##Property; \ + \ +public: \ + static property Windows::UI::Xaml::DependencyProperty ^ name##Property \ + { \ + Windows::UI::Xaml::DependencyProperty ^ get() { \ + assert(s_##name##Property); \ + return s_##name##Property; \ + } \ + } \ + \ +private: \ + static Windows::UI::Xaml::DependencyProperty ^ Initialize##name##Property() \ + { \ + return Utils::RegisterDependencyPropertyAttachedWithCallback(L#name, defaultValue, &On##name##PropertyChangedImpl); \ + } \ + static void On##name##PropertyChangedImpl(Windows::UI::Xaml::DependencyObject ^ sender, Windows::UI::Xaml::DependencyPropertyChangedEventArgs ^ args) \ + { \ + On##name##PropertyChanged(sender, safe_cast(args->OldValue), safe_cast(args->NewValue)); \ + } \ + \ +public: // This goes into the cpp to initialize the static variable -#define DEPENDENCY_PROPERTY_INITIALIZATION(owner, name)\ - Windows::UI::Xaml::DependencyProperty^ owner::s_##name##Property =\ - owner::Initialize##name##Property(); +#define DEPENDENCY_PROPERTY_INITIALIZATION(owner, name) Windows::UI::Xaml::DependencyProperty ^ owner::s_##name##Property = owner::Initialize##name##Property(); namespace CalculatorApp { template - T from_cx(Platform::Object^ from) + T from_cx(Platform::Object ^ from) { T to{ nullptr }; - winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)->QueryInterface(winrt::guid_of(), - reinterpret_cast(winrt::put_abi(to)))); + winrt::check_hresult(reinterpret_cast<::IUnknown*>(from)->QueryInterface(winrt::guid_of(), reinterpret_cast(winrt::put_abi(to)))); return to; } } + +// There's no standard definition of equality for Windows::UI::Color structs. +// Define a template specialization for std::equal_to. +template<> +class std::equal_to +{ +public: + bool operator()(const Windows::UI::Color& color1, const Windows::UI::Color& color2) + { + return Utils::AreColorsEqual(color1, color2); + } +}; + +bool operator==(const Windows::UI::Color& color1, const Windows::UI::Color& color2); +bool operator!=(const Windows::UI::Color& color1, const Windows::UI::Color& color2); diff --git a/src/CalcViewModel/Common/ValidatingConverters.h b/src/CalcViewModel/Common/ValidatingConverters.h deleted file mode 100644 index b33c148f..00000000 --- a/src/CalcViewModel/Common/ValidatingConverters.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp { namespace Common -{ - public ref class ValidSelectedItemConverter sealed: public Windows::UI::Xaml::Data::IValueConverter - { - public: - ValidSelectedItemConverter() - { } - - private: - - virtual Platform::Object^ Convert( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert - { - // Pass through as we don't want to change the value from the source - return value; - } - - virtual Platform::Object^ ConvertBack( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack - { - if (value) - { - return value; - } - // Stop the binding if the object is nullptr - return Windows::UI::Xaml::DependencyProperty::UnsetValue; - } - }; - - public ref class ValidSelectedIndexConverter sealed: public Windows::UI::Xaml::Data::IValueConverter - { - public: - ValidSelectedIndexConverter() - { } - - private: - - virtual Platform::Object^ Convert( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert - { - // Pass through as we don't want to change the value from the source - return value; - } - - virtual Platform::Object^ ConvertBack( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack - { - // The value to be valid has to be a boxed int32 value - // extract that value and ensure it is valid, ie >= 0 - if (value) - { - auto box = dynamic_cast(value); - if (box && box->Type == Windows::Foundation::PropertyType::Int32) - { - int index = box->GetInt32(); - if (index >= 0) - { - return value; - } - } - } - // The value is not valid therefore stop the binding right here - return Windows::UI::Xaml::DependencyProperty::UnsetValue; - } - }; -}} diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp index 48c64d42..fc91a920 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp @@ -39,28 +39,25 @@ static constexpr auto CURRENCY_UNIT_TO_KEY = L"CURRENCY_UNIT_TO_KEY"; static constexpr long long DAY_DURATION = 1LL * 60 * 60 * 24 * 10000000; static constexpr long long WEEK_DURATION = DAY_DURATION * 7; -static constexpr int FORMATTER_DIGIT_COUNT = 4; +static constexpr int FORMATTER_RATE_FRACTION_PADDING = 2; +static constexpr int FORMATTER_RATE_MIN_DECIMALS = 4; +static constexpr int FORMATTER_RATE_MIN_SIGNIFICANT_DECIMALS = 4; static constexpr auto CACHE_TIMESTAMP_KEY = L"CURRENCY_CONVERTER_TIMESTAMP"; static constexpr auto CACHE_LANGCODE_KEY = L"CURRENCY_CONVERTER_LANGCODE"; static constexpr auto CACHE_DELIMITER = L"%"; static constexpr auto STATIC_DATA_FILENAME = L"CURRENCY_CONVERTER_STATIC_DATA.txt"; -static constexpr array STATIC_DATA_PROPERTIES = { - wstring_view{ L"CountryCode", 11 }, - wstring_view{ L"CountryName", 11 }, - wstring_view{ L"CurrencyCode", 12 }, - wstring_view{ L"CurrencyName", 12 }, - wstring_view{ L"CurrencySymbol", 14 } -}; +static constexpr array STATIC_DATA_PROPERTIES = { wstring_view{ L"CountryCode", 11 }, + wstring_view{ L"CountryName", 11 }, + wstring_view{ L"CurrencyCode", 12 }, + wstring_view{ L"CurrencyName", 12 }, + wstring_view{ L"CurrencySymbol", 14 } }; static constexpr auto ALL_RATIOS_DATA_FILENAME = L"CURRENCY_CONVERTER_ALL_RATIOS_DATA.txt"; static constexpr auto RATIO_KEY = L"Rt"; static constexpr auto CURRENCY_CODE_KEY = L"An"; -static constexpr array ALL_RATIOS_DATA_PROPERTIES = { - wstring_view{ RATIO_KEY, 2 }, - wstring_view{ CURRENCY_CODE_KEY, 2 } -}; +static constexpr array ALL_RATIOS_DATA_PROPERTIES = { wstring_view{ RATIO_KEY, 2 }, wstring_view{ CURRENCY_CODE_KEY, 2 } }; static constexpr auto DEFAULT_FROM_TO_CURRENCY_FILE_URI = L"ms-appx:///DataLoaders/DefaultFromToCurrency.json"; static constexpr auto FROM_KEY = L"from"; @@ -92,18 +89,29 @@ namespace CalculatorApp } } -CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr client) : - m_client(move(client)), - m_loadStatus(CurrencyLoadStatus::NotLoaded), - m_responseLanguage(L"en-US"), - m_ratioFormat(L""), - m_timestampFormat(L""), - m_networkManager(ref new NetworkManager()), - m_meteredOverrideSet(false) +CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr client, const wchar_t * forcedResponseLanguage) + : m_client(move(client)) + , m_loadStatus(CurrencyLoadStatus::NotLoaded) + , m_responseLanguage(L"en-US") + , m_ratioFormat(L"") + , m_timestampFormat(L"") + , m_networkManager(ref new NetworkManager()) + , m_meteredOverrideSet(false) { - if (GlobalizationPreferences::Languages->Size > 0) + if (forcedResponseLanguage != nullptr) { - m_responseLanguage = GlobalizationPreferences::Languages->GetAt(0); + m_responseLanguage = ref new Platform::String(forcedResponseLanguage); + } + else + { + if (GlobalizationPreferences::Languages->Size > 0) + { + m_responseLanguage = GlobalizationPreferences::Languages->GetAt(0); + } + else + { + m_responseLanguage = L"en-US"; + } } if (m_client != nullptr) @@ -112,19 +120,20 @@ CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr clie m_client->SetResponseLanguage(m_responseLanguage); } + auto localizationService = LocalizationService::GetInstance(); if (CoreWindow::GetForCurrentThread() != nullptr) { // Must have a CoreWindow to access the resource context. - m_isRtlLanguage = LocalizationService::GetInstance()->IsRtlLayout(); + m_isRtlLanguage = localizationService->IsRtlLayout(); } - m_ratioFormatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + m_ratioFormatter = localizationService->GetRegionalSettingsAwareDecimalFormatter(); m_ratioFormatter->IsGrouped = true; m_ratioFormatter->IsDecimalPointAlwaysDisplayed = true; - m_ratioFormatter->FractionDigits = FORMATTER_DIGIT_COUNT; + m_ratioFormatter->FractionDigits = FORMATTER_RATE_FRACTION_PADDING; - m_ratioFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyFromToRatioFormat")->Data(); - m_timestampFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyTimestampFormat")->Data(); + m_ratioFormat = AppResourceProvider::GetInstance()->GetResourceString(L"CurrencyFromToRatioFormat"); + m_timestampFormat = AppResourceProvider::GetInstance()->GetResourceString(L"CurrencyTimestampFormat"); } CurrencyDataLoader::~CurrencyDataLoader() @@ -141,11 +150,8 @@ void CurrencyDataLoader::RegisterForNetworkBehaviorChanges() { UnregisterForNetworkBehaviorChanges(); - m_networkBehaviorToken = - m_networkManager->NetworkBehaviorChanged += ref new NetworkBehaviorChangedHandler([this](NetworkAccessBehavior newBehavior) - { - this->OnNetworkBehaviorChanged(newBehavior); - }); + m_networkBehaviorToken = m_networkManager->NetworkBehaviorChanged += + ref new NetworkBehaviorChangedHandler([this](NetworkAccessBehavior newBehavior) { this->OnNetworkBehaviorChanged(newBehavior); }); OnNetworkBehaviorChanged(NetworkManager::GetNetworkAccessBehavior()); } @@ -186,9 +192,8 @@ void CurrencyDataLoader::LoadData() if (!LoadFinished()) { - create_task([this]() -> task - { - vector()>> loadFunctions = { + create_task([this]() -> task { + vector()>> loadFunctions = { [this]() { return TryLoadDataFromCacheAsync(); }, [this]() { return TryLoadDataFromWebAsync(); }, }; @@ -204,23 +209,25 @@ void CurrencyDataLoader::LoadData() } co_return didLoad; - }).then([this](bool didLoad) - { - UpdateDisplayedTimestamp(); - NotifyDataLoadFinished(didLoad); - }, task_continuation_context::use_current()); + }) + .then( + [this](bool didLoad) { + UpdateDisplayedTimestamp(); + NotifyDataLoadFinished(didLoad); + }, + task_continuation_context::use_current()); } }; #pragma optimize("", on) -vector CurrencyDataLoader::LoadOrderedCategories() +vector CurrencyDataLoader::GetOrderedCategories() { // This function should not be called // The model will use the categories from UnitConverterDataLoader return vector(); } -vector CurrencyDataLoader::LoadOrderedUnits(const UCM::Category& /*category*/) +vector CurrencyDataLoader::GetOrderedUnits(const UCM::Category& /*category*/) { lock_guard lock(m_currencyUnitsMutex); return m_currencyUnits; @@ -262,6 +269,23 @@ pair CurrencyDataLoader::GetCurrencySymbols(const UCM::Unit& u return make_pair(symbol1, symbol2); } +double CurrencyDataLoader::RoundCurrencyRatio(double ratio) +{ + // Compute how many decimals we need to display two meaningful digits at minimum + // For example: 0.00000000342334 -> 0.000000003423, 0.000212 -> 0.000212 + int numberDecimals = FORMATTER_RATE_MIN_DECIMALS; + if (ratio < 1) + { + numberDecimals = max( + FORMATTER_RATE_MIN_DECIMALS, + (int)(-log10(ratio)) + FORMATTER_RATE_MIN_SIGNIFICANT_DECIMALS); + } + + unsigned long long scale = (unsigned long long)powl(10l, numberDecimals); + + return (double)(round(ratio * scale) / scale); +} + pair CurrencyDataLoader::GetCurrencyRatioEquality(_In_ const UCM::Unit& unit1, _In_ const UCM::Unit& unit2) { try @@ -274,43 +298,32 @@ pair CurrencyDataLoader::GetCurrencyRatioEquality(_In_ const U if (iter2 != ratioMap.end()) { double ratio = (iter2->second).ratio; + double rounded = RoundCurrencyRatio(ratio); - // Round the ratio to FORMATTER_DIGIT_COUNT digits using int math. - // Ex: to round 1.23456 to three digits, use - // ((int) 1.23456 * (10^3)) / (10^3) - double scale = pow(10, FORMATTER_DIGIT_COUNT); - double rounded = static_cast(ratio * static_cast(scale)) / scale; + auto digit = LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'1'); + auto digitSymbol = ref new String(&digit, 1); + auto roundedFormat = m_ratioFormatter->Format(rounded); - wstring digitSymbol = wstring{ LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'1') }; - wstring roundedFormat = m_ratioFormatter->Format(rounded)->Data(); + auto ratioString = LocalizationStringUtil::GetLocalizedString( + m_ratioFormat, digitSymbol, StringReference(unit1.abbreviation.c_str()), roundedFormat, StringReference(unit2.abbreviation.c_str())); - wstring ratioString = LocalizationStringUtil::GetLocalizedString( - m_ratioFormat.c_str(), - digitSymbol.c_str(), - unit1.abbreviation.c_str(), - roundedFormat.c_str(), - unit2.abbreviation.c_str() - ); + auto accessibleRatioString = + LocalizationStringUtil::GetLocalizedString( + m_ratioFormat, digitSymbol, StringReference(unit1.accessibleName.c_str()), roundedFormat, StringReference(unit2.accessibleName.c_str())); - wstring accessibleRatioString = LocalizationStringUtil::GetLocalizedString( - m_ratioFormat.c_str(), - digitSymbol.c_str(), - unit1.accessibleName.c_str(), - roundedFormat.c_str(), - unit2.accessibleName.c_str() - ); - - return make_pair(ratioString, accessibleRatioString); + return make_pair(ratioString->Data(), accessibleRatioString->Data()); } } } - catch (...) {} + catch (...) + { + } return make_pair(L"", L""); } #pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321 -task CurrencyDataLoader::TryLoadDataFromCacheAsync() +future CurrencyDataLoader::TryLoadDataFromCacheAsync() { try { @@ -324,8 +337,7 @@ task CurrencyDataLoader::TryLoadDataFromCacheAsync() bool loadComplete = false; m_cacheTimestamp = static_cast(localSettings->Values->Lookup(CacheTimestampKey)); - if (Utils::IsDateTimeOlderThan(m_cacheTimestamp, DAY_DURATION) - && m_networkAccessBehavior == NetworkAccessBehavior::Normal) + if (Utils::IsDateTimeOlderThan(m_cacheTimestamp, DAY_DURATION) && m_networkAccessBehavior == NetworkAccessBehavior::Normal) { loadComplete = co_await TryLoadDataFromWebAsync(); } @@ -337,14 +349,14 @@ task CurrencyDataLoader::TryLoadDataFromCacheAsync() co_return loadComplete; } - catch (Exception^ ex) + catch (Exception ^ ex) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + TraceLogger::GetInstance()->LogPlatformException(ViewMode::Currency, __FUNCTIONW__, ex); co_return false; } catch (const exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance()->LogStandardException(ViewMode::Currency, __FUNCTIONW__, e); co_return false; } catch (...) @@ -353,7 +365,7 @@ task CurrencyDataLoader::TryLoadDataFromCacheAsync() } } -task CurrencyDataLoader::TryFinishLoadFromCacheAsync() +future CurrencyDataLoader::TryFinishLoadFromCacheAsync() { auto localSettings = ApplicationData::Current->LocalSettings; if (localSettings == nullptr) @@ -361,29 +373,24 @@ task CurrencyDataLoader::TryFinishLoadFromCacheAsync() co_return false; } - if (!localSettings->Values->HasKey(CacheLangcodeKey) - || !static_cast(localSettings->Values->Lookup(CacheLangcodeKey))->Equals(m_responseLanguage)) + if (!localSettings->Values->HasKey(CacheLangcodeKey) || !static_cast(localSettings->Values->Lookup(CacheLangcodeKey))->Equals(m_responseLanguage)) { co_return false; } - StorageFolder^ localCacheFolder = ApplicationData::Current->LocalCacheFolder; + StorageFolder ^ localCacheFolder = ApplicationData::Current->LocalCacheFolder; if (localCacheFolder == nullptr) { co_return false; } - String^ staticDataResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, StaticDataFilename); - String^ allRatiosResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, AllRatiosDataFilename); + String ^ staticDataResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, StaticDataFilename); + String ^ allRatiosResponse = co_await Utils::ReadFileFromFolder(localCacheFolder, AllRatiosDataFilename); vector staticData{}; CurrencyRatioMap ratioMap{}; - bool didParse = TryParseWebResponses( - staticDataResponse, - allRatiosResponse, - staticData, - ratioMap); + bool didParse = TryParseWebResponses(staticDataResponse, allRatiosResponse, staticData, ratioMap); if (!didParse) { co_return false; @@ -395,7 +402,7 @@ task CurrencyDataLoader::TryFinishLoadFromCacheAsync() co_return true; } -task CurrencyDataLoader::TryLoadDataFromWebAsync() +future CurrencyDataLoader::TryLoadDataFromWebAsync() { try { @@ -406,14 +413,13 @@ task CurrencyDataLoader::TryLoadDataFromWebAsync() co_return false; } - if (m_networkAccessBehavior == NetworkAccessBehavior::Offline || - (m_networkAccessBehavior == NetworkAccessBehavior::OptIn && !m_meteredOverrideSet)) + if (m_networkAccessBehavior == NetworkAccessBehavior::Offline || (m_networkAccessBehavior == NetworkAccessBehavior::OptIn && !m_meteredOverrideSet)) { co_return false; } - String^ staticDataResponse = co_await m_client->GetCurrencyMetadata(); - String^ allRatiosResponse = co_await m_client->GetCurrencyRatios(); + String ^ staticDataResponse = co_await m_client->GetCurrencyMetadata(); + String ^ allRatiosResponse = co_await m_client->GetCurrencyRatios(); if (staticDataResponse == nullptr || allRatiosResponse == nullptr) { co_return false; @@ -422,11 +428,7 @@ task CurrencyDataLoader::TryLoadDataFromWebAsync() vector staticData{}; CurrencyRatioMap ratioMap{}; - bool didParse = TryParseWebResponses( - staticDataResponse, - allRatiosResponse, - staticData, - ratioMap); + bool didParse = TryParseWebResponses(staticDataResponse, allRatiosResponse, staticData, ratioMap); if (!didParse) { co_return false; @@ -437,19 +439,12 @@ task CurrencyDataLoader::TryLoadDataFromWebAsync() try { - const vector> cachedFiles = { - { StaticDataFilename, staticDataResponse }, - { AllRatiosDataFilename, allRatiosResponse } - }; + const vector> cachedFiles = { { StaticDataFilename, staticDataResponse }, { AllRatiosDataFilename, allRatiosResponse } }; - StorageFolder^ localCacheFolder = ApplicationData::Current->LocalCacheFolder; + StorageFolder ^ localCacheFolder = ApplicationData::Current->LocalCacheFolder; for (const auto& fileInfo : cachedFiles) { - co_await Utils::WriteFileToFolder( - localCacheFolder, - fileInfo.first, - fileInfo.second, - CreationCollisionOption::ReplaceExisting); + co_await Utils::WriteFileToFolder(localCacheFolder, fileInfo.first, fileInfo.second, CreationCollisionOption::ReplaceExisting); } SaveLangCodeAndTimestamp(); @@ -464,14 +459,14 @@ task CurrencyDataLoader::TryLoadDataFromWebAsync() co_return true; } - catch (Exception^ ex) + catch (Exception ^ ex) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + TraceLogger::GetInstance()->LogPlatformException(ViewMode::Currency, __FUNCTIONW__, ex); co_return false; } catch (const exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance()->LogStandardException(ViewMode::Currency, __FUNCTIONW__, e); co_return false; } catch (...) @@ -480,14 +475,14 @@ task CurrencyDataLoader::TryLoadDataFromWebAsync() } } -task CurrencyDataLoader::TryLoadDataFromWebOverrideAsync() +future CurrencyDataLoader::TryLoadDataFromWebOverrideAsync() { m_meteredOverrideSet = true; bool didLoad = co_await TryLoadDataFromWebAsync(); if (!didLoad) { m_loadStatus = CurrencyLoadStatus::FailedToLoad; - TraceLogger::GetInstance().LogUserRequestedRefreshFailed(); + TraceLogger::GetInstance()->LogError(ViewMode::Currency, L"CurrencyDataLoader::TryLoadDataFromWebOverrideAsync", L"UserRequestedRefreshFailed"); } co_return didLoad; @@ -495,18 +490,17 @@ task CurrencyDataLoader::TryLoadDataFromWebOverrideAsync() #pragma optimize("", on) bool CurrencyDataLoader::TryParseWebResponses( - _In_ String^ staticDataJson, - _In_ String^ allRatiosJson, + _In_ String ^ staticDataJson, + _In_ String ^ allRatiosJson, _Inout_ vector& staticData, _Inout_ CurrencyRatioMap& allRatiosData) { - return TryParseStaticData(staticDataJson, staticData) - && TryParseAllRatiosData(allRatiosJson, allRatiosData); + return TryParseStaticData(staticDataJson, staticData) && TryParseAllRatiosData(allRatiosJson, allRatiosData); } -bool CurrencyDataLoader::TryParseStaticData(_In_ String^ rawJson, _Inout_ vector& staticData) +bool CurrencyDataLoader::TryParseStaticData(_In_ String ^ rawJson, _Inout_ vector& staticData) { - JsonArray^ data = nullptr; + JsonArray ^ data = nullptr; if (!JsonArray::TryParse(rawJson, &data)) { return false; @@ -518,46 +512,49 @@ bool CurrencyDataLoader::TryParseStaticData(_In_ String^ rawJson, _Inout_ vector wstring currencyName{ L"" }; wstring currencySymbol{ L"" }; - vector values = { - &countryCode, - &countryName, - ¤cyCode, - ¤cyName, - ¤cySymbol - }; + vector values = { &countryCode, &countryName, ¤cyCode, ¤cyName, ¤cySymbol }; assert(values.size() == STATIC_DATA_PROPERTIES.size()); staticData.resize(size_t{ data->Size }); for (unsigned int i = 0; i < data->Size; i++) { - JsonObject^ obj = data->GetAt(i)->GetObject(); + JsonObject ^ obj; + try + { + obj = data->GetAt(i)->GetObject(); + } + catch (COMException ^ e) + { + if (e->HResult == E_ILLEGAL_METHOD_CALL) + { + continue; + } + else + { + throw; + } + } for (size_t j = 0; j < values.size(); j++) { (*values[j]) = obj->GetNamedString(StringReference(STATIC_DATA_PROPERTIES[j].data()))->Data(); } - staticData[i] = CurrencyStaticData{ - countryCode, - countryName, - currencyCode, - currencyName, - currencySymbol - }; + staticData[i] = CurrencyStaticData{ countryCode, countryName, currencyCode, currencyName, currencySymbol }; } - // TODO - MSFT 8533667: this sort will be replaced by a WinRT call to sort localized strings - sort(begin(staticData), end(staticData), [](CurrencyStaticData unit1, CurrencyStaticData unit2) - { - return unit1.countryName < unit2.countryName; - }); + auto sortCountryNames = [](const UCM::CurrencyStaticData & s) { + return ref new String(s.countryName.c_str()); + }; + + LocalizationService::GetInstance()->Sort(staticData, sortCountryNames); return true; } -bool CurrencyDataLoader::TryParseAllRatiosData(_In_ String^ rawJson, _Inout_ CurrencyRatioMap& allRatios) +bool CurrencyDataLoader::TryParseAllRatiosData(_In_ String ^ rawJson, _Inout_ CurrencyRatioMap& allRatios) { - JsonArray^ data = nullptr; + JsonArray ^ data = nullptr; if (!JsonArray::TryParse(rawJson, &data)) { return false; @@ -568,17 +565,28 @@ bool CurrencyDataLoader::TryParseAllRatiosData(_In_ String^ rawJson, _Inout_ Cur allRatios.clear(); for (unsigned int i = 0; i < data->Size; i++) { - JsonObject^ obj = data->GetAt(i)->GetObject(); + JsonObject ^ obj; + try + { + obj = data->GetAt(i)->GetObject(); + } + catch (COMException^ e) + { + if (e->HResult == E_ILLEGAL_METHOD_CALL) + { + continue; + } + else + { + throw; + } + } // Rt is ratio, An is target currency ISO code. double relativeRatio = obj->GetNamedNumber(StringReference(RATIO_KEY)); wstring targetCurrencyCode = obj->GetNamedString(StringReference(CURRENCY_CODE_KEY))->Data(); - allRatios.emplace(targetCurrencyCode, CurrencyRatio{ - relativeRatio, - sourceCurrencyCode, - targetCurrencyCode - }); + allRatios.emplace(targetCurrencyCode, CurrencyRatio{ relativeRatio, sourceCurrencyCode, targetCurrencyCode }); } return true; @@ -685,6 +693,23 @@ void CurrencyDataLoader::GuaranteeSelectedUnits() isConversionTargetSet = true; } } + + // If still not set for either source or target, just select the first currency in the list + + if (!m_currencyUnits.empty()) + { + if (!isConversionSourceSet) + { + m_currencyUnits[0].isConversionSource = true; + isConversionSourceSet = true; + } + + if (!isConversionTargetSet) + { + m_currencyUnits[0].isConversionTarget = true; + isConversionTargetSet = true; + } + } } void CurrencyDataLoader::NotifyDataLoadFinished(bool didLoad) @@ -702,7 +727,7 @@ void CurrencyDataLoader::NotifyDataLoadFinished(bool didLoad) void CurrencyDataLoader::SaveLangCodeAndTimestamp() { - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (localSettings == nullptr) { return; @@ -724,25 +749,19 @@ void CurrencyDataLoader::UpdateDisplayedTimestamp() } wstring CurrencyDataLoader::GetCurrencyTimestamp() { - wstring timestamp = L""; - DateTime epoch{}; if (m_cacheTimestamp.UniversalTime != epoch.UniversalTime) { - DateTimeFormatter^ dateFormatter = ref new DateTimeFormatter(L"{month.abbreviated} {day.integer}, {year.full}"); - wstring date = dateFormatter->Format(m_cacheTimestamp)->Data(); + DateTimeFormatter ^ dateFormatter = ref new DateTimeFormatter(L"shortdate"); + auto date = dateFormatter->Format(m_cacheTimestamp); - DateTimeFormatter^ timeFormatter = ref new DateTimeFormatter(L"shorttime"); - wstring time = timeFormatter->Format(m_cacheTimestamp)->Data(); + DateTimeFormatter ^ timeFormatter = ref new DateTimeFormatter(L"shorttime"); + auto time = timeFormatter->Format(m_cacheTimestamp); - timestamp = LocalizationStringUtil::GetLocalizedString( - m_timestampFormat.c_str(), - date.c_str(), - time.c_str() - ); + return LocalizationStringUtil::GetLocalizedString(m_timestampFormat, date, time)->Data(); } - return timestamp; + return L""; } #pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321 @@ -758,23 +777,25 @@ task CurrencyDataLoader::GetDefaultFromToCurrency() try { // Second, see if the current locale has preset defaults in DefaultFromToCurrency.json. - Uri^ fileUri = ref new Uri(StringReference(DEFAULT_FROM_TO_CURRENCY_FILE_URI)); - StorageFile^ defaultFromToCurrencyFile = co_await StorageFile::GetFileFromApplicationUriAsync(fileUri); + Uri ^ fileUri = ref new Uri(StringReference(DEFAULT_FROM_TO_CURRENCY_FILE_URI)); + StorageFile ^ defaultFromToCurrencyFile = co_await StorageFile::GetFileFromApplicationUriAsync(fileUri); if (defaultFromToCurrencyFile != nullptr) { - String^ fileContents = co_await FileIO::ReadTextAsync(defaultFromToCurrencyFile); - JsonObject^ fromToObject = JsonObject::Parse(fileContents); - JsonObject^ regionalDefaults = fromToObject->GetNamedObject(m_responseLanguage); + String ^ fileContents = co_await FileIO::ReadTextAsync(defaultFromToCurrencyFile); + JsonObject ^ fromToObject = JsonObject::Parse(fileContents); + JsonObject ^ regionalDefaults = fromToObject->GetNamedObject(m_responseLanguage); // Get both values before assignment in-case either fails. - String^ selectedFrom = regionalDefaults->GetNamedString(StringReference(FROM_KEY)); - String^ selectedTo = regionalDefaults->GetNamedString(StringReference(TO_KEY)); + String ^ selectedFrom = regionalDefaults->GetNamedString(StringReference(FROM_KEY)); + String ^ selectedTo = regionalDefaults->GetNamedString(StringReference(TO_KEY)); fromCurrency = selectedFrom->Data(); toCurrency = selectedTo->Data(); } } - catch (...) {} + catch (...) + { + } } co_return make_pair(fromCurrency, toCurrency); @@ -783,16 +804,16 @@ task CurrencyDataLoader::GetDefaultFromToCurrency() bool CurrencyDataLoader::TryGetLastUsedCurrenciesFromLocalSettings(_Out_ wstring* const fromCurrency, _Out_ wstring* const toCurrency) { - String^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey; - String^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey; - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + String ^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey; + String ^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey; + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (localSettings != nullptr && localSettings->Values != nullptr) { - IPropertySet^ values = localSettings->Values; + IPropertySet ^ values = localSettings->Values; if (values->HasKey(fromKey) && values->HasKey(toKey)) { - *fromCurrency = static_cast(values->Lookup(fromKey))->Data(); - *toCurrency = static_cast(values->Lookup(toKey))->Data(); + *fromCurrency = static_cast(values->Lookup(fromKey))->Data(); + *toCurrency = static_cast(values->Lookup(toKey))->Data(); return true; } @@ -803,12 +824,12 @@ bool CurrencyDataLoader::TryGetLastUsedCurrenciesFromLocalSettings(_Out_ wstring void CurrencyDataLoader::SaveSelectedUnitsToLocalSettings(_In_ const SelectedUnits& selectedUnits) { - String^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey; - String^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey; - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + String ^ fromKey = UnitConverterResourceKeys::CurrencyUnitFromKey; + String ^ toKey = UnitConverterResourceKeys::CurrencyUnitToKey; + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (localSettings != nullptr && localSettings->Values != nullptr) { - IPropertySet^ values = localSettings->Values; + IPropertySet ^ values = localSettings->Values; values->Insert(fromKey, StringReference(selectedUnits.first.c_str())); values->Insert(toKey, StringReference(selectedUnits.second.c_str())); } diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h index eba76641..3216d63e 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h @@ -11,7 +11,8 @@ namespace CalculatorApp { namespace ViewModel { - public enum class CurrencyLoadStatus + public + enum class CurrencyLoadStatus { NotLoaded = 0, FailedToLoad = 1, @@ -42,16 +43,18 @@ namespace CalculatorApp struct CurrencyUnitMetadata { - CurrencyUnitMetadata(const std::wstring& s) : symbol(s) {} + CurrencyUnitMetadata(const std::wstring& s) + : symbol(s) + { + } const std::wstring symbol; }; - class CurrencyDataLoader : public UCM::IConverterDataLoader, - public UCM::ICurrencyConverterDataLoader + class CurrencyDataLoader : public UCM::IConverterDataLoader, public UCM::ICurrencyConverterDataLoader { public: - CurrencyDataLoader(_In_ std::unique_ptr client); + CurrencyDataLoader(_In_ std::unique_ptr client, const wchar_t* overrideLanguage = nullptr); ~CurrencyDataLoader(); bool LoadFinished(); @@ -60,8 +63,8 @@ namespace CalculatorApp // IConverterDataLoader void LoadData() override; - std::vector LoadOrderedCategories() override; - std::vector LoadOrderedUnits(const UCM::Category& category) override; + std::vector GetOrderedCategories() override; + std::vector GetOrderedUnits(const UCM::Category& category) override; std::unordered_map LoadOrderedRatios(const UCM::Unit& unit) override; bool SupportsCategory(const UnitConversionManager::Category& target) override; // IConverterDataLoader @@ -69,12 +72,14 @@ namespace CalculatorApp // ICurrencyConverterDataLoader void SetViewModelCallback(const std::shared_ptr& callback) override; std::pair GetCurrencySymbols(const UCM::Unit& unit1, const UCM::Unit& unit2) override; - std::pair GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) override; + std::pair + GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) override; std::wstring GetCurrencyTimestamp() override; + static double RoundCurrencyRatio(double ratio); - concurrency::task TryLoadDataFromCacheAsync() override; - concurrency::task TryLoadDataFromWebAsync() override; - concurrency::task TryLoadDataFromWebOverrideAsync() override; + std::future TryLoadDataFromCacheAsync() override; + std::future TryLoadDataFromWebAsync() override; + std::future TryLoadDataFromWebOverrideAsync() override; // ICurrencyConverterDataLoader void OnNetworkBehaviorChanged(CalculatorApp::NetworkAccessBehavior newBehavior); @@ -83,15 +88,15 @@ namespace CalculatorApp void ResetLoadStatus(); void NotifyDataLoadFinished(bool didLoad); - concurrency::task TryFinishLoadFromCacheAsync(); + std::future TryFinishLoadFromCacheAsync(); bool TryParseWebResponses( - _In_ Platform::String^ staticDataJson, - _In_ Platform::String^ allRatiosJson, + _In_ Platform::String ^ staticDataJson, + _In_ Platform::String ^ allRatiosJson, _Inout_ std::vector& staticData, _Inout_ CurrencyRatioMap& allRatiosData); - bool TryParseStaticData(_In_ Platform::String^ rawJson, _Inout_ std::vector& staticData); - bool TryParseAllRatiosData(_In_ Platform::String^ rawJson, _Inout_ CurrencyRatioMap& allRatiosData); + bool TryParseStaticData(_In_ Platform::String ^ rawJson, _Inout_ std::vector& staticData); + bool TryParseAllRatiosData(_In_ Platform::String ^ rawJson, _Inout_ CurrencyRatioMap& allRatiosData); concurrency::task FinalizeUnits(_In_ const std::vector& staticData, _In_ const CurrencyRatioMap& ratioMap); void GuaranteeSelectedUnits(); @@ -106,7 +111,7 @@ namespace CalculatorApp void SaveSelectedUnitsToLocalSettings(_In_ const SelectedUnits& selectedUnits); private: - Platform::String^ m_responseLanguage; + Platform::String ^ m_responseLanguage; std::unique_ptr m_client; bool m_isRtlLanguage; @@ -118,14 +123,14 @@ namespace CalculatorApp std::shared_ptr m_vmCallback; - Windows::Globalization::NumberFormatting::DecimalFormatter^ m_ratioFormatter; - std::wstring m_ratioFormat; + Windows::Globalization::NumberFormatting::DecimalFormatter ^ m_ratioFormatter; + Platform::String ^ m_ratioFormat; Windows::Foundation::DateTime m_cacheTimestamp; - std::wstring m_timestampFormat; + Platform::String ^ m_timestampFormat; CurrencyLoadStatus m_loadStatus; - CalculatorApp::NetworkManager^ m_networkManager; + CalculatorApp::NetworkManager ^ m_networkManager; CalculatorApp::NetworkAccessBehavior m_networkAccessBehavior; Windows::Foundation::EventRegistrationToken m_networkBehaviorToken; bool m_meteredOverrideSet; diff --git a/src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp b/src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp index 7a2658a3..e9d42c6c 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyHttpClient.cpp @@ -1,35 +1,38 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" #include "CurrencyHttpClient.h" +#ifdef USE_MOCK_DATA +#include "DataLoaderMockConstants.h" +#else +#include "DataLoaderConstants.h" +#endif + using namespace CalculatorApp::DataLoaders; using namespace Platform; using namespace std; using namespace Windows::Foundation; using namespace Windows::Web::Http; -static constexpr auto sc_MetadataUriLocalizeFor = L"https://go.microsoft.com/fwlink/?linkid=2041093&localizeFor="; -static constexpr auto sc_RatiosUriRelativeTo = L"https://go.microsoft.com/fwlink/?linkid=2041339&localCurrency="; - -CurrencyHttpClient::CurrencyHttpClient() : - m_client(ref new HttpClient()), - m_responseLanguage(L"en-US") +CurrencyHttpClient::CurrencyHttpClient() + : m_client(ref new HttpClient()) + , m_responseLanguage(L"en-US") { } -void CurrencyHttpClient::SetSourceCurrencyCode(String^ sourceCurrencyCode) +void CurrencyHttpClient::SetSourceCurrencyCode(String ^ sourceCurrencyCode) { m_sourceCurrencyCode = sourceCurrencyCode; } -void CurrencyHttpClient::SetResponseLanguage(String^ responseLanguage) +void CurrencyHttpClient::SetResponseLanguage(String ^ responseLanguage) { m_responseLanguage = responseLanguage; } -IAsyncOperationWithProgress^ CurrencyHttpClient::GetCurrencyMetadata() +IAsyncOperationWithProgress ^ CurrencyHttpClient::GetCurrencyMetadata() { wstring uri = wstring{ sc_MetadataUriLocalizeFor } + m_responseLanguage->Data(); auto metadataUri = ref new Uri(StringReference(uri.c_str())); @@ -37,7 +40,7 @@ IAsyncOperationWithProgress^ CurrencyHttpClient::GetCurre return m_client->GetStringAsync(metadataUri); } -IAsyncOperationWithProgress^ CurrencyHttpClient::GetCurrencyRatios() +IAsyncOperationWithProgress ^ CurrencyHttpClient::GetCurrencyRatios() { wstring uri = wstring{ sc_RatiosUriRelativeTo } + m_sourceCurrencyCode->Data(); auto ratiosUri = ref new Uri(StringReference(uri.c_str())); diff --git a/src/CalcViewModel/DataLoaders/CurrencyHttpClient.h b/src/CalcViewModel/DataLoaders/CurrencyHttpClient.h index 8187c74a..cfd10e7d 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyHttpClient.h +++ b/src/CalcViewModel/DataLoaders/CurrencyHttpClient.h @@ -14,16 +14,16 @@ namespace CalculatorApp public: CurrencyHttpClient(); - void SetSourceCurrencyCode(Platform::String^ sourceCurrencyCode) override; - void SetResponseLanguage(Platform::String^ responseLanguage) override; + void SetSourceCurrencyCode(Platform::String ^ sourceCurrencyCode) override; + void SetResponseLanguage(Platform::String ^ responseLanguage) override; - Windows::Foundation::IAsyncOperationWithProgress^ GetCurrencyMetadata() override; - Windows::Foundation::IAsyncOperationWithProgress^ GetCurrencyRatios() override; + Windows::Foundation::IAsyncOperationWithProgress ^ GetCurrencyMetadata() override; + Windows::Foundation::IAsyncOperationWithProgress ^ GetCurrencyRatios() override; private: - Windows::Web::Http::HttpClient^ m_client; - Platform::String^ m_responseLanguage; - Platform::String^ m_sourceCurrencyCode; + Windows::Web::Http::HttpClient ^ m_client; + Platform::String ^ m_responseLanguage; + Platform::String ^ m_sourceCurrencyCode; }; } } diff --git a/src/CalcViewModel/DataLoaders/DataLoaderMockConstants.h b/src/CalcViewModel/DataLoaders/DataLoaderMockConstants.h new file mode 100644 index 00000000..02e7e349 --- /dev/null +++ b/src/CalcViewModel/DataLoaders/DataLoaderMockConstants.h @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +namespace CalculatorApp +{ + namespace DataLoaders + { + static constexpr auto sc_MetadataUriLocalizeFor = L"https://go.microsoft.com/fwlink/?linkid=2091028&localizeFor="; + static constexpr auto sc_RatiosUriRelativeTo = L"https://go.microsoft.com/fwlink/?linkid=2091307&localCurrency="; + } +} diff --git a/src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h b/src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h index 50ec8571..69f23f52 100644 --- a/src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h +++ b/src/CalcViewModel/DataLoaders/ICurrencyHttpClient.h @@ -10,13 +10,15 @@ namespace CalculatorApp class ICurrencyHttpClient { public: - virtual ~ICurrencyHttpClient() {} + virtual ~ICurrencyHttpClient() + { + } - virtual void SetSourceCurrencyCode(Platform::String^ sourceCurrencyCode) = 0; - virtual void SetResponseLanguage(Platform::String^ responseLanguage) = 0; + virtual void SetSourceCurrencyCode(Platform::String ^ sourceCurrencyCode) = 0; + virtual void SetResponseLanguage(Platform::String ^ responseLanguage) = 0; - virtual Windows::Foundation::IAsyncOperationWithProgress^ GetCurrencyMetadata() = 0; - virtual Windows::Foundation::IAsyncOperationWithProgress^ GetCurrencyRatios() = 0; + virtual Windows::Foundation::IAsyncOperationWithProgress ^ GetCurrencyMetadata() = 0; + virtual Windows::Foundation::IAsyncOperationWithProgress ^ GetCurrencyRatios() = 0; }; } } diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h b/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h index bd8cb169..0d22a14a 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataConstants.h @@ -1,11 +1,12 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace CalculatorApp { namespace ViewModel { - private enum UnitConverterUnits + private + enum UnitConverterUnits { UnitStart = 0, Area_Acre = UnitStart + 1, @@ -162,7 +163,8 @@ namespace CalculatorApp Data_Zebibytes = UnitStart + 162, Data_Zetabits = UnitStart + 163, Data_Zetabytes = UnitStart + 164, - UnitEnd = Data_Zetabytes + Area_Pyeong = UnitStart + 165, + UnitEnd = Area_Pyeong }; } } diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp index 45411c1d..145bd271 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp @@ -18,22 +18,22 @@ using namespace Windows::Globalization; static constexpr bool CONVERT_WITH_OFFSET_FIRST = true; -UnitConverterDataLoader::UnitConverterDataLoader(GeographicRegion^ region) : - m_currentRegionCode(region->CodeTwoLetter) +UnitConverterDataLoader::UnitConverterDataLoader(GeographicRegion ^ region) + : m_currentRegionCode(region->CodeTwoLetter) { m_categoryList = make_shared>(); - m_categoryToUnits = make_shared(); + m_categoryIDToUnitsMap = make_shared(); m_ratioMap = make_shared(); } -vector UnitConverterDataLoader::LoadOrderedCategories() +vector UnitConverterDataLoader::GetOrderedCategories() { return *m_categoryList; } -vector UnitConverterDataLoader::LoadOrderedUnits(const UCM::Category& category) +vector UnitConverterDataLoader::GetOrderedUnits(const UCM::Category& category) { - return m_categoryToUnits->at(category); + return this->m_categoryIDToUnitsMap->at(category.id); } unordered_map UnitConverterDataLoader::LoadOrderedRatios(const UCM::Unit& unit) @@ -54,9 +54,7 @@ bool UnitConverterDataLoader::SupportsCategory(const UCM::Category& target) } static int currencyId = NavCategory::Serialize(ViewMode::Currency); - auto itr = find_if(supportedCategories->begin(), supportedCategories->end(), - [&](const UCM::Category& category) - { + auto itr = find_if(supportedCategories->begin(), supportedCategories->end(), [&](const UCM::Category& category) { return currencyId != category.id && target.id == category.id; }); @@ -77,8 +75,8 @@ void UnitConverterDataLoader::LoadData() GetConversionData(categoryToUnitConversionDataMap); GetExplicitConversionData(explicitConversionData); // This is needed for temperature conversions - m_categoryToUnits->clear(); - m_ratioMap->clear(); + this->m_categoryIDToUnitsMap->clear(); + this->m_ratioMap->clear(); for (UCM::Category objectCategory : *m_categoryList) { ViewMode categoryViewMode = NavCategory::Deserialize(objectCategory.id); @@ -88,7 +86,7 @@ void UnitConverterDataLoader::LoadData() // Currency is an ordered category but we do not want to process it here // because this function is not thread-safe and currency data is asynchronously // loaded. - m_categoryToUnits->insert(pair>(objectCategory, {})); + this->m_categoryIDToUnitsMap->insert(pair>(objectCategory.id, {})); continue; } @@ -96,7 +94,7 @@ void UnitConverterDataLoader::LoadData() vector unitList; // Sort the units by order - sort(orderedUnits.begin(), orderedUnits.end(), [](const OrderedUnit& first, const OrderedUnit& second){ return first.order < second.order; }); + sort(orderedUnits.begin(), orderedUnits.end(), [](const OrderedUnit& first, const OrderedUnit& second) { return first.order < second.order; }); for (OrderedUnit u : orderedUnits) { @@ -105,7 +103,7 @@ void UnitConverterDataLoader::LoadData() } // Save units per category - m_categoryToUnits->insert(pair>(objectCategory, unitList)); + this->m_categoryIDToUnitsMap->insert(pair>(objectCategory.id, unitList)); // For each unit, populate the conversion data for (UCM::Unit unit : unitList) @@ -118,12 +116,19 @@ void UnitConverterDataLoader::LoadData() unordered_map unitConversions = categoryToUnitConversionDataMap.at(categoryViewMode); double unitFactor = unitConversions[unit.id]; - for (auto itr = unitConversions.begin(); itr != unitConversions.end(); ++itr) + for (const auto& [id, conversionFactor] : unitConversions) { + if (idToUnit.find(id) == idToUnit.end()) + { + // Optional units will not be in idToUnit but can be in unitConversions. + // For optional units that did not make it to the current set of units, just continue. + continue; + } + UCM::ConversionData parsedData = { 1.0, 0.0, false }; - assert(itr->second > 0); // divide by zero assert - parsedData.ratio = unitFactor / itr->second; - conversions.insert(pair(idToUnit.at(itr->first), parsedData)); + assert(conversionFactor > 0); // divide by zero assert + parsedData.ratio = unitFactor / conversionFactor; + conversions.insert(pair(idToUnit.at(id), parsedData)); } } else @@ -147,20 +152,15 @@ void UnitConverterDataLoader::GetCategories(_In_ shared_ptrCategories) { /* Id, CategoryName, SupportsNegative */ - categoriesList->emplace_back( - NavCategory::Serialize(category->Mode), - category->Name->Data(), - category->SupportsNegative); + categoriesList->emplace_back(NavCategory::Serialize(category->Mode), category->Name->Data(), category->SupportsNegative); } } void UnitConverterDataLoader::GetUnits(_In_ unordered_map>& unitMap) { // US + Federated States of Micronesia, Marshall Islands, Palau - bool useUSCustomaryAndFahrenheit = m_currentRegionCode == L"US" || - m_currentRegionCode == L"FM" || - m_currentRegionCode == L"MH" || - m_currentRegionCode == L"PW"; + bool useUSCustomaryAndFahrenheit = + m_currentRegionCode == L"US" || m_currentRegionCode == L"FM" || m_currentRegionCode == L"MH" || m_currentRegionCode == L"PW"; // useUSCustomaryAndFahrenheit + Liberia // Source: https://en.wikipedia.org/wiki/Metrication @@ -175,363 +175,765 @@ void UnitConverterDataLoader::GetUnits(_In_ unordered_map areaUnits; - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Acre, GetLocalizedStringName(L"UnitName_Acre"), GetLocalizedStringName(L"UnitAbbreviation_Acre"), 9 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Hectare, GetLocalizedStringName(L"UnitName_Hectare"), GetLocalizedStringName(L"UnitAbbreviation_Hectare"), 4 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareCentimeter, GetLocalizedStringName(L"UnitName_SquareCentimeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareCentimeter"), 2 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareFoot, GetLocalizedStringName(L"UnitName_SquareFoot"), GetLocalizedStringName(L"UnitAbbreviation_SquareFoot"), 7, useSI, useUSCustomary, false }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareInch, GetLocalizedStringName(L"UnitName_SquareInch"), GetLocalizedStringName(L"UnitAbbreviation_SquareInch"), 6 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareKilometer, GetLocalizedStringName(L"UnitName_SquareKilometer"), GetLocalizedStringName(L"UnitAbbreviation_SquareKilometer"), 5 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMeter, GetLocalizedStringName(L"UnitName_SquareMeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareMeter"), 3, useUSCustomary, useSI, false}); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMile, GetLocalizedStringName(L"UnitName_SquareMile"), GetLocalizedStringName(L"UnitAbbreviation_SquareMile"), 10 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMillimeter, GetLocalizedStringName(L"UnitName_SquareMillimeter"), GetLocalizedStringName(L"UnitAbbreviation_SquareMillimeter"), 1 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareYard, GetLocalizedStringName(L"UnitName_SquareYard"), GetLocalizedStringName(L"UnitAbbreviation_SquareYard"), 8 }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 11, false, false, true}); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Paper, GetLocalizedStringName(L"UnitName_Paper"), GetLocalizedStringName(L"UnitAbbreviation_Paper"), 12, false, false, true }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SoccerField, GetLocalizedStringName(L"UnitName_SoccerField"), GetLocalizedStringName(L"UnitAbbreviation_SoccerField"),13, false, false, true }); - areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Castle, GetLocalizedStringName(L"UnitName_Castle"), GetLocalizedStringName(L"UnitAbbreviation_Castle"), 14, false, false, true }); + areaUnits.push_back( + OrderedUnit{ UnitConverterUnits::Area_Acre, GetLocalizedStringName(L"UnitName_Acre"), GetLocalizedStringName(L"UnitAbbreviation_Acre"), 9 }); + areaUnits.push_back( + OrderedUnit{ UnitConverterUnits::Area_Hectare, GetLocalizedStringName(L"UnitName_Hectare"), GetLocalizedStringName(L"UnitAbbreviation_Hectare"), 4 }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareCentimeter, + GetLocalizedStringName(L"UnitName_SquareCentimeter"), + GetLocalizedStringName(L"UnitAbbreviation_SquareCentimeter"), + 2 }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareFoot, + GetLocalizedStringName(L"UnitName_SquareFoot"), + GetLocalizedStringName(L"UnitAbbreviation_SquareFoot"), + 7, + useSI, + useUSCustomary, + false }); + areaUnits.push_back(OrderedUnit{ + UnitConverterUnits::Area_SquareInch, GetLocalizedStringName(L"UnitName_SquareInch"), GetLocalizedStringName(L"UnitAbbreviation_SquareInch"), 6 }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareKilometer, + GetLocalizedStringName(L"UnitName_SquareKilometer"), + GetLocalizedStringName(L"UnitAbbreviation_SquareKilometer"), + 5 }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMeter, + GetLocalizedStringName(L"UnitName_SquareMeter"), + GetLocalizedStringName(L"UnitAbbreviation_SquareMeter"), + 3, + useUSCustomary, + useSI, + false }); + areaUnits.push_back(OrderedUnit{ + UnitConverterUnits::Area_SquareMile, GetLocalizedStringName(L"UnitName_SquareMile"), GetLocalizedStringName(L"UnitAbbreviation_SquareMile"), 10 }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SquareMillimeter, + GetLocalizedStringName(L"UnitName_SquareMillimeter"), + GetLocalizedStringName(L"UnitAbbreviation_SquareMillimeter"), + 1 }); + areaUnits.push_back(OrderedUnit{ + UnitConverterUnits::Area_SquareYard, GetLocalizedStringName(L"UnitName_SquareYard"), GetLocalizedStringName(L"UnitAbbreviation_SquareYard"), 8 }); + areaUnits.push_back(OrderedUnit{ + UnitConverterUnits::Area_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 11, false, false, true }); + areaUnits.push_back(OrderedUnit{ + UnitConverterUnits::Area_Paper, GetLocalizedStringName(L"UnitName_Paper"), GetLocalizedStringName(L"UnitAbbreviation_Paper"), 12, false, false, true }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_SoccerField, + GetLocalizedStringName(L"UnitName_SoccerField"), + GetLocalizedStringName(L"UnitAbbreviation_SoccerField"), + 13, + false, + false, + true }); + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Castle, + GetLocalizedStringName(L"UnitName_Castle"), + GetLocalizedStringName(L"UnitAbbreviation_Castle"), + 14, + false, + false, + true }); + if (usePyeong) + { + areaUnits.push_back(OrderedUnit{ UnitConverterUnits::Area_Pyeong, + GetLocalizedStringName(L"UnitName_Pyeong"), + GetLocalizedStringName(L"UnitAbbreviation_Pyeong"), + 15, + false, + false, + false }); + } unitMap.emplace(ViewMode::Area, areaUnits); vector dataUnits; - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Bit, GetLocalizedStringName(L"UnitName_Bit"), GetLocalizedStringName(L"UnitAbbreviation_Bit"), 1 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Byte, GetLocalizedStringName(L"UnitName_Byte"), GetLocalizedStringName(L"UnitAbbreviation_Byte"), 2 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exabits, GetLocalizedStringName(L"UnitName_Exabits"), GetLocalizedStringName(L"UnitAbbreviation_Exabits"), 23 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exabytes, GetLocalizedStringName(L"UnitName_Exabytes"), GetLocalizedStringName(L"UnitAbbreviation_Exabytes"), 25 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exbibits, GetLocalizedStringName(L"UnitName_Exbibits"), GetLocalizedStringName(L"UnitAbbreviation_Exbibits"), 24 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Exbibytes, GetLocalizedStringName(L"UnitName_Exbibytes"), GetLocalizedStringName(L"UnitAbbreviation_Exbibytes"), 26 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gibibits, GetLocalizedStringName(L"UnitName_Gibibits"), GetLocalizedStringName(L"UnitAbbreviation_Gibibits"), 12 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gibibytes, GetLocalizedStringName(L"UnitName_Gibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Gibibytes"), 14 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gigabit, GetLocalizedStringName(L"UnitName_Gigabit"), GetLocalizedStringName(L"UnitAbbreviation_Gigabit"), 11 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gigabyte, GetLocalizedStringName(L"UnitName_Gigabyte"), GetLocalizedStringName(L"UnitAbbreviation_Gigabyte"),13, true, false, false}); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kibibits, GetLocalizedStringName(L"UnitName_Kibibits"), GetLocalizedStringName(L"UnitAbbreviation_Kibibits"), 4 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kibibytes, GetLocalizedStringName(L"UnitName_Kibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Kibibytes"),6 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kilobit, GetLocalizedStringName(L"UnitName_Kilobit"), GetLocalizedStringName(L"UnitAbbreviation_Kilobit"), 3 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Kilobyte, GetLocalizedStringName(L"UnitName_Kilobyte"), GetLocalizedStringName(L"UnitAbbreviation_Kilobyte"), 5 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Mebibits, GetLocalizedStringName(L"UnitName_Mebibits"), GetLocalizedStringName(L"UnitAbbreviation_Mebibits"), 8 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Mebibytes, GetLocalizedStringName(L"UnitName_Mebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Mebibytes"), 10 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Megabit, GetLocalizedStringName(L"UnitName_Megabit"), GetLocalizedStringName(L"UnitAbbreviation_Megabit"), 7 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Megabyte, GetLocalizedStringName(L"UnitName_Megabyte"), GetLocalizedStringName(L"UnitAbbreviation_Megabyte"), 9, false, true, false}); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Pebibits, GetLocalizedStringName(L"UnitName_Pebibits"), GetLocalizedStringName(L"UnitAbbreviation_Pebibits"), 20 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Pebibytes, GetLocalizedStringName(L"UnitName_Pebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Pebibytes"), 22 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Petabit, GetLocalizedStringName(L"UnitName_Petabit"), GetLocalizedStringName(L"UnitAbbreviation_Petabit"), 19 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Petabyte, GetLocalizedStringName(L"UnitName_Petabyte"), GetLocalizedStringName(L"UnitAbbreviation_Petabyte"), 21 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Tebibits, GetLocalizedStringName(L"UnitName_Tebibits"), GetLocalizedStringName(L"UnitAbbreviation_Tebibits"), 16 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Tebibytes, GetLocalizedStringName(L"UnitName_Tebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Tebibytes"), 18 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Terabit, GetLocalizedStringName(L"UnitName_Terabit"), GetLocalizedStringName(L"UnitAbbreviation_Terabit"), 15 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Terabyte, GetLocalizedStringName(L"UnitName_Terabyte"), GetLocalizedStringName(L"UnitAbbreviation_Terabyte"), 17 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yobibits, GetLocalizedStringName(L"UnitName_Yobibits"), GetLocalizedStringName(L"UnitAbbreviation_Yobibits"), 32 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yobibytes, GetLocalizedStringName(L"UnitName_Yobibytes"), GetLocalizedStringName(L"UnitAbbreviation_Yobibytes"), 34 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yottabit, GetLocalizedStringName(L"UnitName_Yottabit"), GetLocalizedStringName(L"UnitAbbreviation_Yottabit"), 31 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Yottabyte, GetLocalizedStringName(L"UnitName_Yottabyte"), GetLocalizedStringName(L"UnitAbbreviation_Yottabyte"), 33 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zebibits, GetLocalizedStringName(L"UnitName_Zebibits"), GetLocalizedStringName(L"UnitAbbreviation_Zebibits"), 28 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zebibytes, GetLocalizedStringName(L"UnitName_Zebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Zebibytes"), 30 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zetabits, GetLocalizedStringName(L"UnitName_Zetabits"), GetLocalizedStringName(L"UnitAbbreviation_Zetabits"), 27 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Zetabytes, GetLocalizedStringName(L"UnitName_Zetabytes"), GetLocalizedStringName(L"UnitAbbreviation_Zetabytes"),29 }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_FloppyDisk, GetLocalizedStringName(L"UnitName_FloppyDisk"), GetLocalizedStringName(L"UnitAbbreviation_FloppyDisk"), 13, false, false, true }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_CD, GetLocalizedStringName(L"UnitName_CD"), GetLocalizedStringName(L"UnitAbbreviation_CD"), 14, false, false, true }); - dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_DVD, GetLocalizedStringName(L"UnitName_DVD"), GetLocalizedStringName(L"UnitAbbreviation_DVD"), 15, false, false, true }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Bit, GetLocalizedStringName(L"UnitName_Bit"), GetLocalizedStringName(L"UnitAbbreviation_Bit"), 1 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Byte, GetLocalizedStringName(L"UnitName_Byte"), GetLocalizedStringName(L"UnitAbbreviation_Byte"), 2 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Exabits, GetLocalizedStringName(L"UnitName_Exabits"), GetLocalizedStringName(L"UnitAbbreviation_Exabits"), 23 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Exabytes, GetLocalizedStringName(L"UnitName_Exabytes"), GetLocalizedStringName(L"UnitAbbreviation_Exabytes"), 25 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Exbibits, GetLocalizedStringName(L"UnitName_Exbibits"), GetLocalizedStringName(L"UnitAbbreviation_Exbibits"), 24 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Exbibytes, GetLocalizedStringName(L"UnitName_Exbibytes"), GetLocalizedStringName(L"UnitAbbreviation_Exbibytes"), 26 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Gibibits, GetLocalizedStringName(L"UnitName_Gibibits"), GetLocalizedStringName(L"UnitAbbreviation_Gibibits"), 12 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Gibibytes, GetLocalizedStringName(L"UnitName_Gibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Gibibytes"), 14 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Gigabit, GetLocalizedStringName(L"UnitName_Gigabit"), GetLocalizedStringName(L"UnitAbbreviation_Gigabit"), 11 }); + dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Gigabyte, + GetLocalizedStringName(L"UnitName_Gigabyte"), + GetLocalizedStringName(L"UnitAbbreviation_Gigabyte"), + 13, + true, + false, + false }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Kibibits, GetLocalizedStringName(L"UnitName_Kibibits"), GetLocalizedStringName(L"UnitAbbreviation_Kibibits"), 4 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Kibibytes, GetLocalizedStringName(L"UnitName_Kibibytes"), GetLocalizedStringName(L"UnitAbbreviation_Kibibytes"), 6 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Kilobit, GetLocalizedStringName(L"UnitName_Kilobit"), GetLocalizedStringName(L"UnitAbbreviation_Kilobit"), 3 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Kilobyte, GetLocalizedStringName(L"UnitName_Kilobyte"), GetLocalizedStringName(L"UnitAbbreviation_Kilobyte"), 5 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Mebibits, GetLocalizedStringName(L"UnitName_Mebibits"), GetLocalizedStringName(L"UnitAbbreviation_Mebibits"), 8 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Mebibytes, GetLocalizedStringName(L"UnitName_Mebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Mebibytes"), 10 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Megabit, GetLocalizedStringName(L"UnitName_Megabit"), GetLocalizedStringName(L"UnitAbbreviation_Megabit"), 7 }); + dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_Megabyte, + GetLocalizedStringName(L"UnitName_Megabyte"), + GetLocalizedStringName(L"UnitAbbreviation_Megabyte"), + 9, + false, + true, + false }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Pebibits, GetLocalizedStringName(L"UnitName_Pebibits"), GetLocalizedStringName(L"UnitAbbreviation_Pebibits"), 20 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Pebibytes, GetLocalizedStringName(L"UnitName_Pebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Pebibytes"), 22 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Petabit, GetLocalizedStringName(L"UnitName_Petabit"), GetLocalizedStringName(L"UnitAbbreviation_Petabit"), 19 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Petabyte, GetLocalizedStringName(L"UnitName_Petabyte"), GetLocalizedStringName(L"UnitAbbreviation_Petabyte"), 21 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Tebibits, GetLocalizedStringName(L"UnitName_Tebibits"), GetLocalizedStringName(L"UnitAbbreviation_Tebibits"), 16 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Tebibytes, GetLocalizedStringName(L"UnitName_Tebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Tebibytes"), 18 }); + dataUnits.push_back( + OrderedUnit{ UnitConverterUnits::Data_Terabit, GetLocalizedStringName(L"UnitName_Terabit"), GetLocalizedStringName(L"UnitAbbreviation_Terabit"), 15 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Terabyte, GetLocalizedStringName(L"UnitName_Terabyte"), GetLocalizedStringName(L"UnitAbbreviation_Terabyte"), 17 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Yobibits, GetLocalizedStringName(L"UnitName_Yobibits"), GetLocalizedStringName(L"UnitAbbreviation_Yobibits"), 32 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Yobibytes, GetLocalizedStringName(L"UnitName_Yobibytes"), GetLocalizedStringName(L"UnitAbbreviation_Yobibytes"), 34 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Yottabit, GetLocalizedStringName(L"UnitName_Yottabit"), GetLocalizedStringName(L"UnitAbbreviation_Yottabit"), 31 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Yottabyte, GetLocalizedStringName(L"UnitName_Yottabyte"), GetLocalizedStringName(L"UnitAbbreviation_Yottabyte"), 33 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Zebibits, GetLocalizedStringName(L"UnitName_Zebibits"), GetLocalizedStringName(L"UnitAbbreviation_Zebibits"), 28 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Zebibytes, GetLocalizedStringName(L"UnitName_Zebibytes"), GetLocalizedStringName(L"UnitAbbreviation_Zebibytes"), 30 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Zetabits, GetLocalizedStringName(L"UnitName_Zetabits"), GetLocalizedStringName(L"UnitAbbreviation_Zetabits"), 27 }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_Zetabytes, GetLocalizedStringName(L"UnitName_Zetabytes"), GetLocalizedStringName(L"UnitAbbreviation_Zetabytes"), 29 }); + dataUnits.push_back(OrderedUnit{ UnitConverterUnits::Data_FloppyDisk, + GetLocalizedStringName(L"UnitName_FloppyDisk"), + GetLocalizedStringName(L"UnitAbbreviation_FloppyDisk"), + 13, + false, + false, + true }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_CD, GetLocalizedStringName(L"UnitName_CD"), GetLocalizedStringName(L"UnitAbbreviation_CD"), 14, false, false, true }); + dataUnits.push_back(OrderedUnit{ + UnitConverterUnits::Data_DVD, GetLocalizedStringName(L"UnitName_DVD"), GetLocalizedStringName(L"UnitAbbreviation_DVD"), 15, false, false, true }); unitMap.emplace(ViewMode::Data, dataUnits); vector energyUnits; - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_BritishThermalUnit, GetLocalizedStringName(L"UnitName_BritishThermalUnit"), GetLocalizedStringName(L"UnitAbbreviation_BritishThermalUnit"), 7 }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Calorie, GetLocalizedStringName(L"UnitName_Calorie"), GetLocalizedStringName(L"UnitAbbreviation_Calorie"), 4 }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_ElectronVolt, GetLocalizedStringName(L"UnitName_Electron-Volt"), GetLocalizedStringName(L"UnitAbbreviation_Electron-Volt"), 1 }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_FootPound, GetLocalizedStringName(L"UnitName_Foot-Pound"), GetLocalizedStringName(L"UnitAbbreviation_Foot-Pound"), 6 }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Joule, GetLocalizedStringName(L"UnitName_Joule"), GetLocalizedStringName(L"UnitAbbreviation_Joule"), 2, true, false, false}); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Kilocalorie, GetLocalizedStringName(L"UnitName_Kilocalorie"), GetLocalizedStringName(L"UnitAbbreviation_Kilocalorie"), 5, false, true, false }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Kilojoule, GetLocalizedStringName(L"UnitName_Kilojoule"), GetLocalizedStringName(L"UnitAbbreviation_Kilojoule"), 3 }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Battery, GetLocalizedStringName(L"UnitName_Battery"), GetLocalizedStringName(L"UnitAbbreviation_Battery"), 8, false, false, true }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Banana, GetLocalizedStringName(L"UnitName_Banana"), GetLocalizedStringName(L"UnitAbbreviation_Banana"), 9, false, false, true }); - energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_SliceOfCake, GetLocalizedStringName(L"UnitName_SliceOfCake"), GetLocalizedStringName(L"UnitAbbreviation_SliceOfCake"),10, false, false, true }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_BritishThermalUnit, + GetLocalizedStringName(L"UnitName_BritishThermalUnit"), + GetLocalizedStringName(L"UnitAbbreviation_BritishThermalUnit"), + 7 }); + energyUnits.push_back( + OrderedUnit{ UnitConverterUnits::Energy_Calorie, GetLocalizedStringName(L"UnitName_Calorie"), GetLocalizedStringName(L"UnitAbbreviation_Calorie"), 4 }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_ElectronVolt, + GetLocalizedStringName(L"UnitName_Electron-Volt"), + GetLocalizedStringName(L"UnitAbbreviation_Electron-Volt"), + 1 }); + energyUnits.push_back(OrderedUnit{ + UnitConverterUnits::Energy_FootPound, GetLocalizedStringName(L"UnitName_Foot-Pound"), GetLocalizedStringName(L"UnitAbbreviation_Foot-Pound"), 6 }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Joule, + GetLocalizedStringName(L"UnitName_Joule"), + GetLocalizedStringName(L"UnitAbbreviation_Joule"), + 2, + true, + false, + false }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Kilocalorie, + GetLocalizedStringName(L"UnitName_Kilocalorie"), + GetLocalizedStringName(L"UnitAbbreviation_Kilocalorie"), + 5, + false, + true, + false }); + energyUnits.push_back(OrderedUnit{ + UnitConverterUnits::Energy_Kilojoule, GetLocalizedStringName(L"UnitName_Kilojoule"), GetLocalizedStringName(L"UnitAbbreviation_Kilojoule"), 3 }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Battery, + GetLocalizedStringName(L"UnitName_Battery"), + GetLocalizedStringName(L"UnitAbbreviation_Battery"), + 8, + false, + false, + true }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_Banana, + GetLocalizedStringName(L"UnitName_Banana"), + GetLocalizedStringName(L"UnitAbbreviation_Banana"), + 9, + false, + false, + true }); + energyUnits.push_back(OrderedUnit{ UnitConverterUnits::Energy_SliceOfCake, + GetLocalizedStringName(L"UnitName_SliceOfCake"), + GetLocalizedStringName(L"UnitAbbreviation_SliceOfCake"), + 10, + false, + false, + true }); unitMap.emplace(ViewMode::Energy, energyUnits); vector lengthUnits; - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Centimeter, GetLocalizedStringName(L"UnitName_Centimeter"), GetLocalizedStringName(L"UnitAbbreviation_Centimeter"), 4, useUSCustomary, useSI, false}); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Foot, GetLocalizedStringName(L"UnitName_Foot"), GetLocalizedStringName(L"UnitAbbreviation_Foot"), 8 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Inch, GetLocalizedStringName(L"UnitName_Inch"), GetLocalizedStringName(L"UnitAbbreviation_Inch"), 7 , useSI, useUSCustomary, false }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Kilometer, GetLocalizedStringName(L"UnitName_Kilometer"), GetLocalizedStringName(L"UnitAbbreviation_Kilometer"), 6 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Meter, GetLocalizedStringName(L"UnitName_Meter"), GetLocalizedStringName(L"UnitAbbreviation_Meter"), 5 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Micron, GetLocalizedStringName(L"UnitName_Micron"), GetLocalizedStringName(L"UnitAbbreviation_Micron"), 2 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Mile, GetLocalizedStringName(L"UnitName_Mile"), GetLocalizedStringName(L"UnitAbbreviation_Mile"), 10 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Millimeter, GetLocalizedStringName(L"UnitName_Millimeter"), GetLocalizedStringName(L"UnitAbbreviation_Millimeter"), 3 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Nanometer, GetLocalizedStringName(L"UnitName_Nanometer"), GetLocalizedStringName(L"UnitAbbreviation_Nanometer"), 1 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_NauticalMile, GetLocalizedStringName(L"UnitName_NauticalMile"), GetLocalizedStringName(L"UnitAbbreviation_NauticalMile"), 11 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Yard, GetLocalizedStringName(L"UnitName_Yard"), GetLocalizedStringName(L"UnitAbbreviation_Yard"), 9 }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Paperclip, GetLocalizedStringName(L"UnitName_Paperclip"), GetLocalizedStringName(L"UnitAbbreviation_Paperclip"), 12 ,false, false, true }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 13 ,false, false, true }); - lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_JumboJet, GetLocalizedStringName(L"UnitName_JumboJet"), GetLocalizedStringName(L"UnitAbbreviation_JumboJet"), 14 , false, false, true }); + lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Centimeter, + GetLocalizedStringName(L"UnitName_Centimeter"), + GetLocalizedStringName(L"UnitAbbreviation_Centimeter"), + 4, + useUSCustomary, + useSI, + false }); + lengthUnits.push_back( + OrderedUnit{ UnitConverterUnits::Length_Foot, GetLocalizedStringName(L"UnitName_Foot"), GetLocalizedStringName(L"UnitAbbreviation_Foot"), 8 }); + lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Inch, + GetLocalizedStringName(L"UnitName_Inch"), + GetLocalizedStringName(L"UnitAbbreviation_Inch"), + 7, + useSI, + useUSCustomary, + false }); + lengthUnits.push_back(OrderedUnit{ + UnitConverterUnits::Length_Kilometer, GetLocalizedStringName(L"UnitName_Kilometer"), GetLocalizedStringName(L"UnitAbbreviation_Kilometer"), 6 }); + lengthUnits.push_back( + OrderedUnit{ UnitConverterUnits::Length_Meter, GetLocalizedStringName(L"UnitName_Meter"), GetLocalizedStringName(L"UnitAbbreviation_Meter"), 5 }); + lengthUnits.push_back( + OrderedUnit{ UnitConverterUnits::Length_Micron, GetLocalizedStringName(L"UnitName_Micron"), GetLocalizedStringName(L"UnitAbbreviation_Micron"), 2 }); + lengthUnits.push_back( + OrderedUnit{ UnitConverterUnits::Length_Mile, GetLocalizedStringName(L"UnitName_Mile"), GetLocalizedStringName(L"UnitAbbreviation_Mile"), 10 }); + lengthUnits.push_back(OrderedUnit{ + UnitConverterUnits::Length_Millimeter, GetLocalizedStringName(L"UnitName_Millimeter"), GetLocalizedStringName(L"UnitAbbreviation_Millimeter"), 3 }); + lengthUnits.push_back(OrderedUnit{ + UnitConverterUnits::Length_Nanometer, GetLocalizedStringName(L"UnitName_Nanometer"), GetLocalizedStringName(L"UnitAbbreviation_Nanometer"), 1 }); + lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_NauticalMile, + GetLocalizedStringName(L"UnitName_NauticalMile"), + GetLocalizedStringName(L"UnitAbbreviation_NauticalMile"), + 11 }); + lengthUnits.push_back( + OrderedUnit{ UnitConverterUnits::Length_Yard, GetLocalizedStringName(L"UnitName_Yard"), GetLocalizedStringName(L"UnitAbbreviation_Yard"), 9 }); + lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_Paperclip, + GetLocalizedStringName(L"UnitName_Paperclip"), + GetLocalizedStringName(L"UnitAbbreviation_Paperclip"), + 12, + false, + false, + true }); + lengthUnits.push_back(OrderedUnit{ + UnitConverterUnits::Length_Hand, GetLocalizedStringName(L"UnitName_Hand"), GetLocalizedStringName(L"UnitAbbreviation_Hand"), 13, false, false, true }); + lengthUnits.push_back(OrderedUnit{ UnitConverterUnits::Length_JumboJet, + GetLocalizedStringName(L"UnitName_JumboJet"), + GetLocalizedStringName(L"UnitAbbreviation_JumboJet"), + 14, + false, + false, + true }); unitMap.emplace(ViewMode::Length, lengthUnits); vector powerUnits; - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_BritishThermalUnitPerMinute, GetLocalizedStringName(L"UnitName_BTUPerMinute"), GetLocalizedStringName(L"UnitAbbreviation_BTUPerMinute"), 5 }); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_FootPoundPerMinute, GetLocalizedStringName(L"UnitName_Foot-PoundPerMinute"), GetLocalizedStringName(L"UnitAbbreviation_Foot-PoundPerMinute"), 4 }); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Horsepower, GetLocalizedStringName(L"UnitName_Horsepower"), GetLocalizedStringName(L"UnitAbbreviation_Horsepower") , 3 , false, true, false }); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Kilowatt, GetLocalizedStringName(L"UnitName_Kilowatt"), GetLocalizedStringName(L"UnitAbbreviation_Kilowatt"), 2 , !useWattInsteadOfKilowatt}); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Watt, GetLocalizedStringName(L"UnitName_Watt"), GetLocalizedStringName(L"UnitAbbreviation_Watt"), 1, useWattInsteadOfKilowatt}); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_LightBulb, GetLocalizedStringName(L"UnitName_LightBulb"), GetLocalizedStringName(L"UnitAbbreviation_LightBulb"), 6 ,false, false, true}); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"), 7 ,false, false, true}); - powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_TrainEngine, GetLocalizedStringName(L"UnitName_TrainEngine"), GetLocalizedStringName(L"UnitAbbreviation_TrainEngine"), 8 ,false, false, true }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_BritishThermalUnitPerMinute, + GetLocalizedStringName(L"UnitName_BTUPerMinute"), + GetLocalizedStringName(L"UnitAbbreviation_BTUPerMinute"), + 5 }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_FootPoundPerMinute, + GetLocalizedStringName(L"UnitName_Foot-PoundPerMinute"), + GetLocalizedStringName(L"UnitAbbreviation_Foot-PoundPerMinute"), + 4 }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Horsepower, + GetLocalizedStringName(L"UnitName_Horsepower"), + GetLocalizedStringName(L"UnitAbbreviation_Horsepower"), + 3, + false, + true, + false }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Kilowatt, + GetLocalizedStringName(L"UnitName_Kilowatt"), + GetLocalizedStringName(L"UnitAbbreviation_Kilowatt"), + 2, + !useWattInsteadOfKilowatt }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_Watt, + GetLocalizedStringName(L"UnitName_Watt"), + GetLocalizedStringName(L"UnitAbbreviation_Watt"), + 1, + useWattInsteadOfKilowatt }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_LightBulb, + GetLocalizedStringName(L"UnitName_LightBulb"), + GetLocalizedStringName(L"UnitAbbreviation_LightBulb"), + 6, + false, + false, + true }); + powerUnits.push_back(OrderedUnit{ + UnitConverterUnits::Power_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"), 7, false, false, true }); + powerUnits.push_back(OrderedUnit{ UnitConverterUnits::Power_TrainEngine, + GetLocalizedStringName(L"UnitName_TrainEngine"), + GetLocalizedStringName(L"UnitAbbreviation_TrainEngine"), + 8, + false, + false, + true }); unitMap.emplace(ViewMode::Power, powerUnits); vector tempUnits; - tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesCelsius, GetLocalizedStringName(L"UnitName_DegreesCelsius"), GetLocalizedStringName(L"UnitAbbreviation_DegreesCelsius"), 1, useFahrenheit, !useFahrenheit, false }); - tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesFahrenheit, GetLocalizedStringName(L"UnitName_DegreesFahrenheit"), GetLocalizedStringName(L"UnitAbbreviation_DegreesFahrenheit"), 2 , !useFahrenheit, useFahrenheit, false }); - tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_Kelvin, GetLocalizedStringName(L"UnitName_Kelvin"), GetLocalizedStringName(L"UnitAbbreviation_Kelvin"), 3 }); + tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesCelsius, + GetLocalizedStringName(L"UnitName_DegreesCelsius"), + GetLocalizedStringName(L"UnitAbbreviation_DegreesCelsius"), + 1, + useFahrenheit, + !useFahrenheit, + false }); + tempUnits.push_back(OrderedUnit{ UnitConverterUnits::Temperature_DegreesFahrenheit, + GetLocalizedStringName(L"UnitName_DegreesFahrenheit"), + GetLocalizedStringName(L"UnitAbbreviation_DegreesFahrenheit"), + 2, + !useFahrenheit, + useFahrenheit, + false }); + tempUnits.push_back(OrderedUnit{ + UnitConverterUnits::Temperature_Kelvin, GetLocalizedStringName(L"UnitName_Kelvin"), GetLocalizedStringName(L"UnitAbbreviation_Kelvin"), 3 }); unitMap.emplace(ViewMode::Temperature, tempUnits); vector timeUnits; - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Day, GetLocalizedStringName(L"UnitName_Day"), GetLocalizedStringName(L"UnitAbbreviation_Day"), 6 }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Hour, GetLocalizedStringName(L"UnitName_Hour"), GetLocalizedStringName(L"UnitAbbreviation_Hour"), 5 ,true, false, false }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Microsecond, GetLocalizedStringName(L"UnitName_Microsecond"), GetLocalizedStringName(L"UnitAbbreviation_Microsecond"), 1 }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Millisecond, GetLocalizedStringName(L"UnitName_Millisecond"), GetLocalizedStringName(L"UnitAbbreviation_Millisecond"), 2 }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Minute, GetLocalizedStringName(L"UnitName_Minute"), GetLocalizedStringName(L"UnitAbbreviation_Minute"), 4 ,false, true, false }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Second, GetLocalizedStringName(L"UnitName_Second"), GetLocalizedStringName(L"UnitAbbreviation_Second"), 3 }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Week, GetLocalizedStringName(L"UnitName_Week"), GetLocalizedStringName(L"UnitAbbreviation_Week"), 7 }); - timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Year, GetLocalizedStringName(L"UnitName_Year"), GetLocalizedStringName(L"UnitAbbreviation_Year"), 8 }); + timeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Time_Day, GetLocalizedStringName(L"UnitName_Day"), GetLocalizedStringName(L"UnitAbbreviation_Day"), 6 }); + timeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Time_Hour, GetLocalizedStringName(L"UnitName_Hour"), GetLocalizedStringName(L"UnitAbbreviation_Hour"), 5, true, false, false }); + timeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Time_Microsecond, GetLocalizedStringName(L"UnitName_Microsecond"), GetLocalizedStringName(L"UnitAbbreviation_Microsecond"), 1 }); + timeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Time_Millisecond, GetLocalizedStringName(L"UnitName_Millisecond"), GetLocalizedStringName(L"UnitAbbreviation_Millisecond"), 2 }); + timeUnits.push_back(OrderedUnit{ UnitConverterUnits::Time_Minute, + GetLocalizedStringName(L"UnitName_Minute"), + GetLocalizedStringName(L"UnitAbbreviation_Minute"), + 4, + false, + true, + false }); + timeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Time_Second, GetLocalizedStringName(L"UnitName_Second"), GetLocalizedStringName(L"UnitAbbreviation_Second"), 3 }); + timeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Time_Week, GetLocalizedStringName(L"UnitName_Week"), GetLocalizedStringName(L"UnitAbbreviation_Week"), 7 }); + timeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Time_Year, GetLocalizedStringName(L"UnitName_Year"), GetLocalizedStringName(L"UnitAbbreviation_Year"), 8 }); unitMap.emplace(ViewMode::Time, timeUnits); vector speedUnits; - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_CentimetersPerSecond, GetLocalizedStringName(L"UnitName_CentimetersPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_CentimetersPerSecond"), 1 }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_FeetPerSecond, GetLocalizedStringName(L"UnitName_FeetPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_FeetPerSecond"), 4 }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_KilometersPerHour, GetLocalizedStringName(L"UnitName_KilometersPerHour"), GetLocalizedStringName(L"UnitAbbreviation_KilometersPerHour"), 3 , useUSCustomary, useSI, false }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Knot, GetLocalizedStringName(L"UnitName_Knot"), GetLocalizedStringName(L"UnitAbbreviation_Knot"), 6 }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Mach, GetLocalizedStringName(L"UnitName_Mach"), GetLocalizedStringName(L"UnitAbbreviation_Mach"), 7 }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MetersPerSecond, GetLocalizedStringName(L"UnitName_MetersPerSecond"), GetLocalizedStringName(L"UnitAbbreviation_MetersPerSecond"), 2 }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MilesPerHour, GetLocalizedStringName(L"UnitName_MilesPerHour"), GetLocalizedStringName(L"UnitAbbreviation_MilesPerHour"), 5, useSI, useUSCustomary, false }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Turtle, GetLocalizedStringName(L"UnitName_Turtle"), GetLocalizedStringName(L"UnitAbbreviation_Turtle"), 8 ,false, false, true }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"),9 , false, false, true }); - speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Jet, GetLocalizedStringName(L"UnitName_Jet"), GetLocalizedStringName(L"UnitAbbreviation_Jet"), 10, false, false, true }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_CentimetersPerSecond, + GetLocalizedStringName(L"UnitName_CentimetersPerSecond"), + GetLocalizedStringName(L"UnitAbbreviation_CentimetersPerSecond"), + 1 }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_FeetPerSecond, + GetLocalizedStringName(L"UnitName_FeetPerSecond"), + GetLocalizedStringName(L"UnitAbbreviation_FeetPerSecond"), + 4 }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_KilometersPerHour, + GetLocalizedStringName(L"UnitName_KilometersPerHour"), + GetLocalizedStringName(L"UnitAbbreviation_KilometersPerHour"), + 3, + useUSCustomary, + useSI, + false }); + speedUnits.push_back( + OrderedUnit{ UnitConverterUnits::Speed_Knot, GetLocalizedStringName(L"UnitName_Knot"), GetLocalizedStringName(L"UnitAbbreviation_Knot"), 6 }); + speedUnits.push_back( + OrderedUnit{ UnitConverterUnits::Speed_Mach, GetLocalizedStringName(L"UnitName_Mach"), GetLocalizedStringName(L"UnitAbbreviation_Mach"), 7 }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MetersPerSecond, + GetLocalizedStringName(L"UnitName_MetersPerSecond"), + GetLocalizedStringName(L"UnitAbbreviation_MetersPerSecond"), + 2 }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_MilesPerHour, + GetLocalizedStringName(L"UnitName_MilesPerHour"), + GetLocalizedStringName(L"UnitAbbreviation_MilesPerHour"), + 5, + useSI, + useUSCustomary, + false }); + speedUnits.push_back(OrderedUnit{ UnitConverterUnits::Speed_Turtle, + GetLocalizedStringName(L"UnitName_Turtle"), + GetLocalizedStringName(L"UnitAbbreviation_Turtle"), + 8, + false, + false, + true }); + speedUnits.push_back(OrderedUnit{ + UnitConverterUnits::Speed_Horse, GetLocalizedStringName(L"UnitName_Horse"), GetLocalizedStringName(L"UnitAbbreviation_Horse"), 9, false, false, true }); + speedUnits.push_back(OrderedUnit{ + UnitConverterUnits::Speed_Jet, GetLocalizedStringName(L"UnitName_Jet"), GetLocalizedStringName(L"UnitAbbreviation_Jet"), 10, false, false, true }); unitMap.emplace(ViewMode::Speed, speedUnits); vector volumeUnits; - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicCentimeter, GetLocalizedStringName(L"UnitName_CubicCentimeter"), GetLocalizedStringName(L"UnitAbbreviation_CubicCentimeter"), 2 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicFoot, GetLocalizedStringName(L"UnitName_CubicFoot"), GetLocalizedStringName(L"UnitAbbreviation_CubicFoot"), 13 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicInch, GetLocalizedStringName(L"UnitName_CubicInch"), GetLocalizedStringName(L"UnitAbbreviation_CubicInch"), 12 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicMeter, GetLocalizedStringName(L"UnitName_CubicMeter"), GetLocalizedStringName(L"UnitAbbreviation_CubicMeter"), 4 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicYard, GetLocalizedStringName(L"UnitName_CubicYard"), GetLocalizedStringName(L"UnitAbbreviation_CubicYard"), 14 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CupUS, GetLocalizedStringName(L"UnitName_CupUS"), GetLocalizedStringName(L"UnitAbbreviation_CupUS"), 8 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUK, GetLocalizedStringName(L"UnitName_FluidOunceUK"), GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUK"), 17 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUS, GetLocalizedStringName(L"UnitName_FluidOunceUS"), GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUS"), 7 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_GallonUK, GetLocalizedStringName(L"UnitName_GallonUK"), GetLocalizedStringName(L"UnitAbbreviation_GallonUK"), 20 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_GallonUS, GetLocalizedStringName(L"UnitName_GallonUS"), GetLocalizedStringName(L"UnitAbbreviation_GallonUS"), 11 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Liter, GetLocalizedStringName(L"UnitName_Liter"), GetLocalizedStringName(L"UnitAbbreviation_Liter"), 3 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Milliliter, GetLocalizedStringName(L"UnitName_Milliliter"), GetLocalizedStringName(L"UnitAbbreviation_Milliliter"), 1, useUSCustomary, useSI}); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_PintUK, GetLocalizedStringName(L"UnitName_PintUK"), GetLocalizedStringName(L"UnitAbbreviation_PintUK"), 18 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_PintUS, GetLocalizedStringName(L"UnitName_PintUS"), GetLocalizedStringName(L"UnitAbbreviation_PintUS"), 9 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUS, GetLocalizedStringName(L"UnitName_TablespoonUS"), GetLocalizedStringName(L"UnitAbbreviation_TablespoonUS"), 6 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUS, GetLocalizedStringName(L"UnitName_TeaspoonUS"), GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUS"), 5, useSI, useUSCustomary && m_currentRegionCode != "GB" }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_QuartUK, GetLocalizedStringName(L"UnitName_QuartUK"), GetLocalizedStringName(L"UnitAbbreviation_QuartUK"), 19 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_QuartUS, GetLocalizedStringName(L"UnitName_QuartUS"), GetLocalizedStringName(L"UnitAbbreviation_QuartUS"), 10 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUK, GetLocalizedStringName(L"UnitName_TeaspoonUK"), GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUK"), 15, false, useUSCustomary && m_currentRegionCode == "GB"}); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUK, GetLocalizedStringName(L"UnitName_TablespoonUK"), GetLocalizedStringName(L"UnitAbbreviation_TablespoonUK"), 16 }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CoffeeCup, GetLocalizedStringName(L"UnitName_CoffeeCup"), GetLocalizedStringName(L"UnitAbbreviation_CoffeeCup"), 22 ,false, false, true }); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Bathtub, GetLocalizedStringName(L"UnitName_Bathtub"), GetLocalizedStringName(L"UnitAbbreviation_Bathtub"), 23 ,false, false, true}); - volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_SwimmingPool, GetLocalizedStringName(L"UnitName_SwimmingPool"), GetLocalizedStringName(L"UnitAbbreviation_SwimmingPool"), 24 ,false, false, true }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CubicCentimeter, + GetLocalizedStringName(L"UnitName_CubicCentimeter"), + GetLocalizedStringName(L"UnitAbbreviation_CubicCentimeter"), + 2 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_CubicFoot, GetLocalizedStringName(L"UnitName_CubicFoot"), GetLocalizedStringName(L"UnitAbbreviation_CubicFoot"), 13 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_CubicInch, GetLocalizedStringName(L"UnitName_CubicInch"), GetLocalizedStringName(L"UnitAbbreviation_CubicInch"), 12 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_CubicMeter, GetLocalizedStringName(L"UnitName_CubicMeter"), GetLocalizedStringName(L"UnitAbbreviation_CubicMeter"), 4 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_CubicYard, GetLocalizedStringName(L"UnitName_CubicYard"), GetLocalizedStringName(L"UnitAbbreviation_CubicYard"), 14 }); + volumeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Volume_CupUS, GetLocalizedStringName(L"UnitName_CupUS"), GetLocalizedStringName(L"UnitAbbreviation_CupUS"), 8 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUK, + GetLocalizedStringName(L"UnitName_FluidOunceUK"), + GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUK"), + 17 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_FluidOunceUS, + GetLocalizedStringName(L"UnitName_FluidOunceUS"), + GetLocalizedStringName(L"UnitAbbreviation_FluidOunceUS"), + 7 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_GallonUK, GetLocalizedStringName(L"UnitName_GallonUK"), GetLocalizedStringName(L"UnitAbbreviation_GallonUK"), 20 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_GallonUS, GetLocalizedStringName(L"UnitName_GallonUS"), GetLocalizedStringName(L"UnitAbbreviation_GallonUS"), 11 }); + volumeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Volume_Liter, GetLocalizedStringName(L"UnitName_Liter"), GetLocalizedStringName(L"UnitAbbreviation_Liter"), 3 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Milliliter, + GetLocalizedStringName(L"UnitName_Milliliter"), + GetLocalizedStringName(L"UnitAbbreviation_Milliliter"), + 1, + useUSCustomary, + useSI }); + volumeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Volume_PintUK, GetLocalizedStringName(L"UnitName_PintUK"), GetLocalizedStringName(L"UnitAbbreviation_PintUK"), 18 }); + volumeUnits.push_back( + OrderedUnit{ UnitConverterUnits::Volume_PintUS, GetLocalizedStringName(L"UnitName_PintUS"), GetLocalizedStringName(L"UnitAbbreviation_PintUS"), 9 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUS, + GetLocalizedStringName(L"UnitName_TablespoonUS"), + GetLocalizedStringName(L"UnitAbbreviation_TablespoonUS"), + 6 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUS, + GetLocalizedStringName(L"UnitName_TeaspoonUS"), + GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUS"), + 5, + useSI, + useUSCustomary && m_currentRegionCode != "GB" }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_QuartUK, GetLocalizedStringName(L"UnitName_QuartUK"), GetLocalizedStringName(L"UnitAbbreviation_QuartUK"), 19 }); + volumeUnits.push_back(OrderedUnit{ + UnitConverterUnits::Volume_QuartUS, GetLocalizedStringName(L"UnitName_QuartUS"), GetLocalizedStringName(L"UnitAbbreviation_QuartUS"), 10 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TeaspoonUK, + GetLocalizedStringName(L"UnitName_TeaspoonUK"), + GetLocalizedStringName(L"UnitAbbreviation_TeaspoonUK"), + 15, + false, + useUSCustomary && m_currentRegionCode == "GB" }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_TablespoonUK, + GetLocalizedStringName(L"UnitName_TablespoonUK"), + GetLocalizedStringName(L"UnitAbbreviation_TablespoonUK"), + 16 }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_CoffeeCup, + GetLocalizedStringName(L"UnitName_CoffeeCup"), + GetLocalizedStringName(L"UnitAbbreviation_CoffeeCup"), + 22, + false, + false, + true }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_Bathtub, + GetLocalizedStringName(L"UnitName_Bathtub"), + GetLocalizedStringName(L"UnitAbbreviation_Bathtub"), + 23, + false, + false, + true }); + volumeUnits.push_back(OrderedUnit{ UnitConverterUnits::Volume_SwimmingPool, + GetLocalizedStringName(L"UnitName_SwimmingPool"), + GetLocalizedStringName(L"UnitAbbreviation_SwimmingPool"), + 24, + false, + false, + true }); unitMap.emplace(ViewMode::Volume, volumeUnits); vector weightUnits; - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Carat, GetLocalizedStringName(L"UnitName_Carat"), GetLocalizedStringName(L"UnitAbbreviation_Carat"), 1 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Centigram, GetLocalizedStringName(L"UnitName_Centigram"), GetLocalizedStringName(L"UnitAbbreviation_Centigram"), 3 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Decigram, GetLocalizedStringName(L"UnitName_Decigram"), GetLocalizedStringName(L"UnitAbbreviation_Decigram"), 4 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Decagram, GetLocalizedStringName(L"UnitName_Decagram"), GetLocalizedStringName(L"UnitAbbreviation_Decagram"), 6 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Gram, GetLocalizedStringName(L"UnitName_Gram"), GetLocalizedStringName(L"UnitAbbreviation_Gram"), 5 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Hectogram, GetLocalizedStringName(L"UnitName_Hectogram"), GetLocalizedStringName(L"UnitAbbreviation_Hectogram"), 7 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Kilogram, GetLocalizedStringName(L"UnitName_Kilogram"), GetLocalizedStringName(L"UnitAbbreviation_Kilogram"), 8 , useUSCustomary, useSI}); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_LongTon, GetLocalizedStringName(L"UnitName_LongTon"), GetLocalizedStringName(L"UnitAbbreviation_LongTon"), 14 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Milligram, GetLocalizedStringName(L"UnitName_Milligram"), GetLocalizedStringName(L"UnitAbbreviation_Milligram"), 2 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Ounce, GetLocalizedStringName(L"UnitName_Ounce"), GetLocalizedStringName(L"UnitAbbreviation_Ounce"), 10 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Pound, GetLocalizedStringName(L"UnitName_Pound"), GetLocalizedStringName(L"UnitAbbreviation_Pound"), 11 , useSI, useUSCustomary }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_ShortTon, GetLocalizedStringName(L"UnitName_ShortTon"), GetLocalizedStringName(L"UnitAbbreviation_ShortTon"), 13 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Stone, GetLocalizedStringName(L"UnitName_Stone"), GetLocalizedStringName(L"UnitAbbreviation_Stone"), 12 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Tonne, GetLocalizedStringName(L"UnitName_Tonne"), GetLocalizedStringName(L"UnitAbbreviation_Tonne"), 9 }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Snowflake, GetLocalizedStringName(L"UnitName_Snowflake"), GetLocalizedStringName(L"UnitAbbreviation_Snowflake"), 15 ,false, false, true }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_SoccerBall, GetLocalizedStringName(L"UnitName_SoccerBall"), GetLocalizedStringName(L"UnitAbbreviation_SoccerBall"), 16 , false, false, true }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Elephant, GetLocalizedStringName(L"UnitName_Elephant"), GetLocalizedStringName(L"UnitAbbreviation_Elephant"), 17 ,false, false, true }); - weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Whale, GetLocalizedStringName(L"UnitName_Whale"), GetLocalizedStringName(L"UnitAbbreviation_Whale"), 18 ,false, false, true }); + weightUnits.push_back( + OrderedUnit{ UnitConverterUnits::Weight_Carat, GetLocalizedStringName(L"UnitName_Carat"), GetLocalizedStringName(L"UnitAbbreviation_Carat"), 1 }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_Centigram, GetLocalizedStringName(L"UnitName_Centigram"), GetLocalizedStringName(L"UnitAbbreviation_Centigram"), 3 }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_Decigram, GetLocalizedStringName(L"UnitName_Decigram"), GetLocalizedStringName(L"UnitAbbreviation_Decigram"), 4 }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_Decagram, GetLocalizedStringName(L"UnitName_Decagram"), GetLocalizedStringName(L"UnitAbbreviation_Decagram"), 6 }); + weightUnits.push_back( + OrderedUnit{ UnitConverterUnits::Weight_Gram, GetLocalizedStringName(L"UnitName_Gram"), GetLocalizedStringName(L"UnitAbbreviation_Gram"), 5 }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_Hectogram, GetLocalizedStringName(L"UnitName_Hectogram"), GetLocalizedStringName(L"UnitAbbreviation_Hectogram"), 7 }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Kilogram, + GetLocalizedStringName(L"UnitName_Kilogram"), + GetLocalizedStringName(L"UnitAbbreviation_Kilogram"), + 8, + useUSCustomary, + useSI }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_LongTon, GetLocalizedStringName(L"UnitName_LongTon"), GetLocalizedStringName(L"UnitAbbreviation_LongTon"), 14 }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_Milligram, GetLocalizedStringName(L"UnitName_Milligram"), GetLocalizedStringName(L"UnitAbbreviation_Milligram"), 2 }); + weightUnits.push_back( + OrderedUnit{ UnitConverterUnits::Weight_Ounce, GetLocalizedStringName(L"UnitName_Ounce"), GetLocalizedStringName(L"UnitAbbreviation_Ounce"), 10 }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Pound, + GetLocalizedStringName(L"UnitName_Pound"), + GetLocalizedStringName(L"UnitAbbreviation_Pound"), + 11, + useSI, + useUSCustomary }); + weightUnits.push_back(OrderedUnit{ + UnitConverterUnits::Weight_ShortTon, GetLocalizedStringName(L"UnitName_ShortTon"), GetLocalizedStringName(L"UnitAbbreviation_ShortTon"), 13 }); + weightUnits.push_back( + OrderedUnit{ UnitConverterUnits::Weight_Stone, GetLocalizedStringName(L"UnitName_Stone"), GetLocalizedStringName(L"UnitAbbreviation_Stone"), 12 }); + weightUnits.push_back( + OrderedUnit{ UnitConverterUnits::Weight_Tonne, GetLocalizedStringName(L"UnitName_Tonne"), GetLocalizedStringName(L"UnitAbbreviation_Tonne"), 9 }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Snowflake, + GetLocalizedStringName(L"UnitName_Snowflake"), + GetLocalizedStringName(L"UnitAbbreviation_Snowflake"), + 15, + false, + false, + true }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_SoccerBall, + GetLocalizedStringName(L"UnitName_SoccerBall"), + GetLocalizedStringName(L"UnitAbbreviation_SoccerBall"), + 16, + false, + false, + true }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Elephant, + GetLocalizedStringName(L"UnitName_Elephant"), + GetLocalizedStringName(L"UnitAbbreviation_Elephant"), + 17, + false, + false, + true }); + weightUnits.push_back(OrderedUnit{ UnitConverterUnits::Weight_Whale, + GetLocalizedStringName(L"UnitName_Whale"), + GetLocalizedStringName(L"UnitAbbreviation_Whale"), + 18, + false, + false, + true }); unitMap.emplace(ViewMode::Weight, weightUnits); vector pressureUnits; - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Atmosphere, GetLocalizedStringName(L"UnitName_Atmosphere"), GetLocalizedStringName(L"UnitAbbreviation_Atmosphere"), 1 , true, false, false }); - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Bar, GetLocalizedStringName(L"UnitName_Bar"), GetLocalizedStringName(L"UnitAbbreviation_Bar"), 2, false, true, false}); - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_KiloPascal, GetLocalizedStringName(L"UnitName_KiloPascal"), GetLocalizedStringName(L"UnitAbbreviation_KiloPascal"), 3 }); - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_MillimeterOfMercury, GetLocalizedStringName(L"UnitName_MillimeterOfMercury "), GetLocalizedStringName(L"UnitAbbreviation_MillimeterOfMercury "), 4 }); - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Pascal, GetLocalizedStringName(L"UnitName_Pascal"), GetLocalizedStringName(L"UnitAbbreviation_Pascal"), 5 }); - pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_PSI, GetLocalizedStringName(L"UnitName_PSI"), GetLocalizedStringName(L"UnitAbbreviation_PSI"), 6, false, false, false }); + pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_Atmosphere, + GetLocalizedStringName(L"UnitName_Atmosphere"), + GetLocalizedStringName(L"UnitAbbreviation_Atmosphere"), + 1, + true, + false, + false }); + pressureUnits.push_back(OrderedUnit{ + UnitConverterUnits::Pressure_Bar, GetLocalizedStringName(L"UnitName_Bar"), GetLocalizedStringName(L"UnitAbbreviation_Bar"), 2, false, true, false }); + pressureUnits.push_back(OrderedUnit{ + UnitConverterUnits::Pressure_KiloPascal, GetLocalizedStringName(L"UnitName_KiloPascal"), GetLocalizedStringName(L"UnitAbbreviation_KiloPascal"), 3 }); + pressureUnits.push_back(OrderedUnit{ UnitConverterUnits::Pressure_MillimeterOfMercury, + GetLocalizedStringName(L"UnitName_MillimeterOfMercury "), + GetLocalizedStringName(L"UnitAbbreviation_MillimeterOfMercury "), + 4 }); + pressureUnits.push_back( + OrderedUnit{ UnitConverterUnits::Pressure_Pascal, GetLocalizedStringName(L"UnitName_Pascal"), GetLocalizedStringName(L"UnitAbbreviation_Pascal"), 5 }); + pressureUnits.push_back(OrderedUnit{ + UnitConverterUnits::Pressure_PSI, GetLocalizedStringName(L"UnitName_PSI"), GetLocalizedStringName(L"UnitAbbreviation_PSI"), 6, false, false, false }); unitMap.emplace(ViewMode::Pressure, pressureUnits); vector angleUnits; - angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Degree, GetLocalizedStringName(L"UnitName_Degree"), GetLocalizedStringName(L"UnitAbbreviation_Degree"), 1, true, false, false }); - angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Radian, GetLocalizedStringName(L"UnitName_Radian"), GetLocalizedStringName(L"UnitAbbreviation_Radian"), 2, false, true, false }); - angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Gradian, GetLocalizedStringName(L"UnitName_Gradian"), GetLocalizedStringName(L"UnitAbbreviation_Gradian"), 3}); + angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Degree, + GetLocalizedStringName(L"UnitName_Degree"), + GetLocalizedStringName(L"UnitAbbreviation_Degree"), + 1, + true, + false, + false }); + angleUnits.push_back(OrderedUnit{ UnitConverterUnits::Angle_Radian, + GetLocalizedStringName(L"UnitName_Radian"), + GetLocalizedStringName(L"UnitAbbreviation_Radian"), + 2, + false, + true, + false }); + angleUnits.push_back( + OrderedUnit{ UnitConverterUnits::Angle_Gradian, GetLocalizedStringName(L"UnitName_Gradian"), GetLocalizedStringName(L"UnitAbbreviation_Gradian"), 3 }); unitMap.emplace(ViewMode::Angle, angleUnits); } void UnitConverterDataLoader::GetConversionData(_In_ unordered_map>& categoryToUnitConversionMap) { /*categoryId, UnitId, factor*/ - static const vector unitDataList = { - { ViewMode::Area, UnitConverterUnits::Area_Acre, 4046.8564224 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareMeter, 1 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareFoot, 0.09290304 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareYard, 0.83612736 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareMillimeter, 0.000001 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareCentimeter, 0.0001 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareInch, 0.00064516 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareMile, 2589988.110336 }, - { ViewMode::Area, UnitConverterUnits::Area_SquareKilometer, 1000000 }, - { ViewMode::Area, UnitConverterUnits::Area_Hectare, 10000 }, - { ViewMode::Area, UnitConverterUnits::Area_Hand, 0.012516104 }, - { ViewMode::Area, UnitConverterUnits::Area_Paper, 0.06032246 }, - { ViewMode::Area, UnitConverterUnits::Area_SoccerField, 10869.66 }, - { ViewMode::Area, UnitConverterUnits::Area_Castle, 100000 }, + static const vector unitDataList = { { ViewMode::Area, UnitConverterUnits::Area_Acre, 4046.8564224 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareMeter, 1 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareFoot, 0.09290304 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareYard, 0.83612736 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareMillimeter, 0.000001 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareCentimeter, 0.0001 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareInch, 0.00064516 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareMile, 2589988.110336 }, + { ViewMode::Area, UnitConverterUnits::Area_SquareKilometer, 1000000 }, + { ViewMode::Area, UnitConverterUnits::Area_Hectare, 10000 }, + { ViewMode::Area, UnitConverterUnits::Area_Hand, 0.012516104 }, + { ViewMode::Area, UnitConverterUnits::Area_Paper, 0.06032246 }, + { ViewMode::Area, UnitConverterUnits::Area_SoccerField, 10869.66 }, + { ViewMode::Area, UnitConverterUnits::Area_Castle, 100000 }, + { ViewMode::Area, UnitConverterUnits::Area_Pyeong, 400.0 / 121.0 }, - { ViewMode::Data, UnitConverterUnits::Data_Bit, 0.000000125 }, - { ViewMode::Data, UnitConverterUnits::Data_Byte, 0.000001 }, - { ViewMode::Data, UnitConverterUnits::Data_Kilobyte, 0.001 }, - { ViewMode::Data, UnitConverterUnits::Data_Megabyte, 1 }, - { ViewMode::Data, UnitConverterUnits::Data_Gigabyte, 1000 }, - { ViewMode::Data, UnitConverterUnits::Data_Terabyte, 1000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Petabyte, 1000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Exabytes, 1000000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Zetabytes, 1000000000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Yottabyte, 1000000000000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Kilobit, 0.000125 }, - { ViewMode::Data, UnitConverterUnits::Data_Megabit, 0.125 }, - { ViewMode::Data, UnitConverterUnits::Data_Gigabit, 125 }, - { ViewMode::Data, UnitConverterUnits::Data_Terabit, 125000 }, - { ViewMode::Data, UnitConverterUnits::Data_Petabit, 125000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Exabits, 125000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Zetabits, 125000000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Yottabit, 125000000000000000 }, - { ViewMode::Data, UnitConverterUnits::Data_Gibibits, 134.217728 }, - { ViewMode::Data, UnitConverterUnits::Data_Gibibytes, 1073.741824 }, - { ViewMode::Data, UnitConverterUnits::Data_Kibibits, 0.000128 }, - { ViewMode::Data, UnitConverterUnits::Data_Kibibytes, 0.001024 }, - { ViewMode::Data, UnitConverterUnits::Data_Mebibits, 0.131072 }, - { ViewMode::Data, UnitConverterUnits::Data_Mebibytes, 1.048576 }, - { ViewMode::Data, UnitConverterUnits::Data_Pebibits, 140737488.355328 }, - { ViewMode::Data, UnitConverterUnits::Data_Pebibytes, 1125899906.842624 }, - { ViewMode::Data, UnitConverterUnits::Data_Tebibits, 137438.953472 }, - { ViewMode::Data, UnitConverterUnits::Data_Tebibytes, 1099511.627776 }, - { ViewMode::Data, UnitConverterUnits::Data_Exbibits, 144115188075.855872 }, - { ViewMode::Data, UnitConverterUnits::Data_Exbibytes, 1152921504606.846976 }, - { ViewMode::Data, UnitConverterUnits::Data_Zebibits, 147573952589676.412928 }, - { ViewMode::Data, UnitConverterUnits::Data_Zebibytes, 1180591620717411.303424 }, - { ViewMode::Data, UnitConverterUnits::Data_Yobibits, 151115727451828646.838272 }, - { ViewMode::Data, UnitConverterUnits::Data_Yobibytes, 1208925819614629174.706176 }, - { ViewMode::Data, UnitConverterUnits::Data_FloppyDisk, 1.509949 }, - { ViewMode::Data, UnitConverterUnits::Data_CD, 734.003200 }, - { ViewMode::Data, UnitConverterUnits::Data_DVD, 5046.586573 }, + { ViewMode::Data, UnitConverterUnits::Data_Bit, 0.000000125 }, + { ViewMode::Data, UnitConverterUnits::Data_Byte, 0.000001 }, + { ViewMode::Data, UnitConverterUnits::Data_Kilobyte, 0.001 }, + { ViewMode::Data, UnitConverterUnits::Data_Megabyte, 1 }, + { ViewMode::Data, UnitConverterUnits::Data_Gigabyte, 1000 }, + { ViewMode::Data, UnitConverterUnits::Data_Terabyte, 1000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Petabyte, 1000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Exabytes, 1000000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Zetabytes, 1000000000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Yottabyte, 1000000000000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Kilobit, 0.000125 }, + { ViewMode::Data, UnitConverterUnits::Data_Megabit, 0.125 }, + { ViewMode::Data, UnitConverterUnits::Data_Gigabit, 125 }, + { ViewMode::Data, UnitConverterUnits::Data_Terabit, 125000 }, + { ViewMode::Data, UnitConverterUnits::Data_Petabit, 125000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Exabits, 125000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Zetabits, 125000000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Yottabit, 125000000000000000 }, + { ViewMode::Data, UnitConverterUnits::Data_Gibibits, 134.217728 }, + { ViewMode::Data, UnitConverterUnits::Data_Gibibytes, 1073.741824 }, + { ViewMode::Data, UnitConverterUnits::Data_Kibibits, 0.000128 }, + { ViewMode::Data, UnitConverterUnits::Data_Kibibytes, 0.001024 }, + { ViewMode::Data, UnitConverterUnits::Data_Mebibits, 0.131072 }, + { ViewMode::Data, UnitConverterUnits::Data_Mebibytes, 1.048576 }, + { ViewMode::Data, UnitConverterUnits::Data_Pebibits, 140737488.355328 }, + { ViewMode::Data, UnitConverterUnits::Data_Pebibytes, 1125899906.842624 }, + { ViewMode::Data, UnitConverterUnits::Data_Tebibits, 137438.953472 }, + { ViewMode::Data, UnitConverterUnits::Data_Tebibytes, 1099511.627776 }, + { ViewMode::Data, UnitConverterUnits::Data_Exbibits, 144115188075.855872 }, + { ViewMode::Data, UnitConverterUnits::Data_Exbibytes, 1152921504606.846976 }, + { ViewMode::Data, UnitConverterUnits::Data_Zebibits, 147573952589676.412928 }, + { ViewMode::Data, UnitConverterUnits::Data_Zebibytes, 1180591620717411.303424 }, + { ViewMode::Data, UnitConverterUnits::Data_Yobibits, 151115727451828646.838272 }, + { ViewMode::Data, UnitConverterUnits::Data_Yobibytes, 1208925819614629174.706176 }, + { ViewMode::Data, UnitConverterUnits::Data_FloppyDisk, 1.509949 }, + { ViewMode::Data, UnitConverterUnits::Data_CD, 734.003200 }, + { ViewMode::Data, UnitConverterUnits::Data_DVD, 5046.586573 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Calorie, 4.184 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Kilocalorie, 4184}, - { ViewMode::Energy, UnitConverterUnits::Energy_BritishThermalUnit, 1055.056 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Kilojoule, 1000 }, - { ViewMode::Energy, UnitConverterUnits::Energy_ElectronVolt, 0.0000000000000000001602176565 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Joule, 1 }, - { ViewMode::Energy, UnitConverterUnits::Energy_FootPound, 1.3558179483314 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Battery, 9000 }, - { ViewMode::Energy, UnitConverterUnits::Energy_Banana, 439614 }, - { ViewMode::Energy, UnitConverterUnits::Energy_SliceOfCake, 1046700 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Calorie, 4.184 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Kilocalorie, 4184 }, + { ViewMode::Energy, UnitConverterUnits::Energy_BritishThermalUnit, 1055.056 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Kilojoule, 1000 }, + { ViewMode::Energy, UnitConverterUnits::Energy_ElectronVolt, 0.0000000000000000001602176565 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Joule, 1 }, + { ViewMode::Energy, UnitConverterUnits::Energy_FootPound, 1.3558179483314 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Battery, 9000 }, + { ViewMode::Energy, UnitConverterUnits::Energy_Banana, 439614 }, + { ViewMode::Energy, UnitConverterUnits::Energy_SliceOfCake, 1046700 }, - { ViewMode::Length, UnitConverterUnits::Length_Inch, 0.0254 }, - { ViewMode::Length, UnitConverterUnits::Length_Foot, 0.3048 }, - { ViewMode::Length, UnitConverterUnits::Length_Yard, 0.9144 }, - { ViewMode::Length, UnitConverterUnits::Length_Mile, 1609.344 }, - { ViewMode::Length, UnitConverterUnits::Length_Micron, 0.000001 }, - { ViewMode::Length, UnitConverterUnits::Length_Millimeter, 0.001 }, - { ViewMode::Length, UnitConverterUnits::Length_Nanometer, 0.000000001 }, - { ViewMode::Length, UnitConverterUnits::Length_Centimeter, 0.01 }, - { ViewMode::Length, UnitConverterUnits::Length_Meter, 1 }, - { ViewMode::Length, UnitConverterUnits::Length_Kilometer, 1000 }, - { ViewMode::Length, UnitConverterUnits::Length_NauticalMile, 1852 }, - { ViewMode::Length, UnitConverterUnits::Length_Paperclip, 0.035052 }, - { ViewMode::Length, UnitConverterUnits::Length_Hand, 0.18669 }, - { ViewMode::Length, UnitConverterUnits::Length_JumboJet, 76 }, + { ViewMode::Length, UnitConverterUnits::Length_Inch, 0.0254 }, + { ViewMode::Length, UnitConverterUnits::Length_Foot, 0.3048 }, + { ViewMode::Length, UnitConverterUnits::Length_Yard, 0.9144 }, + { ViewMode::Length, UnitConverterUnits::Length_Mile, 1609.344 }, + { ViewMode::Length, UnitConverterUnits::Length_Micron, 0.000001 }, + { ViewMode::Length, UnitConverterUnits::Length_Millimeter, 0.001 }, + { ViewMode::Length, UnitConverterUnits::Length_Nanometer, 0.000000001 }, + { ViewMode::Length, UnitConverterUnits::Length_Centimeter, 0.01 }, + { ViewMode::Length, UnitConverterUnits::Length_Meter, 1 }, + { ViewMode::Length, UnitConverterUnits::Length_Kilometer, 1000 }, + { ViewMode::Length, UnitConverterUnits::Length_NauticalMile, 1852 }, + { ViewMode::Length, UnitConverterUnits::Length_Paperclip, 0.035052 }, + { ViewMode::Length, UnitConverterUnits::Length_Hand, 0.18669 }, + { ViewMode::Length, UnitConverterUnits::Length_JumboJet, 76 }, - { ViewMode::Power, UnitConverterUnits::Power_BritishThermalUnitPerMinute, 17.58426666666667 }, - { ViewMode::Power, UnitConverterUnits::Power_FootPoundPerMinute, 0.0225969658055233 }, - { ViewMode::Power, UnitConverterUnits::Power_Watt, 1 }, - { ViewMode::Power, UnitConverterUnits::Power_Kilowatt, 1000 }, - { ViewMode::Power, UnitConverterUnits::Power_Horsepower, 745.69987158227022 }, - { ViewMode::Power, UnitConverterUnits::Power_LightBulb, 60 }, - { ViewMode::Power, UnitConverterUnits::Power_Horse, 745.7 }, - { ViewMode::Power, UnitConverterUnits::Power_TrainEngine, 2982799.486329081 }, + { ViewMode::Power, UnitConverterUnits::Power_BritishThermalUnitPerMinute, 17.58426666666667 }, + { ViewMode::Power, UnitConverterUnits::Power_FootPoundPerMinute, 0.0225969658055233 }, + { ViewMode::Power, UnitConverterUnits::Power_Watt, 1 }, + { ViewMode::Power, UnitConverterUnits::Power_Kilowatt, 1000 }, + { ViewMode::Power, UnitConverterUnits::Power_Horsepower, 745.69987158227022 }, + { ViewMode::Power, UnitConverterUnits::Power_LightBulb, 60 }, + { ViewMode::Power, UnitConverterUnits::Power_Horse, 745.7 }, + { ViewMode::Power, UnitConverterUnits::Power_TrainEngine, 2982799.486329081 }, - { ViewMode::Time, UnitConverterUnits::Time_Day, 86400 }, - { ViewMode::Time, UnitConverterUnits::Time_Second, 1 }, - { ViewMode::Time, UnitConverterUnits::Time_Week, 604800 }, - { ViewMode::Time, UnitConverterUnits::Time_Year, 31557600 }, - { ViewMode::Time, UnitConverterUnits::Time_Millisecond, 0.001 }, - { ViewMode::Time, UnitConverterUnits::Time_Microsecond, 0.000001 }, - { ViewMode::Time, UnitConverterUnits::Time_Minute, 60 }, - { ViewMode::Time, UnitConverterUnits::Time_Hour, 3600 }, + { ViewMode::Time, UnitConverterUnits::Time_Day, 86400 }, + { ViewMode::Time, UnitConverterUnits::Time_Second, 1 }, + { ViewMode::Time, UnitConverterUnits::Time_Week, 604800 }, + { ViewMode::Time, UnitConverterUnits::Time_Year, 31557600 }, + { ViewMode::Time, UnitConverterUnits::Time_Millisecond, 0.001 }, + { ViewMode::Time, UnitConverterUnits::Time_Microsecond, 0.000001 }, + { ViewMode::Time, UnitConverterUnits::Time_Minute, 60 }, + { ViewMode::Time, UnitConverterUnits::Time_Hour, 3600 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CupUS, 236.588237 }, - { ViewMode::Volume, UnitConverterUnits::Volume_PintUS, 473.176473 }, - { ViewMode::Volume, UnitConverterUnits::Volume_PintUK, 568.26125 }, - { ViewMode::Volume, UnitConverterUnits::Volume_QuartUS, 946.352946 }, - { ViewMode::Volume, UnitConverterUnits::Volume_QuartUK, 1136.5225 }, - { ViewMode::Volume, UnitConverterUnits::Volume_GallonUS, 3785.411784 }, - { ViewMode::Volume, UnitConverterUnits::Volume_GallonUK, 4546.09 }, - { ViewMode::Volume, UnitConverterUnits::Volume_Liter, 1000 }, - { ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUS, 4.92892159375 }, - { ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUS, 14.78676478125 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CubicCentimeter, 1 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CubicYard, 764554.857984 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CubicMeter, 1000000 }, - { ViewMode::Volume, UnitConverterUnits::Volume_Milliliter, 1 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CubicInch, 16.387064 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CubicFoot, 28316.846592 }, - { ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUS, 29.5735295625 }, - { ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUK, 28.4130625 }, - { ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUK, 5.91938802083333333333 }, - { ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUK, 17.7581640625 }, - { ViewMode::Volume, UnitConverterUnits::Volume_CoffeeCup, 236.5882 }, - { ViewMode::Volume, UnitConverterUnits::Volume_Bathtub, 378541.2 }, - { ViewMode::Volume, UnitConverterUnits::Volume_SwimmingPool, 3750000000 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CupUS, 236.588237 }, + { ViewMode::Volume, UnitConverterUnits::Volume_PintUS, 473.176473 }, + { ViewMode::Volume, UnitConverterUnits::Volume_PintUK, 568.26125 }, + { ViewMode::Volume, UnitConverterUnits::Volume_QuartUS, 946.352946 }, + { ViewMode::Volume, UnitConverterUnits::Volume_QuartUK, 1136.5225 }, + { ViewMode::Volume, UnitConverterUnits::Volume_GallonUS, 3785.411784 }, + { ViewMode::Volume, UnitConverterUnits::Volume_GallonUK, 4546.09 }, + { ViewMode::Volume, UnitConverterUnits::Volume_Liter, 1000 }, + { ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUS, 4.92892159375 }, + { ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUS, 14.78676478125 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CubicCentimeter, 1 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CubicYard, 764554.857984 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CubicMeter, 1000000 }, + { ViewMode::Volume, UnitConverterUnits::Volume_Milliliter, 1 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CubicInch, 16.387064 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CubicFoot, 28316.846592 }, + { ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUS, 29.5735295625 }, + { ViewMode::Volume, UnitConverterUnits::Volume_FluidOunceUK, 28.4130625 }, + { ViewMode::Volume, UnitConverterUnits::Volume_TeaspoonUK, 5.91938802083333333333 }, + { ViewMode::Volume, UnitConverterUnits::Volume_TablespoonUK, 17.7581640625 }, + { ViewMode::Volume, UnitConverterUnits::Volume_CoffeeCup, 236.5882 }, + { ViewMode::Volume, UnitConverterUnits::Volume_Bathtub, 378541.2 }, + { ViewMode::Volume, UnitConverterUnits::Volume_SwimmingPool, 3750000000 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Kilogram, 1 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Hectogram, 0.1 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Decagram, 0.01 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Gram, 0.001 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Pound, 0.45359237 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Ounce, 0.028349523125 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Milligram, 0.000001 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Centigram, 0.00001 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Decigram, 0.0001 }, - { ViewMode::Weight, UnitConverterUnits::Weight_LongTon, 1016.0469088 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Tonne, 1000 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Stone, 6.35029318 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Carat, 0.0002 }, - { ViewMode::Weight, UnitConverterUnits::Weight_ShortTon, 907.18474 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Snowflake, 0.000002 }, - { ViewMode::Weight, UnitConverterUnits::Weight_SoccerBall, 0.4325 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Elephant, 4000 }, - { ViewMode::Weight, UnitConverterUnits::Weight_Whale, 90000 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Kilogram, 1 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Hectogram, 0.1 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Decagram, 0.01 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Gram, 0.001 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Pound, 0.45359237 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Ounce, 0.028349523125 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Milligram, 0.000001 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Centigram, 0.00001 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Decigram, 0.0001 }, + { ViewMode::Weight, UnitConverterUnits::Weight_LongTon, 1016.0469088 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Tonne, 1000 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Stone, 6.35029318 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Carat, 0.0002 }, + { ViewMode::Weight, UnitConverterUnits::Weight_ShortTon, 907.18474 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Snowflake, 0.000002 }, + { ViewMode::Weight, UnitConverterUnits::Weight_SoccerBall, 0.4325 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Elephant, 4000 }, + { ViewMode::Weight, UnitConverterUnits::Weight_Whale, 90000 }, - { ViewMode::Speed, UnitConverterUnits::Speed_CentimetersPerSecond, 1 }, - { ViewMode::Speed, UnitConverterUnits::Speed_FeetPerSecond, 30.48 }, - { ViewMode::Speed, UnitConverterUnits::Speed_KilometersPerHour, 27.777777777777777777778 }, - { ViewMode::Speed, UnitConverterUnits::Speed_Knot, 51.44 }, - { ViewMode::Speed, UnitConverterUnits::Speed_Mach, 34030 }, - { ViewMode::Speed, UnitConverterUnits::Speed_MetersPerSecond, 100 }, - { ViewMode::Speed, UnitConverterUnits::Speed_MilesPerHour, 44.7 }, - { ViewMode::Speed, UnitConverterUnits::Speed_Turtle, 8.94 }, - { ViewMode::Speed, UnitConverterUnits::Speed_Horse, 2011.5 }, - { ViewMode::Speed, UnitConverterUnits::Speed_Jet, 24585 }, + { ViewMode::Speed, UnitConverterUnits::Speed_CentimetersPerSecond, 1 }, + { ViewMode::Speed, UnitConverterUnits::Speed_FeetPerSecond, 30.48 }, + { ViewMode::Speed, UnitConverterUnits::Speed_KilometersPerHour, 27.777777777777777777778 }, + { ViewMode::Speed, UnitConverterUnits::Speed_Knot, 51.44 }, + { ViewMode::Speed, UnitConverterUnits::Speed_Mach, 34030 }, + { ViewMode::Speed, UnitConverterUnits::Speed_MetersPerSecond, 100 }, + { ViewMode::Speed, UnitConverterUnits::Speed_MilesPerHour, 44.7 }, + { ViewMode::Speed, UnitConverterUnits::Speed_Turtle, 8.94 }, + { ViewMode::Speed, UnitConverterUnits::Speed_Horse, 2011.5 }, + { ViewMode::Speed, UnitConverterUnits::Speed_Jet, 24585 }, - { ViewMode::Angle, UnitConverterUnits::Angle_Degree, 1 }, - { ViewMode::Angle, UnitConverterUnits::Angle_Radian, 57.29577951308233 }, - { ViewMode::Angle, UnitConverterUnits::Angle_Gradian, 0.9 }, + { ViewMode::Angle, UnitConverterUnits::Angle_Degree, 1 }, + { ViewMode::Angle, UnitConverterUnits::Angle_Radian, 57.29577951308233 }, + { ViewMode::Angle, UnitConverterUnits::Angle_Gradian, 0.9 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_Atmosphere, 1 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_Bar, 0.9869232667160128 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_KiloPascal, 0.0098692326671601 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_MillimeterOfMercury, 0.0013155687145324 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_Pascal, 9.869232667160128e-6 }, - { ViewMode::Pressure, UnitConverterUnits::Pressure_PSI, 0.068045961016531 } - }; + { ViewMode::Pressure, UnitConverterUnits::Pressure_Atmosphere, 1 }, + { ViewMode::Pressure, UnitConverterUnits::Pressure_Bar, 0.9869232667160128 }, + { ViewMode::Pressure, UnitConverterUnits::Pressure_KiloPascal, 0.0098692326671601 }, + { ViewMode::Pressure, UnitConverterUnits::Pressure_MillimeterOfMercury, 0.0013155687145324 }, + { ViewMode::Pressure, UnitConverterUnits::Pressure_Pascal, 9.869232667160128e-6 }, + { ViewMode::Pressure, UnitConverterUnits::Pressure_PSI, 0.068045961016531 } }; // Populate the hash map and return; for (UnitData unitdata : unitDataList) @@ -547,40 +949,55 @@ void UnitConverterDataLoader::GetConversionData(_In_ unordered_map(unitdata.unitId, unitdata.factor)); } } - } +} - wstring UnitConverterDataLoader::GetLocalizedStringName(String^ stringId) - { - return AppResourceProvider::GetInstance().GetResourceString(stringId)->Data(); - } +wstring UnitConverterDataLoader::GetLocalizedStringName(String ^ stringId) +{ + return AppResourceProvider::GetInstance()->GetResourceString(stringId)->Data(); +} - void UnitConverterDataLoader::GetExplicitConversionData(_In_ unordered_map>& unitToUnitConversionList) - { - /* categoryId, ParentUnitId, UnitId, ratio, offset, offsetfirst*/ - ExplicitUnitConversionData conversionDataList[] = { - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesCelsius , 1, 0 }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, 32 }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_Kelvin, 1, 273.15 }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_DegreesCelsius, 0.55555555555555555555555555555556, -32, CONVERT_WITH_OFFSET_FIRST }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_DegreesFahrenheit, 1, 0 }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_Kelvin, 0.55555555555555555555555555555556, 459.67, CONVERT_WITH_OFFSET_FIRST }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_DegreesCelsius, 1, -273.15, CONVERT_WITH_OFFSET_FIRST }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, -459.67 }, - { ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_Kelvin, 1, 0 } - }; +void UnitConverterDataLoader::GetExplicitConversionData(_In_ unordered_map>& unitToUnitConversionList) +{ + /* categoryId, ParentUnitId, UnitId, ratio, offset, offsetfirst*/ + ExplicitUnitConversionData conversionDataList[] = { + { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesCelsius, 1, 0 }, + { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, 32 }, + { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesCelsius, UnitConverterUnits::Temperature_Kelvin, 1, 273.15 }, + { ViewMode::Temperature, + UnitConverterUnits::Temperature_DegreesFahrenheit, + UnitConverterUnits::Temperature_DegreesCelsius, + 0.55555555555555555555555555555556, + -32, + CONVERT_WITH_OFFSET_FIRST }, + { ViewMode::Temperature, UnitConverterUnits::Temperature_DegreesFahrenheit, UnitConverterUnits::Temperature_DegreesFahrenheit, 1, 0 }, + { ViewMode::Temperature, + UnitConverterUnits::Temperature_DegreesFahrenheit, + UnitConverterUnits::Temperature_Kelvin, + 0.55555555555555555555555555555556, + 459.67, + CONVERT_WITH_OFFSET_FIRST }, + { ViewMode::Temperature, + UnitConverterUnits::Temperature_Kelvin, + UnitConverterUnits::Temperature_DegreesCelsius, + 1, + -273.15, + CONVERT_WITH_OFFSET_FIRST }, + { ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_DegreesFahrenheit, 1.8, -459.67 }, + { ViewMode::Temperature, UnitConverterUnits::Temperature_Kelvin, UnitConverterUnits::Temperature_Kelvin, 1, 0 } + }; - // Populate the hash map and return; - for (ExplicitUnitConversionData data : conversionDataList) - { - if (unitToUnitConversionList.find(data.parentUnitId) == unitToUnitConversionList.end()) - { - unordered_map conversionData; - conversionData.insert(pair(data.unitId, static_cast(data))); - unitToUnitConversionList.insert(pair>(data.parentUnitId, conversionData)); - } - else - { - unitToUnitConversionList.at(data.parentUnitId).insert(pair(data.unitId, static_cast(data))); - } - } - } + // Populate the hash map and return; + for (ExplicitUnitConversionData data : conversionDataList) + { + if (unitToUnitConversionList.find(data.parentUnitId) == unitToUnitConversionList.end()) + { + unordered_map conversionData; + conversionData.insert(pair(data.unitId, static_cast(data))); + unitToUnitConversionList.insert(pair>(data.parentUnitId, conversionData)); + } + else + { + unitToUnitConversionList.at(data.parentUnitId).insert(pair(data.unitId, static_cast(data))); + } + } +} diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h index 519ffda9..0011fdb6 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.h @@ -12,10 +12,20 @@ namespace CalculatorApp { struct OrderedUnit : UnitConversionManager::Unit { - OrderedUnit(){} + OrderedUnit() + { + } - OrderedUnit(int id, std::wstring name, std::wstring abbreviation, int order, bool isConversionSource = false, bool isConversionTarget = false, bool isWhimsical = false) - : UnitConversionManager::Unit(id, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical), order(order) + OrderedUnit( + int id, + std::wstring name, + std::wstring abbreviation, + int order, + bool isConversionSource = false, + bool isConversionTarget = false, + bool isWhimsical = false) + : UnitConversionManager::Unit(id, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical) + , order(order) { } @@ -31,11 +41,21 @@ namespace CalculatorApp struct ExplicitUnitConversionData : UnitConversionManager::ConversionData { - ExplicitUnitConversionData(){} - ExplicitUnitConversionData(CalculatorApp::Common::ViewMode categoryId, int parentUnitId, int unitId, double ratio, double offset, bool offsetFirst = false) : - categoryId(categoryId), parentUnitId(parentUnitId), unitId(unitId), UnitConversionManager::ConversionData(ratio, offset, offsetFirst) + ExplicitUnitConversionData() + { + } + ExplicitUnitConversionData( + CalculatorApp::Common::ViewMode categoryId, + int parentUnitId, + int unitId, + double ratio, + double offset, + bool offsetFirst = false) + : categoryId(categoryId) + , parentUnitId(parentUnitId) + , unitId(unitId) + , UnitConversionManager::ConversionData(ratio, offset, offsetFirst) { - } CalculatorApp::Common::ViewMode categoryId; @@ -43,32 +63,33 @@ namespace CalculatorApp int unitId; }; - class UnitConverterDataLoader : public UnitConversionManager::IConverterDataLoader, - public std::enable_shared_from_this + class UnitConverterDataLoader : public UnitConversionManager::IConverterDataLoader, public std::enable_shared_from_this { public: - UnitConverterDataLoader(Windows::Globalization::GeographicRegion^ region); + UnitConverterDataLoader(Windows::Globalization::GeographicRegion ^ region); private: // IConverterDataLoader void LoadData() override; - std::vector LoadOrderedCategories() override; - std::vector LoadOrderedUnits(const UnitConversionManager::Category& c) override; - std::unordered_map LoadOrderedRatios(const UnitConversionManager::Unit& unit) override; + std::vector GetOrderedCategories() override; + std::vector GetOrderedUnits(const UnitConversionManager::Category& c) override; + std::unordered_map + LoadOrderedRatios(const UnitConversionManager::Unit& unit) override; bool SupportsCategory(const UnitConversionManager::Category& target) override; // IConverterDataLoader void GetCategories(_In_ std::shared_ptr> categoriesList); void GetUnits(_In_ std::unordered_map>& unitMap); void GetConversionData(_In_ std::unordered_map>& categoryToUnitConversionMap); - void GetExplicitConversionData(_In_ std::unordered_map>& unitToUnitConversionList); + void + GetExplicitConversionData(_In_ std::unordered_map>& unitToUnitConversionList); - std::wstring GetLocalizedStringName(_In_ Platform::String^ stringId); + std::wstring GetLocalizedStringName(_In_ Platform::String ^ stringId); std::shared_ptr> m_categoryList; - std::shared_ptr m_categoryToUnits; + std::shared_ptr m_categoryIDToUnitsMap; std::shared_ptr m_ratioMap; - Platform::String^ m_currentRegionCode; + Platform::String ^ m_currentRegionCode; }; } } diff --git a/src/CalcViewModel/DateCalculatorViewModel.cpp b/src/CalcViewModel/DateCalculatorViewModel.cpp index e705c983..a34c1288 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.cpp +++ b/src/CalcViewModel/DateCalculatorViewModel.cpp @@ -22,63 +22,58 @@ using namespace Windows::Globalization; using namespace Windows::Globalization::DateTimeFormatting; using namespace Windows::System::UserProfile; -namespace CalculatorApp::ViewModel::DateCalculatorViewModelProperties +namespace { - StringReference StrDateDiffResult(L"StrDateDiffResult"); - StringReference StrDateDiffResultAutomationName(L"StrDateDiffResultAutomationName"); - StringReference StrDateDiffResultInDays(L"StrDateDiffResultInDays"); - StringReference StrDateResult(L"StrDateResult"); - StringReference StrDateResultAutomationName(L"StrDateResultAutomationName"); - StringReference IsDiffInDays(L"IsDiffInDays"); + StringReference StrDateDiffResultPropertyName(L"StrDateDiffResult"); + StringReference StrDateDiffResultAutomationNamePropertyName(L"StrDateDiffResultAutomationName"); + StringReference StrDateDiffResultInDaysPropertyName(L"StrDateDiffResultInDays"); + StringReference StrDateResultPropertyName(L"StrDateResult"); + StringReference StrDateResultAutomationNamePropertyName(L"StrDateResultAutomationName"); + StringReference IsDiffInDaysPropertyName(L"IsDiffInDays"); } -DateCalculatorViewModel::DateCalculatorViewModel() : - m_IsDateDiffMode(true), - m_IsAddMode(true), - m_isOutOfBound(false), - m_DaysOffset(0), - m_MonthsOffset(0), - m_YearsOffset(0), - m_StrDateDiffResult(L""), - m_StrDateDiffResultAutomationName(L""), - m_StrDateDiffResultInDays(L""), - m_StrDateResult(L""), - m_StrDateResultAutomationName(L""), - m_fromDate({ 0 }), - m_toDate({ 0 }), - m_startDate({ 0 }), - m_dateResult({ 0 }) +DateCalculatorViewModel::DateCalculatorViewModel() + : m_IsDateDiffMode(true) + , m_IsAddMode(true) + , m_isOutOfBound(false) + , m_DaysOffset(0) + , m_MonthsOffset(0) + , m_YearsOffset(0) + , m_StrDateDiffResult(L"") + , m_StrDateDiffResultAutomationName(L"") + , m_StrDateDiffResultInDays(L"") + , m_StrDateResult(L"") + , m_StrDateResultAutomationName(L"") { - const auto& localizationSettings = LocalizationSettings::GetInstance(); + const auto & localizationSettings = LocalizationSettings::GetInstance(); // Initialize Date Output format instances InitializeDateOutputFormats(localizationSettings.GetCalendarIdentifier()); // Initialize Date Calc engine - m_dateCalcEngine = make_shared(localizationSettings.GetCalendarIdentifier()); - + m_dateCalcEngine = ref new DateCalculationEngine(localizationSettings.GetCalendarIdentifier()); // Initialize dates of DatePicker controls to today's date auto calendar = ref new Calendar(); + // We force the timezone to UTC, in order to avoid being affected by Daylight Saving Time + // when we calculate the difference between 2 dates. + calendar->ChangeTimeZone("UTC"); auto today = calendar->GetDateTime(); // FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC) - m_fromDate = today; - m_toDate = today; - FromDate = ClipTime(today); - ToDate = ClipTime(today); + m_fromDate = m_toDate = ClipTime(today); // StartDate should not be clipped - StartDate = today; + m_startDate = today; m_dateResult = today; // Initialize the list separator delimiter appended with a space at the end, e.g. ", " // This will be used for date difference formatting: Y years, M months, W weeks, D days - m_listSeparator = ref new String((localizationSettings.GetListSeparator() + L" ").c_str()); + m_listSeparator = localizationSettings.GetListSeparator() + L" "; // Initialize the output results UpdateDisplayResult(); - m_offsetValues = ref new Vector(); + m_offsetValues = ref new Vector(); for (int i = 0; i <= c_maxOffsetValue; i++) { wstring numberStr(to_wstring(i)); @@ -86,15 +81,6 @@ DateCalculatorViewModel::DateCalculatorViewModel() : m_offsetValues->Append(ref new String(numberStr.c_str())); } - /* In the ClipTime function, we used to change timezone to UTC before clipping the time. - The comment from the previous developers said this was done to eliminate the effects of - Daylight Savings Time. We can't think of a good reason why this change in timezone is - necessary and did find bugs related to the change, therefore, we have removed the - change. Just in case, we will see if the clipped time is ever a different day from the - original day, which would hopefully indicate the change in timezone was actually - necessary. We will collect telemetry if we find this case. If we don't see any - telemetry events after the application has been used for some time, we will feel safe - and can remove this function. */ DayOfWeek trueDayOfWeek = calendar->DayOfWeek; DateTime clippedTime = ClipTime(today); @@ -102,26 +88,22 @@ DateCalculatorViewModel::DateCalculatorViewModel() : if (calendar->DayOfWeek != trueDayOfWeek) { calendar->SetDateTime(today); - TraceLogger::GetInstance().LogDateClippedTimeDifferenceFound( - from_cx(calendar), - winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ clippedTime.UniversalTime } }); } } -void DateCalculatorViewModel::OnPropertyChanged(_In_ String^ prop) +void DateCalculatorViewModel::OnPropertyChanged(_In_ String ^ prop) { - if (prop == DateCalculatorViewModelProperties::StrDateDiffResult) + if (prop == StrDateDiffResultPropertyName) { UpdateStrDateDiffResultAutomationName(); } - else if (prop == DateCalculatorViewModelProperties::StrDateResult) + else if (prop == StrDateResultPropertyName) { UpdateStrDateResultAutomationName(); } - else if (prop != DateCalculatorViewModelProperties::StrDateDiffResultAutomationName - && prop != DateCalculatorViewModelProperties::StrDateDiffResultInDays - && prop != DateCalculatorViewModelProperties::StrDateResultAutomationName - && prop != DateCalculatorViewModelProperties::IsDiffInDays) + else if ( + prop != StrDateDiffResultAutomationNamePropertyName && prop != StrDateDiffResultInDaysPropertyName && prop != StrDateResultAutomationNamePropertyName + && prop != IsDiffInDaysPropertyName) { OnInputsChanged(); } @@ -129,42 +111,57 @@ void DateCalculatorViewModel::OnPropertyChanged(_In_ String^ prop) void DateCalculatorViewModel::OnInputsChanged() { - DateDifference dateDiff; - if (m_IsDateDiffMode) { DateTime clippedFromDate = ClipTime(FromDate); DateTime clippedToDate = ClipTime(ToDate); // Calculate difference between two dates - m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_allDateUnitsOutputFormat, &dateDiff); - DateDiffResult = dateDiff; - - m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_daysOutputFormat, &dateDiff); - DateDiffResultInDays = dateDiff; + auto dateDiff = m_dateCalcEngine->TryGetDateDifference(clippedFromDate, clippedToDate, m_daysOutputFormat); + if (dateDiff != nullptr) + { + DateDiffResultInDays = dateDiff->Value; + dateDiff = m_dateCalcEngine->TryGetDateDifference(clippedFromDate, clippedToDate, m_allDateUnitsOutputFormat); + if (dateDiff != nullptr) + { + DateDiffResult = dateDiff->Value; + } + else + { + // TryGetDateDifference wasn't able to calculate the difference in days/weeks/months/years, we will instead display the difference in days. + DateDiffResult = DateDiffResultInDays; + } + } + else + { + DateDiffResult = DateDifferenceUnknown; + DateDiffResultInDays = DateDifferenceUnknown; + } } else { + DateDifference dateDiff; dateDiff.day = DaysOffset; dateDiff.month = MonthsOffset; dateDiff.year = YearsOffset; - DateTime dateTimeResult; + IBox ^ dateTimeResult; if (m_IsAddMode) { // Add number of Days, Months and Years to a Date - IsOutOfBound = !m_dateCalcEngine->AddDuration(StartDate, dateDiff, &dateTimeResult); + dateTimeResult = m_dateCalcEngine->AddDuration(StartDate, dateDiff); } else { // Subtract number of Days, Months and Years from a Date - IsOutOfBound = !m_dateCalcEngine->SubtractDuration(StartDate, dateDiff, &dateTimeResult); + dateTimeResult = m_dateCalcEngine->SubtractDuration(StartDate, dateDiff); } + IsOutOfBound = dateTimeResult == nullptr; if (!m_isOutOfBound) { - DateResult = dateTimeResult; + DateResult = dateTimeResult->Value; } } } @@ -173,16 +170,20 @@ void DateCalculatorViewModel::UpdateDisplayResult() { if (m_IsDateDiffMode) { - // Are to and from dates the same - if (m_dateDiffResultInDays.day == 0) + if (m_dateDiffResultInDays == DateDifferenceUnknown) { + IsDiffInDays = false; + StrDateDiffResultInDays = L""; + StrDateDiffResult = AppResourceProvider::GetInstance()->GetResourceString(L"CalculationFailed"); + } + else if (m_dateDiffResultInDays.day == 0) + { + // to and from dates the same IsDiffInDays = true; StrDateDiffResultInDays = L""; - StrDateDiffResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_SameDates"); + StrDateDiffResult = AppResourceProvider::GetInstance()->GetResourceString(L"Date_SameDates"); } - else if ((m_dateDiffResult.year == 0) && - (m_dateDiffResult.month == 0) && - (m_dateDiffResult.week == 0)) + else if (m_dateDiffResult == DateDifferenceUnknown || (m_dateDiffResult.year == 0 && m_dateDiffResult.month == 0 && m_dateDiffResult.week == 0)) { IsDiffInDays = true; StrDateDiffResultInDays = L""; @@ -206,7 +207,7 @@ void DateCalculatorViewModel::UpdateDisplayResult() if (m_isOutOfBound) { // Display Date out of bound message - StrDateResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_OutOfBoundMessage"); + StrDateResult = AppResourceProvider::GetInstance()->GetResourceString(L"Date_OutOfBoundMessage"); } else { @@ -218,22 +219,20 @@ void DateCalculatorViewModel::UpdateDisplayResult() void DateCalculatorViewModel::UpdateStrDateDiffResultAutomationName() { - String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_DifferenceResultAutomationName"); - wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateDiffResult->Data()); - StrDateDiffResultAutomationName = ref new String(localizedAutomationName.c_str()); + String ^ automationFormat = AppResourceProvider::GetInstance()->GetResourceString(L"Date_DifferenceResultAutomationName"); + StrDateDiffResultAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat, StrDateDiffResult); } void DateCalculatorViewModel::UpdateStrDateResultAutomationName() { - String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_ResultingDateAutomationName"); - wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateResult->Data()); - StrDateResultAutomationName = ref new String(localizedAutomationName.c_str()); + String ^ automationFormat = AppResourceProvider::GetInstance()->GetResourceString(L"Date_ResultingDateAutomationName"); + StrDateResultAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat, StrDateResult); } -void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String^ calendarIdentifier) +void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String ^ calendarIdentifier) { // Format for Add/Subtract days - m_dateTimeFormatter = LocalizationService::GetRegionalSettingsAwareDateTimeFormatter( + m_dateTimeFormatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDateTimeFormatter( L"longdate", calendarIdentifier, ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used @@ -243,24 +242,25 @@ void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String^ calendarI m_daysOutputFormat = DateUnit::Day; } -String^ DateCalculatorViewModel::GetDateDiffString() const +String ^ DateCalculatorViewModel::GetDateDiffString() const { - String^ result = L""; + wstring result; bool addDelimiter = false; - AppResourceProvider resourceLoader = AppResourceProvider::GetInstance(); + AppResourceProvider ^ resourceLoader = AppResourceProvider::GetInstance(); auto yearCount = m_dateDiffResult.year; if (yearCount > 0) { - result = String::Concat(GetLocalizedNumberString(yearCount), L" "); + result += GetLocalizedNumberString(yearCount)->Data(); + result += L' '; if (yearCount > 1) { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Years")); + result += resourceLoader->GetResourceString(L"Date_Years")->Data(); } else { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Year")); + result += resourceLoader->GetResourceString(L"Date_Year")->Data(); } // set the flags to add a delimiter whenever the next unit is added @@ -272,22 +272,23 @@ String^ DateCalculatorViewModel::GetDateDiffString() const { if (addDelimiter) { - result = String::Concat(result, m_listSeparator); + result += m_listSeparator; } else { addDelimiter = true; } - result = String::Concat(result, String::Concat(GetLocalizedNumberString(monthCount), L" ")); + result += GetLocalizedNumberString(monthCount)->Data(); + result += L' '; if (monthCount > 1) { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Months")); + result += resourceLoader->GetResourceString(L"Date_Months")->Data(); } else { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Month")); + result += resourceLoader->GetResourceString(L"Date_Month")->Data(); } } @@ -296,22 +297,23 @@ String^ DateCalculatorViewModel::GetDateDiffString() const { if (addDelimiter) { - result = String::Concat(result, m_listSeparator); + result += m_listSeparator; } else { addDelimiter = true; } - result = String::Concat(result, String::Concat(GetLocalizedNumberString(weekCount), L" ")); + result += GetLocalizedNumberString(weekCount)->Data(); + result += L' '; if (weekCount > 1) { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Weeks")); + result += resourceLoader->GetResourceString(L"Date_Weeks")->Data(); } else { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Week")); + result += resourceLoader->GetResourceString(L"Date_Week")->Data(); } } @@ -320,46 +322,48 @@ String^ DateCalculatorViewModel::GetDateDiffString() const { if (addDelimiter) { - result = String::Concat(result, m_listSeparator); + result += m_listSeparator; } else { addDelimiter = true; } - result = String::Concat(result, String::Concat(GetLocalizedNumberString(dayCount), L" ")); + result += GetLocalizedNumberString(dayCount)->Data(); + result += L' '; if (dayCount > 1) { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Days")); + result += resourceLoader->GetResourceString(L"Date_Days")->Data(); } else { - result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Day")); + result += resourceLoader->GetResourceString(L"Date_Day")->Data(); } } - return result; + return ref new String(result.data()); } -String^ DateCalculatorViewModel::GetDateDiffStringInDays() const +String ^ DateCalculatorViewModel::GetDateDiffStringInDays() const { - String^ strDateUnit; + wstring result = GetLocalizedNumberString(m_dateDiffResultInDays.day)->Data(); + result += L' '; // Display the result as '1 day' or 'N days' if (m_dateDiffResultInDays.day > 1) { - strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Days"); + result += AppResourceProvider::GetInstance()->GetResourceString(L"Date_Days")->Data(); } else { - strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Day"); + result += AppResourceProvider::GetInstance()->GetResourceString(L"Date_Day")->Data(); } - return String::Concat(GetLocalizedNumberString(m_dateDiffResultInDays.day), String::Concat(L" ", strDateUnit)); + return ref new String(result.data()); } -void DateCalculatorViewModel::OnCopyCommand(Platform::Object^ parameter) +void DateCalculatorViewModel::OnCopyCommand(Platform::Object ^ parameter) { if (m_IsDateDiffMode) { @@ -371,20 +375,43 @@ void DateCalculatorViewModel::OnCopyCommand(Platform::Object^ parameter) } } -String^ DateCalculatorViewModel::GetLocalizedNumberString(int value) const +String ^ DateCalculatorViewModel::GetLocalizedNumberString(int value) const { wstring numberStr(to_wstring(value)); LocalizationSettings::GetInstance().LocalizeDisplayValue(&numberStr); return ref new String(numberStr.c_str()); } -// Adjusts the given DateTime to 12AM of the same day -DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime) +/// +/// Adjusts the given DateTime to 12AM of the same day +/// +/// DateTime to clip +/// Adjust the datetime using local time (by default adjust using UTC time) +DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime, bool adjustUsingLocalTime) { + DateTime referenceDateTime; + if (adjustUsingLocalTime) + { + FILETIME fileTime; + fileTime.dwLowDateTime = (DWORD)(dateTime.UniversalTime & 0xffffffff); + fileTime.dwHighDateTime = (DWORD)(dateTime.UniversalTime >> 32); + + FILETIME localFileTime; + FileTimeToLocalFileTime(&fileTime, &localFileTime); + + referenceDateTime.UniversalTime = (DWORD)localFileTime.dwHighDateTime; + referenceDateTime.UniversalTime <<= 32; + referenceDateTime.UniversalTime |= (DWORD)localFileTime.dwLowDateTime; + } + else + { + referenceDateTime = dateTime; + } auto calendar = ref new Calendar(); - calendar->SetDateTime(dateTime); - calendar->Period = 1; - calendar->Hour = 12; + calendar->ChangeTimeZone("UTC"); + calendar->SetDateTime(referenceDateTime); + calendar->Period = calendar->FirstPeriodInThisDay; + calendar->Hour = calendar->FirstHourInThisPeriod; calendar->Minute = 0; calendar->Second = 0; calendar->Nanosecond = 0; diff --git a/src/CalcViewModel/DateCalculatorViewModel.h b/src/CalcViewModel/DateCalculatorViewModel.h index f2fe7ebf..48573784 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.h +++ b/src/CalcViewModel/DateCalculatorViewModel.h @@ -12,8 +12,7 @@ namespace CalculatorApp { namespace ViewModel { - [Windows::UI::Xaml::Data::Bindable] - public ref class DateCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class DateCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: DateCalculatorViewModel(); @@ -23,8 +22,8 @@ namespace CalculatorApp // Input Properties OBSERVABLE_PROPERTY_RW(bool, IsDateDiffMode); OBSERVABLE_PROPERTY_RW(bool, IsAddMode); - OBSERVABLE_PROPERTY_RW(bool, IsDiffInDays); // If diff is only in days or the dates are the same, - // then show only one result and avoid redundancy + OBSERVABLE_PROPERTY_R(bool, IsDiffInDays); // If diff is only in days or the dates are the same, + // then show only one result and avoid redundancy OBSERVABLE_PROPERTY_RW(int, DaysOffset); OBSERVABLE_PROPERTY_RW(int, MonthsOffset); @@ -39,7 +38,10 @@ namespace CalculatorApp // From date for Date Diff property Windows::Foundation::DateTime FromDate { - Windows::Foundation::DateTime get() { return m_fromDate; } + Windows::Foundation::DateTime get() + { + return m_fromDate; + } void set(Windows::Foundation::DateTime value) { @@ -54,7 +56,10 @@ namespace CalculatorApp // To date for Date Diff property Windows::Foundation::DateTime ToDate { - Windows::Foundation::DateTime get() { return m_toDate; } + Windows::Foundation::DateTime get() + { + return m_toDate; + } void set(Windows::Foundation::DateTime value) { @@ -69,7 +74,10 @@ namespace CalculatorApp // Start date for Add/Subtract date property Windows::Foundation::DateTime StartDate { - Windows::Foundation::DateTime get() { return m_startDate; } + Windows::Foundation::DateTime get() + { + return m_startDate; + } void set(Windows::Foundation::DateTime value) { @@ -82,58 +90,84 @@ namespace CalculatorApp } // Output Properties - OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResult); - OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultAutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultInDays); - OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResult); - OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResultAutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, StrDateDiffResult); + OBSERVABLE_PROPERTY_R(Platform::String ^, StrDateDiffResultAutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, StrDateDiffResultInDays); + OBSERVABLE_PROPERTY_R(Platform::String ^, StrDateResult); + OBSERVABLE_PROPERTY_R(Platform::String ^, StrDateResultAutomationName); COMMAND_FOR_METHOD(CopyCommand, DateCalculatorViewModel::OnCopyCommand); - void OnCopyCommand(Platform::Object^ parameter); + void OnCopyCommand(Platform::Object ^ parameter); private: - void OnPropertyChanged(_In_ Platform::String^ prop); + void OnPropertyChanged(_In_ Platform::String ^ prop); void OnInputsChanged(); void UpdateDisplayResult(); void UpdateStrDateDiffResultAutomationName(); void UpdateStrDateResultAutomationName(); - void InitializeDateOutputFormats(Platform::String^ calendarIdentifier); - Platform::String^ GetDateDiffString() const; - Platform::String^ GetDateDiffStringInDays() const; - Platform::String^ GetLocalizedNumberString(int value) const; - static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime); - - static void CheckClipTimeSameDay(Windows::Globalization::Calendar^ reference); + void InitializeDateOutputFormats(Platform::String ^ calendarIdentifier); + Platform::String ^ GetDateDiffString() const; + Platform::String ^ GetDateDiffStringInDays() const; + Platform::String ^ GetLocalizedNumberString(int value) const; + static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime, bool adjustUsingLocalTime = false); property bool IsOutOfBound { - bool get() { return m_isOutOfBound; } - void set(bool value) { m_isOutOfBound = value; UpdateDisplayResult(); } + bool get() + { + return m_isOutOfBound; + } + void set(bool value) + { + m_isOutOfBound = value; + UpdateDisplayResult(); + } } property CalculatorApp::Common::DateCalculation::DateDifference DateDiffResult { - CalculatorApp::Common::DateCalculation::DateDifference get() { return m_dateDiffResult; } - void set(CalculatorApp::Common::DateCalculation::DateDifference value) { m_dateDiffResult = value; UpdateDisplayResult(); } + CalculatorApp::Common::DateCalculation::DateDifference get() + { + return m_dateDiffResult; + } + void set(CalculatorApp::Common::DateCalculation::DateDifference value) + { + m_dateDiffResult = value; + UpdateDisplayResult(); + } } property CalculatorApp::Common::DateCalculation::DateDifference DateDiffResultInDays { - CalculatorApp::Common::DateCalculation::DateDifference get() { return m_dateDiffResultInDays; } - void set(CalculatorApp::Common::DateCalculation::DateDifference value) { m_dateDiffResultInDays = value; UpdateDisplayResult(); } + CalculatorApp::Common::DateCalculation::DateDifference get() + { + return m_dateDiffResultInDays; + } + void set(CalculatorApp::Common::DateCalculation::DateDifference value) + { + m_dateDiffResultInDays = value; + UpdateDisplayResult(); + } } property Windows::Foundation::DateTime DateResult { - Windows::Foundation::DateTime get() { return m_dateResult; } - void set(Windows::Foundation::DateTime value) { m_dateResult = value; UpdateDisplayResult();} + Windows::Foundation::DateTime get() + { + return m_dateResult; + } + void set(Windows::Foundation::DateTime value) + { + m_dateResult = value; + UpdateDisplayResult(); + } } private: // Property variables bool m_isOutOfBound; - Platform::Collections::Vector^ m_offsetValues; + Platform::Collections::Vector ^ m_offsetValues; Windows::Foundation::DateTime m_fromDate; Windows::Foundation::DateTime m_toDate; Windows::Foundation::DateTime m_startDate; @@ -142,11 +176,11 @@ namespace CalculatorApp CalculatorApp::Common::DateCalculation::DateDifference m_dateDiffResultInDays; // Private members - std::shared_ptr m_dateCalcEngine; + CalculatorApp::Common::DateCalculation::DateCalculationEngine ^ m_dateCalcEngine; CalculatorApp::Common::DateCalculation::DateUnit m_daysOutputFormat; CalculatorApp::Common::DateCalculation::DateUnit m_allDateUnitsOutputFormat; - Windows::Globalization::DateTimeFormatting::DateTimeFormatter^ m_dateTimeFormatter; - Platform::String^ m_listSeparator; + Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ m_dateTimeFormatter; + std::wstring m_listSeparator; }; } } diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp new file mode 100644 index 00000000..50592aa4 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp @@ -0,0 +1,461 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "EquationViewModel.h" +#include "CalcViewModel\Common\LocalizationSettings.h" +#include "CalcViewModel\GraphingCalculatorEnums.h" + +using namespace CalculatorApp::Common; +using namespace Graphing; +using namespace Platform; +using namespace Platform::Collections; +using namespace std; +using namespace Windows::ApplicationModel::Resources; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::Foundation::Collections; +using namespace GraphControl; + +namespace CalculatorApp::ViewModel +{ + GridDisplayItems::GridDisplayItems() + : m_Expression{ "" } + , m_Direction{ "" } + { + } + + KeyGraphFeaturesItem::KeyGraphFeaturesItem() + : m_Title{ "" } + , m_DisplayItems{ ref new Vector() } + , m_GridItems{ ref new Vector() } + , m_IsText{ false } + { + } + + EquationViewModel::EquationViewModel(Equation ^ equation, int functionLabelIndex, Windows::UI::Color color, int colorIndex) + : m_AnalysisErrorVisible{ false } + , m_FunctionLabelIndex{ functionLabelIndex } + , m_KeyGraphFeaturesItems{ ref new Vector() } + , m_resourceLoader{ ::ResourceLoader::GetForCurrentView() } + { + if (equation == nullptr) + { + throw ref new InvalidArgumentException(L"Equation cannot be null"); + } + + GraphEquation = equation; + LineColor = color; + LineColorIndex = colorIndex; + IsLineEnabled = true; + } + + void EquationViewModel::PopulateKeyGraphFeatures(KeyGraphFeaturesInfo ^ graphEquation) + { + if (graphEquation->AnalysisError != 0) + { + AnalysisErrorVisible = true; + if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisCouldNotBePerformed)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisCouldNotBePerformed"); + } + else if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisNotSupported)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisNotSupported"); + } + else if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::VariableIsNotX)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFVariableIsNotX"); + } + return; + } + + KeyGraphFeaturesItems->Clear(); + + AddKeyGraphFeature(m_resourceLoader->GetString(L"Domain"), graphEquation->Domain, m_resourceLoader->GetString(L"KGFDomainNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Range"), graphEquation->Range, m_resourceLoader->GetString(L"KGFRangeNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"XIntercept"), graphEquation->XIntercept, m_resourceLoader->GetString(L"KGFXInterceptNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"YIntercept"), graphEquation->YIntercept, m_resourceLoader->GetString(L"KGFYInterceptNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Minima"), graphEquation->Minima, m_resourceLoader->GetString(L"KGFMinimaNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Maxima"), graphEquation->Maxima, m_resourceLoader->GetString(L"KGFMaximaNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"InflectionPoints"), graphEquation->InflectionPoints, m_resourceLoader->GetString(L"KGFInflectionPointsNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"HorizontalAsymptotes"), + graphEquation->HorizontalAsymptotes, + m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone")); + AddParityKeyGraphFeature(graphEquation); + AddPeriodicityKeyGraphFeature(graphEquation); + AddMonotoncityKeyGraphFeature(graphEquation); + AddTooComplexKeyGraphFeature(graphEquation); + + AnalysisErrorVisible = false; + } + + void EquationViewModel::AddKeyGraphFeature(String ^ title, String ^ expression, String ^ errorString) + { + KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem(); + item->Title = title; + if (expression != L"") + { + item->DisplayItems->Append(expression); + item->IsText = false; + } + else + { + item->DisplayItems->Append(errorString); + item->IsText = true; + } + KeyGraphFeaturesItems->Append(item); + } + + void EquationViewModel::AddKeyGraphFeature(String ^ title, IVector ^ expressionVector, String ^ errorString) + { + KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem(); + item->Title = title; + if (expressionVector->Size != 0) + { + for (auto expression : expressionVector) + { + item->DisplayItems->Append(expression); + } + item->IsText = false; + } + else + { + item->DisplayItems->Append(errorString); + item->IsText = true; + } + KeyGraphFeaturesItems->Append(item); + } + + void EquationViewModel::AddParityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ parityItem = ref new KeyGraphFeaturesItem(); + parityItem->Title = m_resourceLoader->GetString(L"Parity"); + switch (graphEquation->Parity) + { + case 0: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown")); + break; + case 1: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityOdd")); + break; + case 2: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityEven")); + break; + case 3: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityNeither")); + break; + default: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown")); + } + parityItem->IsText = true; + + KeyGraphFeaturesItems->Append(parityItem); + } + + void EquationViewModel::AddPeriodicityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ periodicityItem = ref new KeyGraphFeaturesItem(); + periodicityItem->Title = m_resourceLoader->GetString(L"Periodicity"); + switch (graphEquation->PeriodicityDirection) + { + case 0: + // Periodicity is not supported or is too complex to calculate. + // Return out of this function without adding periodicity to KeyGraphFeatureItems. + // SetTooComplexFeaturesErrorProperty will set the too complex error when periodicity is supported and unknown + return; + case 1: + if (graphEquation->PeriodicityExpression == L"") + { + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityUnknown")); + periodicityItem->IsText = true; + } + else + { + periodicityItem->DisplayItems->Append(graphEquation->PeriodicityExpression); + periodicityItem->IsText = false; + } + break; + case 2: + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityNotPeriodic")); + periodicityItem->IsText = false; + break; + default: + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityError")); + periodicityItem->IsText = true; + } + + KeyGraphFeaturesItems->Append(periodicityItem); + } + + void EquationViewModel::AddMonotoncityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ monotonicityItem = ref new KeyGraphFeaturesItem(); + monotonicityItem->Title = m_resourceLoader->GetString(L"Monotonicity"); + if (graphEquation->Monotonicity->Size != 0) + { + for (auto item : graphEquation->Monotonicity) + { + GridDisplayItems ^ gridItem = ref new GridDisplayItems(); + gridItem->Expression = item->Key; + + auto monotonicityType = item->Value->Data(); + switch (*monotonicityType) + { + case '0': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityUnknown"); + break; + case '1': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityIncreasing"); + break; + case '2': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityDecreasing"); + break; + case '3': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityConstant"); + break; + default: + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityError"); + break; + } + + monotonicityItem->GridItems->Append(gridItem); + } + monotonicityItem->IsText = false; + } + else + { + monotonicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFMonotonicityError")); + monotonicityItem->IsText = true; + } + + KeyGraphFeaturesItems->Append(monotonicityItem); + } + + void EquationViewModel::AddTooComplexKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + if (graphEquation->TooComplexFeatures <= 0) + { + return; + } + + Platform::String ^ separator = ref new String(LocalizationSettings::GetInstance().GetListSeparator().c_str()); + + wstring error; + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain) + { + error.append((m_resourceLoader->GetString(L"Domain") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range) + { + error.append((m_resourceLoader->GetString(L"Range") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros) + { + error.append((m_resourceLoader->GetString(L"XIntercept") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept) + { + error.append((m_resourceLoader->GetString(L"YIntercept") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity) + { + error.append((m_resourceLoader->GetString(L"Parity") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity) + { + error.append((m_resourceLoader->GetString(L"Periodicity") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima) + { + error.append((m_resourceLoader->GetString(L"Minima") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima) + { + error.append((m_resourceLoader->GetString(L"Maxima") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints) + { + error.append((m_resourceLoader->GetString(L"InflectionPoints") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes) + { + error.append((m_resourceLoader->GetString(L"VerticalAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes) + { + error.append((m_resourceLoader->GetString(L"HorizontalAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes) + { + error.append((m_resourceLoader->GetString(L"ObliqueAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals) + { + error.append((m_resourceLoader->GetString(L"Monotonicity") + separator + L" ")->Data()); + } + + KeyGraphFeaturesItem ^ tooComplexItem = ref new KeyGraphFeaturesItem(); + tooComplexItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFTooComplexFeaturesError")); + tooComplexItem->DisplayItems->Append(ref new String(error.substr(0, (error.length() - (separator->Length() + 1))).c_str())); + tooComplexItem->IsText = true; + + KeyGraphFeaturesItems->Append(tooComplexItem); + } + + String ^ EquationViewModel::EquationErrorText(ErrorType errorType, int errorCode) + { + auto resLoader = ResourceLoader::GetForCurrentView(); + if (errorType == ::ErrorType::Evaluation) + { + switch (static_cast(errorCode)) + { + case (EvaluationErrorCode::Overflow): + return resLoader->GetString(L"Overflow"); + break; + case (EvaluationErrorCode::RequireRadiansMode): + return resLoader->GetString(L"RequireRadiansMode"); + break; + case (EvaluationErrorCode::TooComplexToSolve): + return resLoader->GetString(L"TooComplexToSolve"); + break; + case (EvaluationErrorCode::RequireDegreesMode): + return resLoader->GetString(L"RequireDegreesMode"); + break; + case (EvaluationErrorCode::FactorialInvalidArgument): + case (EvaluationErrorCode::Factorial2InvalidArgument): + return resLoader->GetString(L"FactorialInvalidArgument"); + break; + case (EvaluationErrorCode::FactorialCannotPerformOnLargeNumber): + return resLoader->GetString(L"FactorialCannotPerformOnLargeNumber"); + break; + case (EvaluationErrorCode::ModuloCannotPerformOnFloat): + return resLoader->GetString(L"ModuloCannotPerformOnFloat"); + break; + case (EvaluationErrorCode::EquationTooComplexToSolve): + case (EvaluationErrorCode::EquationTooComplexToSolveSymbolic): + case (EvaluationErrorCode::EquationTooComplexToPlot): + case (EvaluationErrorCode::InequalityTooComplexToSolve): + case (EvaluationErrorCode::GE_TooComplexToSolve): + return resLoader->GetString(L"TooComplexToSolve"); + break; + case (EvaluationErrorCode::EquationHasNoSolution): + case (EvaluationErrorCode::InequalityHasNoSolution): + return resLoader->GetString(L"EquationHasNoSolution"); + break; + case (EvaluationErrorCode::DivideByZero): + return resLoader->GetString(L"DivideByZero"); + break; + case (EvaluationErrorCode::MutuallyExclusiveConditions): + return resLoader->GetString(L"MutuallyExclusiveConditions"); + break; + case (EvaluationErrorCode::OutOfDomain): + return resLoader->GetString(L"OutOfDomain"); + break; + case (EvaluationErrorCode::GE_NotSupported): + return resLoader->GetString(L"GE_NotSupported"); + break; + default: + return resLoader->GetString(L"GeneralError"); + break; + } + } + else if (errorType == ::ErrorType::Syntax) + { + switch (static_cast(errorCode)) + { + case (SyntaxErrorCode::ParenthesisMismatch): + return resLoader->GetString(L"ParenthesisMismatch"); + break; + case (SyntaxErrorCode::UnmatchedParenthesis): + return resLoader->GetString(L"UnmatchedParenthesis"); + break; + case (SyntaxErrorCode::TooManyDecimalPoints): + return resLoader->GetString(L"TooManyDecimalPoints"); + break; + case (SyntaxErrorCode::DecimalPointWithoutDigits): + return resLoader->GetString(L"DecimalPointWithoutDigits"); + break; + case (SyntaxErrorCode::UnexpectedEndOfExpression): + return resLoader->GetString(L"UnexpectedEndOfExpression"); + break; + case (SyntaxErrorCode::UnexpectedToken): + return resLoader->GetString(L"UnexpectedToken"); + break; + case (SyntaxErrorCode::InvalidToken): + return resLoader->GetString(L"InvalidToken"); + break; + case (SyntaxErrorCode::TooManyEquals): + return resLoader->GetString(L"TooManyEquals"); + break; + case (SyntaxErrorCode::EqualWithoutGraphVariable): + return resLoader->GetString(L"EqualWithoutGraphVariable"); + break; + case (SyntaxErrorCode::InvalidEquationSyntax): + case (SyntaxErrorCode::InvalidEquationFormat): + return resLoader->GetString(L"InvalidEquationSyntax"); + break; + case (SyntaxErrorCode::EmptyExpression): + return resLoader->GetString(L"EmptyExpression"); + break; + case (SyntaxErrorCode::EqualWithoutEquation): + return resLoader->GetString(L"EqualWithoutEquation"); + break; + case (SyntaxErrorCode::ExpectParenthesisAfterFunctionName): + return resLoader->GetString(L"ExpectParenthesisAfterFunctionName"); + break; + case (SyntaxErrorCode::IncorrectNumParameter): + return resLoader->GetString(L"IncorrectNumParameter"); + break; + case (SyntaxErrorCode::InvalidVariableNameFormat): + return resLoader->GetString(L"InvalidVariableNameFormat"); + break; + case (SyntaxErrorCode::BracketMismatch): + return resLoader->GetString(L"BracketMismatch"); + break; + case (SyntaxErrorCode::UnmatchedBracket): + return resLoader->GetString(L"UnmatchedBracket"); + break; + case (SyntaxErrorCode::CannotUseIInReal): + return resLoader->GetString(L"CannotUseIInReal"); + break; + case (SyntaxErrorCode::InvalidNumberDigit): + return resLoader->GetString(L"InvalidNumberDigit"); + break; + case (SyntaxErrorCode::InvalidNumberBase): + return resLoader->GetString(L"InvalidNumberBase"); + break; + case (SyntaxErrorCode::InvalidVariableSpecification): + return resLoader->GetString(L"InvalidVariableSpecification"); + break; + case (SyntaxErrorCode::ExpectingLogicalOperands): + case (SyntaxErrorCode::ExpectingScalarOperands): + return resLoader->GetString(L"ExpectingLogicalOperands"); + break; + case (SyntaxErrorCode::CannotUseIndexVarInOpLimits): + return resLoader->GetString(L"CannotUseIndexVarInOpLimits"); + break; + case (SyntaxErrorCode::CannotUseIndexVarInLimPoint): + return resLoader->GetString(L"Overflow"); + break; + case (SyntaxErrorCode::CannotUseComplexInfinityInReal): + return resLoader->GetString(L"CannotUseComplexInfinityInReal"); + break; + case (SyntaxErrorCode::CannotUseIInInequalitySolving): + return resLoader->GetString(L"CannotUseIInInequalitySolving"); + break; + default: + return resLoader->GetString(L"GeneralError"); + break; + } + } + + return resLoader->GetString(L"GeneralError"); + } +} diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.h b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h new file mode 100644 index 00000000..292e857d --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" + +namespace GraphControl +{ + ref class Equation; + ref class KeyGraphFeaturesInfo; +} + +namespace CalculatorApp::ViewModel +{ +public + ref class GridDisplayItems sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + GridDisplayItems(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Expression); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Direction); + }; + +public + ref class KeyGraphFeaturesItem sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + KeyGraphFeaturesItem(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Title); + OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, DisplayItems); + OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, GridItems); + OBSERVABLE_PROPERTY_RW(bool, IsText); + }; + +public + ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + EquationViewModel(GraphControl::Equation ^ equation, int functionLabelIndex, Windows::UI::Color color, int colorIndex); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(GraphControl::Equation ^, GraphEquation); + OBSERVABLE_PROPERTY_RW(int, FunctionLabelIndex); + OBSERVABLE_PROPERTY_RW(bool, IsLastItemInList); + PROPERTY_RW(int, LineColorIndex); + + property Platform::String ^ Expression + { + Platform::String ^ get() + { + return GraphEquation->Expression; + } + void set(Platform::String ^ value) + { + if (GraphEquation->Expression != value) + { + GraphEquation->Expression = value; + RaisePropertyChanged("Expression"); + } + } + } + + property Windows::UI::Color LineColor + { + Windows::UI::Color get() + { + return GraphEquation->LineColor; + } + void set(Windows::UI::Color value) + { + if (!Utils::AreColorsEqual(GraphEquation->LineColor, value)) + { + GraphEquation->LineColor = value; + RaisePropertyChanged("LineColor"); + } + } + } + + property bool IsLineEnabled + { + bool get() + { + return GraphEquation->IsLineEnabled; + } + void set(bool value) + { + if (GraphEquation->IsLineEnabled != value) + { + GraphEquation->IsLineEnabled = value; + RaisePropertyChanged("IsLineEnabled"); + } + } + } + + // Key Graph Features + OBSERVABLE_PROPERTY_R(Platform::String ^, AnalysisErrorString); + OBSERVABLE_PROPERTY_R(bool, AnalysisErrorVisible); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, KeyGraphFeaturesItems) + + void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info); + + static Platform::String ^ EquationErrorText(GraphControl::ErrorType errorType, int errorCode); + + private: + void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString); + void AddKeyGraphFeature( + Platform::String ^ title, + Windows::Foundation::Collections::IVector ^ expressionVector, + Platform::String ^ errorString); + void AddParityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddPeriodicityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddMonotoncityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddTooComplexKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + + Windows::Foundation::Collections::IObservableMap ^ m_Monotonicity; + Windows::ApplicationModel::Resources::ResourceLoader ^ m_resourceLoader; + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp new file mode 100644 index 00000000..4e605612 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "GraphingCalculatorViewModel.h" + +using namespace CalculatorApp::ViewModel; +using namespace Platform; +using namespace Platform::Collections; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml::Data; +using namespace GraphControl; + +namespace CalculatorApp::ViewModel +{ + GraphingCalculatorViewModel::GraphingCalculatorViewModel() + : m_IsDecimalEnabled{ true } + , m_Equations{ ref new Vector() } + , m_Variables{ ref new Vector() } + { + } + + void GraphingCalculatorViewModel::OnButtonPressed(Object ^ parameter) + { + } + + void GraphingCalculatorViewModel::UpdateVariables(IMap ^ variables) + { + Variables->Clear(); + for (auto variablePair : variables) + { + auto variable = ref new VariableViewModel(variablePair->Key, variablePair->Value); + variable->VariableUpdated += ref new EventHandler([this, variable](Object ^ sender, VariableChangedEventArgs e) { + VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue }); + }); + Variables->Append(variable); + } + } + + void GraphingCalculatorViewModel::SetSelectedEquation(EquationViewModel ^ equation) + { + SelectedEquation = equation; + } +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h new file mode 100644 index 00000000..eabd88e9 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" +#include "EquationViewModel.h" +#include "VariableViewModel.h" + +namespace CalculatorApp::ViewModel +{ + [Windows::UI::Xaml::Data::Bindable] public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + GraphingCalculatorViewModel(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Equations); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Variables); + OBSERVABLE_PROPERTY_R(EquationViewModel ^, SelectedEquation); + + COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed); + + event Windows::Foundation::EventHandler ^ VariableUpdated; + + void UpdateVariables(Windows::Foundation::Collections::IMap ^ variables); + + void SetSelectedEquation(EquationViewModel ^ equation); + private: + void OnButtonPressed(Platform::Object ^ parameter); + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp new file mode 100644 index 00000000..3c7de353 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "GraphingSettingsViewModel.h" +#include + +using namespace CalculatorApp::ViewModel; +using namespace CalcManager::NumberFormattingUtils; +using namespace GraphControl; +using namespace std; +using namespace Platform; +using namespace Windows::UI::Xaml; + +GraphingSettingsViewModel::GraphingSettingsViewModel() + : m_XMinValue(0) + , m_XMaxValue(0) + , m_YMinValue(0) + , m_YMaxValue(0) + , m_XMinError(false) + , m_XMaxError(false) + , m_YMinError(false) + , m_YMaxError(false) + , m_dontUpdateDisplayRange() + , m_XIsMinLastChanged(true) + , m_YIsMinLastChanged(true) +{ +} + +void GraphingSettingsViewModel::SetGrapher(Grapher ^ grapher) +{ + if (grapher != nullptr) + { + if (grapher->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Invalid) + { + grapher->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians; + } + } + Graph = grapher; + + InitRanges(); + RaisePropertyChanged(L"TrigUnit"); +} + +void GraphingSettingsViewModel::InitRanges() +{ + double xMin = 0, xMax = 0, yMin = 0, yMax = 0; + if (m_Graph != nullptr) + { + m_Graph->GetDisplayRanges(&xMin, &xMax, &yMin, &yMax); + } + m_dontUpdateDisplayRange = true; + m_XMinValue = xMin; + m_XMaxValue = xMax; + m_YMinValue = yMin; + m_YMaxValue = yMax; + auto valueStr = to_wstring(m_XMinValue); + TrimTrailingZeros(valueStr); + XMin = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_XMaxValue); + TrimTrailingZeros(valueStr); + XMax = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_YMinValue); + TrimTrailingZeros(valueStr); + YMin = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_YMaxValue); + TrimTrailingZeros(valueStr); + YMax = ref new String(valueStr.c_str()); + + m_dontUpdateDisplayRange = false; +} + +void GraphingSettingsViewModel::ResetView() +{ + if (m_Graph != nullptr) + { + m_Graph->ResetGrid(); + InitRanges(); + m_XMinError = false; + m_XMaxError = false; + m_YMinError = false; + m_YMaxError = false; + + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMin"); + RaisePropertyChanged("XMax"); + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMin"); + RaisePropertyChanged("YMax"); + } +} + +void GraphingSettingsViewModel::UpdateDisplayRange() +{ + if (m_Graph == nullptr || m_dontUpdateDisplayRange || HasError()) + { + return; + } + + m_Graph->SetDisplayRanges(m_XMinValue, m_XMaxValue, m_YMinValue, m_YMaxValue); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::Grid, L""); +} + +bool GraphingSettingsViewModel::HasError() +{ + return m_XMinError || m_YMinError || m_XMaxError || m_YMaxError || XError || YError; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h new file mode 100644 index 00000000..6d6045c0 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "../Common/Utils.h" +#include "CalcViewModel/Common/TraceLogger.h" + +namespace CalculatorApp::ViewModel +{ +#pragma once + [Windows::UI::Xaml::Data::Bindable] public ref class GraphingSettingsViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(bool, YMinError); + OBSERVABLE_PROPERTY_R(bool, XMinError); + OBSERVABLE_PROPERTY_R(bool, XMaxError); + OBSERVABLE_PROPERTY_R(bool, YMaxError); + OBSERVABLE_PROPERTY_R(GraphControl::Grapher ^, Graph); + + GraphingSettingsViewModel(); + + property bool XError + { + bool get() + { + return !m_XMinError && !m_XMaxError && m_XMinValue >= m_XMaxValue; + } + } + + property bool YError + { + bool get() + { + return !m_YMinError && !m_YMaxError && m_YMinValue >= m_YMaxValue; + } + } + + property Platform::String ^ XMin + { + Platform::String ^ get() + { + return m_XMin; + } + void set(Platform::String ^ value) + { + if (m_XMin == value) + { + return; + } + m_XMin = value; + m_XIsMinLastChanged = true; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->XAxisMin = m_XMinValue = number; + XMinError = false; + } + else + { + XMinError = true; + } + } + catch (...) + { + XMinError = true; + } + } + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMin"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ XMax + { + Platform::String ^ get() + { + return m_XMax; + } + void set(Platform::String ^ value) + { + if (m_XMax == value) + { + return; + } + m_XMax = value; + m_XIsMinLastChanged = false; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->XAxisMax = m_XMaxValue = number; + XMaxError = false; + } + else + { + XMaxError = true; + } + } + catch (...) + { + XMaxError = true; + } + } + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMax"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ YMin + { + Platform::String ^ get() + { + return m_YMin; + } + void set(Platform::String ^ value) + { + if (m_YMin == value) + { + return; + } + m_YMin = value; + m_YIsMinLastChanged = true; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->YAxisMin = m_YMinValue = number; + YMinError = false; + } + else + { + YMinError = true; + } + } + catch (...) + { + YMinError = true; + } + } + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMin"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ YMax + { + Platform::String ^ get() + { + return m_YMax; + } + void set(Platform::String ^ value) + { + if (m_YMax == value) + { + return; + } + m_YMax = value; + m_YIsMinLastChanged = false; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->YAxisMax = m_YMaxValue = number; + YMaxError = false; + } + else + { + YMaxError = true; + } + } + catch (...) + { + YMaxError = true; + } + } + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMax"); + UpdateDisplayRange(); + } + } + + property int TrigUnit + { + int get() + { + return m_Graph == nullptr ? (int)Graphing::EvalTrigUnitMode::Invalid : m_Graph->TrigUnitMode; + } + void set(int value) + { + if (m_Graph == nullptr) + { + return; + } + m_Graph->TrigUnitMode = value; + RaisePropertyChanged(L"TrigUnit"); + } + } + + property bool TrigModeRadians + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Radians; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Radians) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians; + + RaisePropertyChanged(L"TrigModeRadians"); + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeGradians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Radians"); + } + } + } + + property bool TrigModeDegrees + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Degrees; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Degrees) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Degrees; + + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeRadians"); + RaisePropertyChanged(L"TrigModeGradians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Degrees"); + } + } + } + + property bool TrigModeGradians + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Grads; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Grads) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Grads; + + RaisePropertyChanged(L"TrigModeGradians"); + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeRadians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Gradians"); + } + } + } + + public: + void UpdateDisplayRange(); + + public: + void SetGrapher(GraphControl::Grapher ^ grapher); + void InitRanges(); + void ResetView(); + bool HasError(); + + private: + Platform::String ^ m_XMin; + Platform::String ^ m_XMax; + Platform::String ^ m_YMin; + Platform::String ^ m_YMax; + double m_XMinValue; + double m_XMaxValue; + double m_YMinValue; + double m_YMaxValue; + bool m_dontUpdateDisplayRange; + bool m_XIsMinLastChanged; + bool m_YIsMinLastChanged; + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/VariableViewModel.h b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h new file mode 100644 index 00000000..151499ec --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" +#include "CalcViewModel/Common/LocalizationStringUtil.h" +#include "EquationViewModel.h" + +namespace CalculatorApp::ViewModel +{ + +inline constexpr int DefaultMinMaxRange = 10; + +public + value struct VariableChangedEventArgs sealed + { + Platform::String ^ variableName; + double newValue; + }; + + [Windows::UI::Xaml::Data::Bindable] public ref class VariableViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + VariableViewModel(Platform::String ^ name, GraphControl::Variable ^ variable) + : m_Name(name) + , m_variable{ variable } + , m_SliderSettingsVisible(false) + { + } + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(Platform::String ^, Name); + OBSERVABLE_PROPERTY_RW(bool, SliderSettingsVisible); + + property double Min + { + double get() + { + return m_variable->Min; + } + void set(double value) + { + if (m_variable->Min != value) + { + if (value >= m_variable->Max) + { + m_variable->Max = value + DefaultMinMaxRange; + RaisePropertyChanged("Max"); + } + + m_variable->Min = value; + RaisePropertyChanged("Min"); + } + } + } + + property double Step + { + double get() + { + return m_variable->Step; + } + void set(double value) + { + if (m_variable->Step != value) + { + m_variable->Step = value; + RaisePropertyChanged("Step"); + } + } + } + + property double Max + { + double get() + { + return m_variable->Max; + } + void set(double value) + { + if (m_variable->Max != value) + { + if (value <= m_variable->Min) + { + m_variable->Min = value - DefaultMinMaxRange; + RaisePropertyChanged("Min"); + } + + m_variable->Max = value; + RaisePropertyChanged("Max"); + } + } + } + + event Windows::Foundation::EventHandler ^ VariableUpdated; + + property double Value + { + double get() + { + return m_variable->Value; + } + void set(double value) + { + if (value < m_variable->Min) + { + m_variable->Min = value; + RaisePropertyChanged(L"Min"); + } + else if (value > m_variable->Max) + { + m_variable->Max = value; + RaisePropertyChanged(L"Max"); + } + + if (m_variable->Value != value) + { + m_variable->Value = value; + VariableUpdated(this, VariableChangedEventArgs{ Name, value }); + RaisePropertyChanged(L"Value"); + } + } + } + + property Platform::String ^ VariableAutomationName + { + Platform::String ^ get() + { + return CalculatorApp::Common::LocalizationStringUtil::GetLocalizedString( + CalculatorApp::AppResourceProvider::GetInstance()->GetResourceString(L"VariableListViewItem"), Name); + } + } + + private: + GraphControl::Variable ^ m_variable; + }; +} diff --git a/src/CalcViewModel/GraphingCalculatorEnums.h b/src/CalcViewModel/GraphingCalculatorEnums.h new file mode 100644 index 00000000..8093f4a6 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculatorEnums.h @@ -0,0 +1,29 @@ +#pragma once + +namespace CalculatorApp +{ + enum KeyGraphFeaturesFlag + { + Domain = 1, + Range = 2, + Parity = 4, + Periodicity = 8, + Zeros = 16, + YIntercept = 32, + Minima = 64, + Maxima = 128, + InflectionPoints = 256, + VerticalAsymptotes = 512, + HorizontalAsymptotes = 1024, + ObliqueAsymptotes = 2048, + MonotoneIntervals = 4096 + }; + + enum AnalysisErrorType + { + NoError, + AnalysisCouldNotBePerformed, + AnalysisNotSupported, + VariableIsNotX + }; +} diff --git a/src/CalcViewModel/HistoryItemViewModel.cpp b/src/CalcViewModel/HistoryItemViewModel.cpp index c5cd0784..95775bb9 100644 --- a/src/CalcViewModel/HistoryItemViewModel.cpp +++ b/src/CalcViewModel/HistoryItemViewModel.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "pch.h" @@ -10,56 +10,33 @@ using namespace CalculatorApp::ViewModel; using namespace std; using namespace Platform; - -HistoryItemViewModel::HistoryItemViewModel(String^ expression, String^ result, - _In_ const shared_ptr>> &spTokens, - _In_ const shared_ptr>> &spCommands) :m_expression(expression), m_result(result), m_spTokens(spTokens), m_spCommands(spCommands) +HistoryItemViewModel::HistoryItemViewModel( + String ^ expression, + String ^ result, + _In_ const shared_ptr>>& spTokens, + _In_ const shared_ptr>>& spCommands) + : m_expression(expression) + , m_result(result) + , m_spTokens(spTokens) + , m_spCommands(spCommands) { // updating accessibility names for expression and result m_accExpression = HistoryItemViewModel::GetAccessibleExpressionFromTokens(spTokens, m_expression); m_accResult = LocalizationService::GetNarratorReadableString(m_result); } -String^ HistoryItemViewModel::GetAccessibleExpressionFromTokens(_In_ shared_ptr< CalculatorVector< pair< wstring, int > > > const &spTokens, _In_ String^ fallbackExpression) +String + ^ HistoryItemViewModel::GetAccessibleExpressionFromTokens( + _In_ shared_ptr>> const& spTokens, + _In_ String ^ fallbackExpression) { // updating accessibility names for expression and result - wstringstream accExpression{}; - accExpression << L""; + wstring accExpression{}; - unsigned int nTokens; - HRESULT hr = spTokens->GetSize(&nTokens); - if (SUCCEEDED(hr)) + for (const auto& tokenItem : *spTokens) { - pair tokenItem; - for (unsigned int i = 0; i < nTokens; i++) - { - hr = spTokens->GetAt(i, &tokenItem); - if (FAILED(hr)) - { - break; - } - - wstring token = tokenItem.first; - accExpression << LocalizationService::GetNarratorReadableToken(StringReference(token.c_str()))->Data(); - } + accExpression += LocalizationService::GetNarratorReadableToken(StringReference(tokenItem.first.c_str()))->Data(); } - if (SUCCEEDED(hr)) - { - wstring expressionSuffix{}; - hr = spTokens->GetExpressionSuffix(&expressionSuffix); - if (SUCCEEDED(hr)) - { - accExpression << expressionSuffix; - } - } - - if (FAILED(hr)) - { - return LocalizationService::GetNarratorReadableString(fallbackExpression); - } - else - { - return ref new String(accExpression.str().c_str()); - } + return ref new String(accExpression.c_str()); } diff --git a/src/CalcViewModel/HistoryItemViewModel.h b/src/CalcViewModel/HistoryItemViewModel.h index 25822488..0e30b0c8 100644 --- a/src/CalcViewModel/HistoryItemViewModel.h +++ b/src/CalcViewModel/HistoryItemViewModel.h @@ -3,91 +3,57 @@ #pragma once -#include "CalcManager/CalculatorVector.h" #include "CalcManager/ExpressionCommandInterface.h" namespace CalculatorApp { namespace ViewModel { - [Windows::UI::Xaml::Data::Bindable] - public ref class HistoryItemViewModel sealed : Windows::UI::Xaml::Data::ICustomPropertyProvider + [Windows::UI::Xaml::Data::Bindable] public ref class HistoryItemViewModel sealed { + internal : - internal: + HistoryItemViewModel( + Platform::String ^ expression, + Platform::String ^ result, + _In_ std::shared_ptr>> const& spTokens, + _In_ std::shared_ptr>> const& spCommands); - HistoryItemViewModel(Platform::String^ expression, - Platform::String^ result, - _In_ std::shared_ptr>> const &spTokens, - _In_ std::shared_ptr>> const &spCommands); - - std::shared_ptr>> const& GetTokens() + std::shared_ptr>> const& GetTokens() { return m_spTokens; } - std::shared_ptr>> const& GetCommands() + std::shared_ptr>> const& GetCommands() { return m_spCommands; } public: + property Platform::String + ^ Expression { Platform::String ^ get() { return m_expression; } } - property Platform::String^ Expression - { - Platform::String^ get() { return m_expression; } - } + property Platform::String + ^ AccExpression { Platform::String ^ get() { return m_accExpression; } } - property Platform::String^ AccExpression - { - Platform::String^ get() { return m_accExpression; } - } + property Platform::String + ^ Result { Platform::String ^ get() { return m_result; } } - property Platform::String^ Result - { - Platform::String^ get() { return m_result; } - } + property Platform::String + ^ AccResult { Platform::String ^ get() { return m_accResult; } } - property Platform::String^ AccResult - { - Platform::String^ get() { return m_accResult; } - } - - virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name) - { - return nullptr; - } - - virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type) - { - return nullptr; - } - - virtual property Windows::UI::Xaml::Interop::TypeName Type - { - Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - virtual Platform::String^ GetStringRepresentation() - { - return m_accExpression + " " + m_accResult; - } + private : static Platform::String + ^ GetAccessibleExpressionFromTokens( + _In_ std::shared_ptr>> const& spTokens, + _In_ Platform::String ^ fallbackExpression); private: - static Platform::String^ GetAccessibleExpressionFromTokens( - _In_ std::shared_ptr>> const &spTokens, - _In_ Platform::String^ fallbackExpression); - - private: - Platform::String^ m_expression; - Platform::String^ m_accExpression; - Platform::String^ m_accResult; - Platform::String^ m_result; - std::shared_ptr>> m_spTokens; - std::shared_ptr>> m_spCommands; + Platform::String ^ m_expression; + Platform::String ^ m_accExpression; + Platform::String ^ m_accResult; + Platform::String ^ m_result; + std::shared_ptr>> m_spTokens; + std::shared_ptr>> m_spCommands; }; } } diff --git a/src/CalcViewModel/HistoryViewModel.cpp b/src/CalcViewModel/HistoryViewModel.cpp index e3cda23e..8235c93a 100644 --- a/src/CalcViewModel/HistoryViewModel.cpp +++ b/src/CalcViewModel/HistoryViewModel.cpp @@ -6,6 +6,7 @@ #include "Common/TraceLogger.h" #include "Common/LocalizationStringUtil.h" #include "Common/LocalizationSettings.h" +#include "StandardCalculatorViewModel.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; @@ -20,26 +21,22 @@ using namespace Windows::Security::Cryptography; using namespace Windows::Foundation::Collections; static StringReference HistoryVectorLengthKey{ L"HistoryVectorLength" }; +static StringReference ItemsSizeKey{ L"ItemsCount" }; -namespace CalculatorApp::ViewModel::HistoryResourceKeys +namespace HistoryResourceKeys { StringReference HistoryCleared(L"HistoryList_Cleared"); + StringReference HistorySlotCleared(L"Format_HistorySlotCleared"); } -HistoryViewModel::HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager) : - m_calculatorManager(calculatorManager), - m_localizedHistoryCleared(nullptr) +HistoryViewModel::HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager) + : m_calculatorManager(calculatorManager) + , m_localizedHistoryCleared(nullptr) + , m_localizedHistorySlotCleared(nullptr) { AreHistoryShortcutsEnabled = true; - Items = ref new Platform::Collections::Vector(); - ItemSize = 0; -} - -void HistoryViewModel::RestoreCompleteHistory() -{ - RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_STD); - RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_SCI); + Items = ref new Platform::Collections::Vector(); } // this will reload Items with the history list based on current mode @@ -47,11 +44,11 @@ void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode) { if (currentMode == ViewMode::Standard) { - m_currentMode = CalculationManager::CALCULATOR_MODE::CM_STD; + m_currentMode = CalculationManager::CalculatorMode::Standard; } else if (currentMode == ViewMode::Scientific) { - m_currentMode = CalculationManager::CALCULATOR_MODE::CM_SCI; + m_currentMode = CalculationManager::CalculatorMode::Scientific; } else { @@ -59,7 +56,7 @@ void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode) } auto historyListModel = m_calculatorManager->GetHistoryItems(m_currentMode); - auto historyListVM = ref new Platform::Collections::Vector(); + auto historyListVM = ref new Platform::Collections::Vector(); const auto& localizer = LocalizationSettings::GetInstance(); if (historyListModel.size() > 0) { @@ -70,15 +67,17 @@ void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode) localizer.LocalizeDisplayValue(&expression); localizer.LocalizeDisplayValue(&result); - auto item = ref new HistoryItemViewModel(ref new Platform::String( expression.c_str()), + auto item = ref new HistoryItemViewModel( + ref new Platform::String(expression.c_str()), ref new Platform::String(result.c_str()), - (*ritr)->historyItemVector.spTokens, (*ritr)->historyItemVector.spCommands); + (*ritr)->historyItemVector.spTokens, + (*ritr)->historyItemVector.spCommands); historyListVM->Append(item); } } Items = historyListVM; - UpdateItemSize(); + RaisePropertyChanged(ItemsSizeKey); } void HistoryViewModel::OnHistoryItemAdded(_In_ unsigned int addedItemIndex) @@ -89,284 +88,84 @@ void HistoryViewModel::OnHistoryItemAdded(_In_ unsigned int addedItemIndex) wstring result = newItem->historyItemVector.result; localizer.LocalizeDisplayValue(&expression); localizer.LocalizeDisplayValue(&result); - auto item = ref new HistoryItemViewModel(ref new Platform::String(expression.c_str()), - ref new Platform::String(result.c_str()), - newItem->historyItemVector.spTokens, newItem->historyItemVector.spCommands ); + auto item = ref new HistoryItemViewModel( + ref new Platform::String(expression.c_str()), + ref new Platform::String(result.c_str()), + newItem->historyItemVector.spTokens, + newItem->historyItemVector.spCommands); // check if we have not hit the max items if (Items->Size >= m_calculatorManager->MaxHistorySize()) { // this means the item already exists - Items->RemoveAt(Items->Size -1); + Items->RemoveAt(Items->Size - 1); } assert(addedItemIndex <= m_calculatorManager->MaxHistorySize() && addedItemIndex >= 0); Items->InsertAt(0, item); - UpdateItemSize(); - SaveHistory(); + RaisePropertyChanged(ItemsSizeKey); } -void HistoryViewModel::SetCalculatorDisplay(CalculatorDisplay &calculatorDisplay) +void HistoryViewModel::SetCalculatorDisplay(CalculatorDisplay& calculatorDisplay) { WeakReference historyViewModel(this); calculatorDisplay.SetHistoryCallback(historyViewModel); } -void HistoryViewModel::ShowItem(_In_ HistoryItemViewModel^ e) +void HistoryViewModel::ShowItem(_In_ HistoryItemViewModel ^ e) { + unsigned int index; + Items->IndexOf(e, &index); + TraceLogger::GetInstance()->LogHistoryItemLoad((ViewMode)m_currentMode, Items->Size, (int)(index)); HistoryItemClicked(e); } -void HistoryViewModel::DeleteItem(_In_ HistoryItemViewModel^ e) +void HistoryViewModel::DeleteItem(_In_ HistoryItemViewModel ^ e) { uint32_t itemIndex; if (Items->IndexOf(e, &itemIndex)) { if (m_calculatorManager->RemoveHistoryItem(itemIndex)) { - // Keys for the history container are index based. - // SaveHistory() re-inserts the items anyway, so it's faster to just clear out the container. - CalculationManager::CALCULATOR_MODE currentMode = m_currentMode; - ApplicationDataContainer^ historyContainer = GetHistoryContainer(currentMode); - historyContainer->Values->Clear(); - Items->RemoveAt(itemIndex); - UpdateItemSize(); - SaveHistory(); + RaisePropertyChanged(ItemsSizeKey); } } + // Adding 1 to the history item index to provide 1-based numbering on announcements. + wstring localizedIndex = to_wstring(itemIndex + 1); + LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex); + m_localizedHistorySlotCleared = AppResourceProvider::GetInstance()->GetResourceString(HistoryResourceKeys::HistorySlotCleared); + String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedHistorySlotCleared, StringReference(localizedIndex.c_str())); + HistoryAnnouncement = CalculatorAnnouncement::GetHistorySlotClearedAnnouncement(announcement); } -void HistoryViewModel::OnHideCommand(_In_ Platform::Object^ e) +void HistoryViewModel::OnHideCommand(_In_ Platform::Object ^ e) { // added at VM layer so that the views do not have to individually raise events HideHistoryClicked(); } -void HistoryViewModel::OnClearCommand(_In_ Platform::Object^ e) +void HistoryViewModel::OnClearCommand(_In_ Platform::Object ^ e) { - TraceLogger::GetInstance().LogClearHistory(); - if (AreHistoryShortcutsEnabled == true) + if (AreHistoryShortcutsEnabled) { m_calculatorManager->ClearHistory(); if (Items->Size > 0) { - CalculationManager::CALCULATOR_MODE currentMode = m_currentMode; - ClearHistoryContainer(currentMode); Items->Clear(); - UpdateItemSize(); + RaisePropertyChanged(ItemsSizeKey); } - MakeHistoryClearedNarratorAnnouncement(HistoryResourceKeys::HistoryCleared, m_localizedHistoryCleared); - } -} - -// this method restores history vector per mode -void HistoryViewModel::RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer^ historyContainer = GetHistoryContainer(cMode); - std::shared_ptr>> historyVector = std::make_shared>>(); - auto historyVectorLength = static_cast(historyContainer->Values->Lookup(HistoryVectorLengthKey)); - bool failure = false; - - if (historyVectorLength > 0) - { - for (int i = 0; i < historyVectorLength; ++i) + if (m_localizedHistoryCleared == nullptr) { - try - { - // deserialize each item - auto item = DeserializeHistoryItem(i.ToString(), historyContainer); - std::shared_ptr Item = std::make_shared(item); - historyVector->push_back(Item); - } - catch (Platform::Exception^ e) - { - failure = true; - break; - } - } - - if (!failure) - { - // if task has been cancelled set history to 0 - m_calculatorManager->SetHistory(cMode, *historyVector); - - // update length once again for consistency between stored number of items and length - UpdateHistoryVectorLength(static_cast(historyVector->size()), cMode); - } - else - { - // in case of failure do not show any item - UpdateHistoryVectorLength(0, cMode); + m_localizedHistoryCleared = AppResourceProvider::GetInstance()->GetResourceString(HistoryResourceKeys::HistoryCleared); } + HistoryAnnouncement = CalculatorAnnouncement::GetHistoryClearedAnnouncement(m_localizedHistoryCleared); } } -Platform::String^ HistoryViewModel::GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode) +unsigned long long HistoryViewModel::GetMaxItemSize() { - Platform::ValueType^ modeValue = static_cast(cMode); - return Platform::String::Concat(modeValue->ToString(), L"_History"); -} - -ApplicationDataContainer^ HistoryViewModel::GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; - ApplicationDataContainer^ historyContainer; - - // naming container based on mode - Platform::String^ historyContainerKey = GetHistoryContainerKey(cMode); - - if (localSettings->Containers->HasKey(historyContainerKey)) - { - historyContainer = localSettings->Containers->Lookup(historyContainerKey); - } - else - { - // create container for adding data - historyContainer = localSettings->CreateContainer(historyContainerKey, ApplicationDataCreateDisposition::Always); - int initialHistoryVectorLength = 0; - historyContainer->Values->Insert(HistoryVectorLengthKey, initialHistoryVectorLength); - } - - return historyContainer; -} - -void HistoryViewModel::ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; - localSettings->DeleteContainer(GetHistoryContainerKey(cMode)); -} - -// this method will be used to update the history item length -void HistoryViewModel::UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer^ historyContainer = GetHistoryContainer(cMode); - historyContainer->Values->Remove(HistoryVectorLengthKey); - historyContainer->Values->Insert(HistoryVectorLengthKey, newValue); -} - -void HistoryViewModel::ClearHistory() -{ - ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_STD); - ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_SCI); -} - -void HistoryViewModel::SaveHistory() -{ - ApplicationDataContainer^ historyContainer = GetHistoryContainer(m_currentMode); - auto currentHistoryVector = m_calculatorManager->GetHistoryItems(m_currentMode); - bool failure = false; - int index = 0; - Platform::String^ serializedHistoryItem; - - for (auto iter = currentHistoryVector.begin(); iter != currentHistoryVector.end(); ++iter) - { - try - { - serializedHistoryItem = SerializeHistoryItem(*iter); - historyContainer->Values->Insert(index.ToString(), serializedHistoryItem); - } - catch (Platform::Exception^) - { - failure = true; - break; - } - - ++index; - } - - if (!failure) - { - // insertion is successful - UpdateHistoryVectorLength(static_cast(currentHistoryVector.size()), m_currentMode); - } - else - { - UpdateHistoryVectorLength(0, m_currentMode); - } -} - -// this serializes a history item into a base64 encoded string -Platform::String^ HistoryViewModel::SerializeHistoryItem(_In_ std::shared_ptr const &item) -{ - DataWriter^ writer = ref new DataWriter(); - auto expr = item->historyItemVector.expression; - auto result = item->historyItemVector.result; - auto platformExpr = ref new Platform::String(expr.c_str()); - writer->WriteUInt32(writer->MeasureString(platformExpr)); - writer->WriteString(platformExpr); - auto platformResult = ref new Platform::String(result.c_str()); - writer->WriteUInt32(writer->MeasureString(platformResult)); - writer->WriteString(platformResult); - - Utils::SerializeCommandsAndTokens(item->historyItemVector.spTokens, item->historyItemVector.spCommands, writer); - - IBuffer^ buffer = writer->DetachBuffer(); - if (buffer == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - return CryptographicBuffer::EncodeToBase64String(buffer); -} - -CalculationManager::HISTORYITEM HistoryViewModel::DeserializeHistoryItem(_In_ Platform::String^ historyItemKey, _In_ ApplicationDataContainer^ historyContainer) -{ - CalculationManager::HISTORYITEM historyItem; - if (historyContainer->Values->HasKey(historyItemKey)) - { - Object^ historyItemValues = historyContainer->Values->Lookup(historyItemKey); - - if (historyItemValues == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - String^ historyData = safe_cast(historyItemValues); - IBuffer^ buffer = CryptographicBuffer::DecodeFromBase64String(historyData); - - if (buffer == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - DataReader^ reader = DataReader::FromBuffer(buffer); - auto exprLen = reader->ReadUInt32(); - auto expression = reader->ReadString(exprLen); - historyItem.historyItemVector.expression = expression->Data(); - - auto resultLen = reader->ReadUInt32(); - auto result = reader->ReadString(resultLen); - historyItem.historyItemVector.result = result->Data(); - - historyItem.historyItemVector.spCommands = Utils::DeserializeCommands(reader); - historyItem.historyItemVector.spTokens = Utils::DeserializeTokens(reader); - } - else - { - throw ref new Platform::Exception(E_ACCESSDENIED, ref new Platform::String(L"History Item not found")); - } - return historyItem; -} - -bool HistoryViewModel::IsValid(_In_ CalculationManager::HISTORYITEM item) -{ - return (!item.historyItemVector.expression.empty() && - !item.historyItemVector.result.empty() && - (bool)item.historyItemVector.spCommands && - (bool)item.historyItemVector.spTokens); -} - -void HistoryViewModel::UpdateItemSize() -{ - ItemSize = Items->Size; -} - -void HistoryViewModel::MakeHistoryClearedNarratorAnnouncement(String^ resourceKey, String^& formatVariable) -{ - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(resourceKey, formatVariable); - - HistoryAnnouncement = CalculatorAnnouncement::GetHistoryClearedAnnouncement(announcement); + return static_cast(m_calculatorManager->MaxHistorySize()); } diff --git a/src/CalcViewModel/HistoryViewModel.h b/src/CalcViewModel/HistoryViewModel.h index 6913fa83..d7c7874e 100644 --- a/src/CalcViewModel/HistoryViewModel.h +++ b/src/CalcViewModel/HistoryViewModel.h @@ -15,63 +15,53 @@ namespace CalculatorApp namespace ViewModel { - public delegate void HideHistoryClickedHandler(); - public delegate void HistoryItemClickedHandler(CalculatorApp::ViewModel::HistoryItemViewModel^ e); + ref class StandardCalculatorViewModel; - [Windows::UI::Xaml::Data::Bindable] - public ref class HistoryViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + public + delegate void HideHistoryClickedHandler(); + public + delegate void HistoryItemClickedHandler(CalculatorApp::ViewModel::HistoryItemViewModel ^ e); + + [Windows::UI::Xaml::Data::Bindable] public ref class HistoryViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - public: OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_RW(int, ItemSize); - OBSERVABLE_PROPERTY_RW(Windows::UI::Xaml::Interop::IBindableObservableVector^, Items); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Items); OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); - OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, HistoryAnnouncement); + OBSERVABLE_PROPERTY_R(CalculatorApp::Common::Automation::NarratorAnnouncement ^, HistoryAnnouncement); + property int ItemsCount + { + int get() + { + return Items->Size; + } + } void OnHistoryItemAdded(_In_ unsigned int addedItemIndex); COMMAND_FOR_METHOD(HideCommand, HistoryViewModel::OnHideCommand); - void OnHideCommand(_In_ Platform::Object^ e); + void OnHideCommand(_In_ Platform::Object ^ e); COMMAND_FOR_METHOD(ClearCommand, HistoryViewModel::OnClearCommand); - void OnClearCommand(_In_ Platform::Object^ e); + void OnClearCommand(_In_ Platform::Object ^ e); // events that are created - event HideHistoryClickedHandler^ HideHistoryClicked; - event HistoryItemClickedHandler^ HistoryItemClicked; - void ShowItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel^ e); - void ClearHistory(); - void RestoreCompleteHistory(); + event HideHistoryClickedHandler ^ HideHistoryClicked; + event HistoryItemClickedHandler ^ HistoryItemClicked; + void ShowItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel ^ e); - internal: - HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager); - void SetCalculatorDisplay(CalculatorDisplay &calculatorDisplay); + internal : HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager); + void SetCalculatorDisplay(CalculatorDisplay& calculatorDisplay); void ReloadHistory(_In_ CalculatorApp::Common::ViewMode currentMode); + unsigned long long GetMaxItemSize(); - void DeleteItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel^ e); - - // store history in app data functions - Platform::String^ SerializeHistoryItem(_In_ std::shared_ptr const &item); - void SaveHistory(); + void DeleteItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel ^ e); private: CalculationManager::CalculatorManager* const m_calculatorManager; CalculatorDisplay m_calculatorDisplay; - CalculationManager::CALCULATOR_MODE m_currentMode; - Platform::String^ m_localizedHistoryCleared; - - void RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode); - CalculationManager::HISTORYITEM DeserializeHistoryItem(_In_ Platform::String^ historyItemKey, _In_ Windows::Storage::ApplicationDataContainer^ historyContainer); - Windows::Storage::ApplicationDataContainer^ GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode); - Platform::String^ GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode); - void ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode); - void UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode); - bool IsValid(_In_ CalculationManager::HISTORYITEM item); - - void MakeHistoryClearedNarratorAnnouncement(Platform::String^ resourceKey, Platform::String^& formatVariable); - - friend class CalculatorDisplay; - void UpdateItemSize(); + CalculationManager::CalculatorMode m_currentMode; + Platform::String ^ m_localizedHistoryCleared; + Platform::String ^ m_localizedHistorySlotCleared; }; } } diff --git a/src/CalcViewModel/MemoryItemViewModel.h b/src/CalcViewModel/MemoryItemViewModel.h index 948053e6..fc5dce0c 100644 --- a/src/CalcViewModel/MemoryItemViewModel.h +++ b/src/CalcViewModel/MemoryItemViewModel.h @@ -14,46 +14,24 @@ namespace CalculatorApp /// /// Model representation of a single item in the Memory list /// - [Windows::UI::Xaml::Data::Bindable] - public ref class MemoryItemViewModel sealed : - public Windows::UI::Xaml::Data::INotifyPropertyChanged, - Windows::UI::Xaml::Data::ICustomPropertyProvider + [Windows::UI::Xaml::Data::Bindable] public ref class MemoryItemViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: - MemoryItemViewModel(StandardCalculatorViewModel^ calcVM) : m_Position(-1), m_calcVM(calcVM) {} + MemoryItemViewModel(StandardCalculatorViewModel ^ calcVM) + : m_Position(-1) + , m_calcVM(calcVM) + { + } OBSERVABLE_OBJECT(); OBSERVABLE_PROPERTY_RW(int, Position); - OBSERVABLE_PROPERTY_RW(Platform::String^, Value); - - virtual Windows::UI::Xaml::Data::ICustomProperty^ GetCustomProperty(Platform::String^ name) - { - return nullptr; - } - - virtual Windows::UI::Xaml::Data::ICustomProperty^ GetIndexedProperty(Platform::String^ name, Windows::UI::Xaml::Interop::TypeName type) - { - return nullptr; - } - - virtual property Windows::UI::Xaml::Interop::TypeName Type - { - Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - virtual Platform::String^ GetStringRepresentation() - { - return Value; - } + OBSERVABLE_PROPERTY_RW(Platform::String ^, Value); void Clear(); void MemoryAdd(); void MemorySubtract(); private: - StandardCalculatorViewModel^ m_calcVM; + StandardCalculatorViewModel ^ m_calcVM; }; } } diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index 7f4b6332..56f2bda4 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -14,6 +14,7 @@ using namespace CalculatorApp::Common; using namespace CalculatorApp::Common::Automation; using namespace CalculatorApp::ViewModel; using namespace CalculationManager; +using namespace concurrency; using namespace Platform; using namespace Platform::Collections; using namespace std; @@ -25,90 +26,89 @@ using namespace Windows::UI::Popups; using namespace Windows::Storage::Streams; using namespace Windows::Foundation::Collections; using namespace Utils; +using namespace concurrency; constexpr int StandardModePrecision = 16; constexpr int ScientificModePrecision = 32; constexpr int ProgrammerModePrecision = 64; -namespace CalculatorApp::ViewModel +namespace { - namespace CalculatorViewModelProperties - { - StringReference IsMemoryEmpty(L"IsMemoryEmpty"); - StringReference IsScientific(L"IsScientific"); - StringReference IsStandard(L"IsStandard"); - StringReference IsProgrammer(L"IsProgrammer"); - StringReference DisplayValue(L"DisplayValue"); - StringReference IsInError(L"IsInError"); - StringReference BinaryDisplayValue(L"BinaryDisplayValue"); - StringReference OpenParenthesisCount(L"OpenParenthesisCount"); - } - - namespace CalculatorResourceKeys - { - StringReference CalculatorExpression(L"Format_CalculatorExpression"); - StringReference CalculatorResults(L"Format_CalculatorResults"); - StringReference CalculatorResults_DecimalSeparator_Announced(L"Format_CalculatorResults_Decimal"); - StringReference HexButton(L"Format_HexButtonValue"); - StringReference DecButton(L"Format_DecButtonValue"); - StringReference OctButton(L"Format_OctButtonValue"); - StringReference BinButton(L"Format_BinButtonValue"); - StringReference LeftParenthesisAutomationFormat(L"Format_OpenParenthesisAutomationNamePrefix"); - StringReference OpenParenthesisCountAutomationFormat(L"Format_OpenParenthesisCountAutomationNamePrefix"); - StringReference NoParenthesisAdded(L"NoRightParenthesisAdded_Announcement"); - StringReference MaxDigitsReachedFormat(L"Format_MaxDigitsReached"); - StringReference ButtonPressFeedbackFormat(L"Format_ButtonPressAuditoryFeedback"); - StringReference MemorySave(L"Format_MemorySave"); - StringReference MemoryItemChanged(L"Format_MemorySlotChanged"); - StringReference MemoryItemCleared(L"Format_MemorySlotCleared"); - StringReference MemoryCleared(L"Memory_Cleared"); - StringReference DisplayCopied(L"Display_Copied"); - } + StringReference IsStandardPropertyName(L"IsStandard"); + StringReference IsScientificPropertyName(L"IsScientific"); + StringReference IsProgrammerPropertyName(L"IsProgrammer"); + StringReference IsAlwaysOnTopPropertyName(L"IsAlwaysOnTop"); + StringReference DisplayValuePropertyName(L"DisplayValue"); + StringReference CalculationResultAutomationNamePropertyName(L"CalculationResultAutomationName"); + StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked"); + StringReference CalcAlwaysOnTop(L"CalcAlwaysOnTop"); + StringReference CalcBackToFullView(L"CalcBackToFullView"); } -StandardCalculatorViewModel::StandardCalculatorViewModel() : - m_DisplayValue(L"0"), - m_DecimalDisplayValue(L"0"), - m_HexDisplayValue(L"0"), - m_BinaryDisplayValue(L"0"), - m_OctalDisplayValue(L"0"), - m_standardCalculatorManager(&m_calculatorDisplay, &m_resourceProvider), - m_MemorizedNumbers(ref new Vector()), - m_IsMemoryEmpty(true), - m_IsFToEChecked(false), - m_isShiftChecked(false), - m_IsShiftProgrammerChecked(false), - m_IsQwordEnabled(true), - m_IsDwordEnabled(true), - m_IsWordEnabled(true), - m_IsByteEnabled(true), - m_isBitFlipChecked(false), - m_isBinaryBitFlippingEnabled(false), - m_CurrentRadixType(RADIX_TYPE::DEC_RADIX), - m_CurrentAngleType(NumbersAndOperatorsEnum::Degree), - m_OpenParenthesisCount(L""), - m_Announcement(nullptr), - m_feedbackForButtonPress(nullptr), - m_isRtlLanguage(false), - m_localizedMaxDigitsReachedAutomationFormat(nullptr), - m_localizedButtonPressFeedbackAutomationFormat(nullptr), - m_localizedMemorySavedAutomationFormat(nullptr), - m_localizedMemoryItemChangedAutomationFormat(nullptr), - m_localizedMemoryItemClearedAutomationFormat(nullptr), - m_localizedMemoryCleared(nullptr), - m_localizedOpenParenthesisCountChangedAutomationFormat(nullptr), - m_localizedNoRightParenthesisAddedFormat(nullptr) +namespace CalculatorResourceKeys +{ + StringReference CalculatorExpression(L"Format_CalculatorExpression"); + StringReference CalculatorResults(L"Format_CalculatorResults"); + StringReference CalculatorResults_DecimalSeparator_Announced(L"Format_CalculatorResults_Decimal"); + StringReference HexButton(L"Format_HexButtonValue"); + StringReference DecButton(L"Format_DecButtonValue"); + StringReference OctButton(L"Format_OctButtonValue"); + StringReference BinButton(L"Format_BinButtonValue"); + StringReference OpenParenthesisCountAutomationFormat(L"Format_OpenParenthesisCountAutomationNamePrefix"); + StringReference NoParenthesisAdded(L"NoRightParenthesisAdded_Announcement"); + StringReference MaxDigitsReachedFormat(L"Format_MaxDigitsReached"); + StringReference ButtonPressFeedbackFormat(L"Format_ButtonPressAuditoryFeedback"); + StringReference MemorySave(L"Format_MemorySave"); + StringReference MemoryItemChanged(L"Format_MemorySlotChanged"); + StringReference MemoryItemCleared(L"Format_MemorySlotCleared"); + StringReference MemoryCleared(L"Memory_Cleared"); + StringReference DisplayCopied(L"Display_Copied"); +} + +StandardCalculatorViewModel::StandardCalculatorViewModel() + : m_DisplayValue(L"0") + , m_DecimalDisplayValue(L"0") + , m_HexDisplayValue(L"0") + , m_BinaryDisplayValue(L"0") + , m_OctalDisplayValue(L"0") + , m_BinaryDigits(ref new Vector(64, false)) + , m_standardCalculatorManager(&m_calculatorDisplay, &m_resourceProvider) + , m_ExpressionTokens(ref new Vector()) + , m_MemorizedNumbers(ref new Vector()) + , m_IsMemoryEmpty(true) + , m_IsFToEChecked(false) + , m_IsShiftProgrammerChecked(false) + , m_valueBitLength(BitLength::BitLengthQWord) + , m_isBitFlipChecked(false) + , m_IsBinaryBitFlippingEnabled(false) + , m_CurrentRadixType(NumberBase::DecBase) + , m_CurrentAngleType(NumbersAndOperatorsEnum::Degree) + , m_Announcement(nullptr) + , m_OpenParenthesisCount(0) + , m_feedbackForButtonPress(nullptr) + , m_isRtlLanguage(false) + , m_localizedMaxDigitsReachedAutomationFormat(nullptr) + , m_localizedButtonPressFeedbackAutomationFormat(nullptr) + , m_localizedMemorySavedAutomationFormat(nullptr) + , m_localizedMemoryItemChangedAutomationFormat(nullptr) + , m_localizedMemoryItemClearedAutomationFormat(nullptr) + , m_localizedMemoryCleared(nullptr) + , m_localizedOpenParenthesisCountChangedAutomationFormat(nullptr) + , m_localizedNoRightParenthesisAddedFormat(nullptr) + , m_TokenPosition(-1) + , m_isLastOperationHistoryLoad(false) { WeakReference calculatorViewModel(this); + auto appResourceProvider = AppResourceProvider::GetInstance(); m_calculatorDisplay.SetCallback(calculatorViewModel); - m_expressionAutomationNameFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorExpression); - m_localizedCalculationResultAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorResults); - m_localizedCalculationResultDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::CalculatorResults_DecimalSeparator_Announced); - m_localizedHexaDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::HexButton); - m_localizedDecimalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::DecButton); - m_localizedOctalAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::OctButton); - m_localizedBinaryAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::BinButton); - m_leftParenthesisAutomationFormat = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::LeftParenthesisAutomationFormat); + m_expressionAutomationNameFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorExpression); + m_localizedCalculationResultAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorResults); + m_localizedCalculationResultDecimalAutomationFormat = + appResourceProvider->GetResourceString(CalculatorResourceKeys::CalculatorResults_DecimalSeparator_Announced); + m_localizedHexaDecimalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::HexButton); + m_localizedDecimalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::DecButton); + m_localizedOctalAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::OctButton); + m_localizedBinaryAutomationFormat = appResourceProvider->GetResourceString(CalculatorResourceKeys::BinButton); // Initialize the Automation Name CalculationResultAutomationName = GetLocalizedStringFormat(m_localizedCalculationResultAutomationFormat, m_DisplayValue); @@ -132,39 +132,26 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() : IsOperandEnabled = true; IsNegateEnabled = true; IsDecimalEnabled = true; - AreHistoryShortcutsEnabled = true; - AreProgrammerRadixOperatorsEnabled = false; - - m_tokenPosition = -1; - m_isLastOperationHistoryLoad = false; + AreProgrammerRadixOperatorsVisible = false; } -String^ StandardCalculatorViewModel::LocalizeDisplayValue(_In_ wstring const &displayValue, _In_ bool isError) +String ^ StandardCalculatorViewModel::LocalizeDisplayValue(_In_ wstring const& displayValue) { wstring result(displayValue); - LocalizationSettings::GetInstance().LocalizeDisplayValue(&result); - - // WINBLUE: 440747 - In BiDi languages, error messages need to be wrapped in LRE/PDF - if (isError && m_isRtlLanguage) - { - result.insert(result.begin(), Utils::LRE); - result.push_back(Utils::PDF); - } - return ref new Platform::String(result.c_str()); } -String^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring const &displayValue, _In_ String^ localizedDisplayValue, _In_ bool isError) +String ^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring const& displayValue, _In_ String ^ localizedDisplayValue) { - String^ localizedValue = localizedDisplayValue; - String^ automationFormat = m_localizedCalculationResultAutomationFormat; + String ^ localizedValue = localizedDisplayValue; + String ^ automationFormat = m_localizedCalculationResultAutomationFormat; // The narrator doesn't read the decimalSeparator if it's the last character if (Utils::IsLastCharacterTarget(displayValue, m_decimalSeparator)) { // remove the decimal separator, to avoid a long pause between words - localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.length() - 1), isError); + localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.length() - 1)); // Use a format which has a word in the decimal separator's place // "The Display is 10 point" @@ -172,7 +159,7 @@ String^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring } // In Programmer modes using non-base10, we want the strings to be read as literal digits. - if (IsProgrammer && CurrentRadixType != RADIX_TYPE::DEC_RADIX) + if (IsProgrammer && CurrentRadixType != NumberBase::DecBase) { localizedValue = GetNarratorStringReadRawNumbers(localizedValue); } @@ -180,34 +167,38 @@ String^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring return GetLocalizedStringFormat(automationFormat, localizedValue); } -String^ StandardCalculatorViewModel::GetNarratorStringReadRawNumbers(_In_ String^ localizedDisplayValue) +String ^ StandardCalculatorViewModel::GetNarratorStringReadRawNumbers(_In_ String ^ localizedDisplayValue) { - wstringstream wss; - auto& locSettings = LocalizationSettings::GetInstance(); + wstring ws; + const auto& locSettings = LocalizationSettings::GetInstance(); // Insert a space after each digit in the string, to force Narrator to read them as separate numbers. - wstring wstrValue(localizedDisplayValue->Data()); - for (wchar_t& c : wstrValue) + for (const wchar_t& c : localizedDisplayValue) { - wss << c; + ws += c; if (locSettings.IsLocalizedHexDigit(c)) { - wss << L' '; + ws += L' '; } } - return ref new String(wss.str().c_str()); + return ref new String(ws.c_str()); } -void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ wstring const &displayStringValue, _In_ bool isError) +void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ String ^ displayStringValue, _In_ bool isError) { - String^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue, isError); + String ^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue->Data()); // Set this variable before the DisplayValue is modified, Otherwise the DisplayValue will // not match what the narrator is saying - m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue, localizedDisplayStringValue, isError); + m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue->Data(), localizedDisplayStringValue); - DisplayValue = localizedDisplayStringValue; + AreAlwaysOnTopResultsUpdated = false; + if (DisplayValue != localizedDisplayStringValue) + { + DisplayValue = localizedDisplayStringValue; + AreAlwaysOnTopResultsUpdated = true; + } IsInError = isError; @@ -222,25 +213,32 @@ void StandardCalculatorViewModel::DisplayPasteError() m_standardCalculatorManager.DisplayPasteError(); } -void StandardCalculatorViewModel::SetParenthesisCount(_In_ const wstring& parenthesisCount) +void StandardCalculatorViewModel::SetParenthesisCount(_In_ unsigned int parenthesisCount) { + if (m_OpenParenthesisCount == parenthesisCount) + { + return; + } + + OpenParenthesisCount = parenthesisCount; if (IsProgrammer || IsScientific) { - OpenParenthesisCount = ref new String(parenthesisCount.c_str()); - RaisePropertyChanged("LeftParenthesisAutomationName"); + SetOpenParenthesisCountNarratorAnnouncement(); } } void StandardCalculatorViewModel::SetOpenParenthesisCountNarratorAnnouncement() { - String^ parenthesisCount = ((m_OpenParenthesisCount == nullptr) ? "0" : m_OpenParenthesisCount); - wstring localizedParenthesisCount = parenthesisCount->Data(); + wstring localizedParenthesisCount = to_wstring(m_OpenParenthesisCount).c_str(); LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount); - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::OpenParenthesisCountAutomationFormat, - m_localizedOpenParenthesisCountChangedAutomationFormat, - localizedParenthesisCount.c_str()); + if (m_localizedOpenParenthesisCountChangedAutomationFormat == nullptr) + { + m_localizedOpenParenthesisCountChangedAutomationFormat = + AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::OpenParenthesisCountAutomationFormat); + } + String ^ announcement = + LocalizationStringUtil::GetLocalizedString(m_localizedOpenParenthesisCountChangedAutomationFormat, StringReference(localizedParenthesisCount.c_str())); Announcement = CalculatorAnnouncement::GetOpenParenthesisCountChangedAnnouncement(announcement); } @@ -252,11 +250,12 @@ void StandardCalculatorViewModel::OnNoRightParenAdded() void StandardCalculatorViewModel::SetNoParenAddedNarratorAnnouncement() { - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::NoParenthesisAdded, - m_localizedNoRightParenthesisAddedFormat); + if (m_localizedNoRightParenthesisAddedFormat == nullptr) + { + m_localizedNoRightParenthesisAddedFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::NoParenthesisAdded); + } - Announcement = CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(announcement); + Announcement = CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(m_localizedNoRightParenthesisAddedFormat); } void StandardCalculatorViewModel::DisableButtons(CommandType selectedExpressionCommandType) @@ -287,16 +286,9 @@ void StandardCalculatorViewModel::DisableButtons(CommandType selectedExpressionC } } -String ^ StandardCalculatorViewModel::GetLeftParenthesisAutomationName() -{ - String^ parenthesisCount = ((m_OpenParenthesisCount == nullptr) ? "0" : m_OpenParenthesisCount); - wstring localizedParenthesisCount = std::wstring(parenthesisCount->Data()); - LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedParenthesisCount); - - return GetLocalizedStringFormat(m_leftParenthesisAutomationFormat, ref new String(localizedParenthesisCount.c_str())); -} - -void StandardCalculatorViewModel::SetExpressionDisplay(_Inout_ shared_ptr>> const &tokens, _Inout_ shared_ptr>> const &commands) +void StandardCalculatorViewModel::SetExpressionDisplay( + _Inout_ shared_ptr>> const& tokens, + _Inout_ shared_ptr>> const& commands) { m_tokens = tokens; m_commands = commands; @@ -310,10 +302,12 @@ void StandardCalculatorViewModel::SetExpressionDisplay(_Inout_ shared_ptr>> const &tokens, _Inout_ shared_ptr>> const &commands) +void StandardCalculatorViewModel::SetHistoryExpressionDisplay( + _Inout_ shared_ptr>> const& tokens, + _Inout_ shared_ptr>> const& commands) { - m_tokens = make_shared>>(*tokens); - m_commands = make_shared>>(*commands); + m_tokens = make_shared>>(*tokens); + m_commands = make_shared>>(*commands); IsEditingEnabled = false; // Setting the History Item Load Mode so that UI does not get updated with recalculation of every token @@ -323,68 +317,71 @@ void StandardCalculatorViewModel::SetHistoryExpressionDisplay(_Inout_ shared_ptr m_isLastOperationHistoryLoad = true; } -void StandardCalculatorViewModel::SetTokens(_Inout_ shared_ptr>> const &tokens) +void StandardCalculatorViewModel::SetTokens(_Inout_ shared_ptr>> const& tokens) { AreTokensUpdated = false; - if (m_ExpressionTokens == nullptr) - { - m_ExpressionTokens = ref new Vector(); - } - else + const size_t nTokens = tokens->size(); + + if (nTokens == 0) { m_ExpressionTokens->Clear(); + return; } - unsigned int nTokens = 0; - tokens->GetSize(&nTokens); - pair currentToken; const auto& localizer = LocalizationSettings::GetInstance(); + const wstring separator = L" "; for (unsigned int i = 0; i < nTokens; ++i) { - if (SUCCEEDED(tokens->GetAt(i, ¤tToken))) + auto currentToken = (*tokens)[i]; + + Common::TokenType type; + bool isEditable = currentToken.second != -1; + localizer.LocalizeDisplayValue(&(currentToken.first)); + + if (!isEditable) { - Common::TokenType type; - const wstring separator = L" "; - bool isEditable = (currentToken.second == -1) ? false : true; - localizer.LocalizeDisplayValue(&(currentToken.first)); + type = currentToken.first == separator ? TokenType::Separator : TokenType::Operator; + } + else + { + const shared_ptr& command = m_commands->at(currentToken.second); + type = command->GetCommandType() == CommandType::OperandCommand ? TokenType::Operand : TokenType::Operator; + } - if (!isEditable) + auto currentTokenString = StringReference(currentToken.first.c_str()); + if (i < m_ExpressionTokens->Size) + { + auto existingItem = m_ExpressionTokens->GetAt(i); + if (type == existingItem->Type && existingItem->Token->Equals(currentTokenString)) { - if (currentToken.first == separator) - { - type = TokenType::Separator; - } - else - { - type = TokenType::Operator; - } + existingItem->TokenPosition = i; + existingItem->IsTokenEditable = isEditable; + existingItem->CommandIndex = 0; } - else { - shared_ptr command; - IFTPlatformException(m_commands->GetAt(static_cast(currentToken.second), &command)); - - if (command->GetCommandType() == CommandType::OperandCommand) - { - type = TokenType::Operand; - } - else - { - type = TokenType::Operator; - } + auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type); + m_ExpressionTokens->InsertAt(i, expressionToken); } - DisplayExpressionToken^ expressionToken = ref new DisplayExpressionToken(ref new String(currentToken.first.c_str()), i, isEditable, type); + } + else + { + auto expressionToken = ref new DisplayExpressionToken(currentTokenString, i, isEditable, type); m_ExpressionTokens->Append(expressionToken); } } + + while (m_ExpressionTokens->Size != nTokens) + { + m_ExpressionTokens->RemoveAtEnd(); + } } -String^ StandardCalculatorViewModel::GetCalculatorExpressionAutomationName() +String ^ StandardCalculatorViewModel::GetCalculatorExpressionAutomationName() { - String^ expression = L""; + String ^ expression = L""; for (auto&& token : m_ExpressionTokens) { expression += LocalizationService::GetNarratorReadableToken(token->Token); @@ -409,13 +406,13 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector& new size_t newValuePosition = newMemorizedNumbers.size() - MemorizedNumbers->Size - 1; auto stringValue = newMemorizedNumbers.at(newValuePosition); - MemoryItemViewModel^ memorySlot = ref new MemoryItemViewModel(this); + MemoryItemViewModel ^ memorySlot = ref new MemoryItemViewModel(this); memorySlot->Position = 0; localizer.LocalizeDisplayValue(&stringValue); - memorySlot->Value = Utils::LRO + ref new String(stringValue.c_str()) + Utils::PDF; + memorySlot->Value = ref new String(stringValue.c_str()); MemorizedNumbers->InsertAt(0, memorySlot); - IsMemoryEmpty = false; + IsMemoryEmpty = IsAlwaysOnTop; // Update the slot position for the rest of the slots for (unsigned int i = 1; i < MemorizedNumbers->Size; i++) @@ -434,9 +431,8 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector& new // If the value is different, update the value if (MemorizedNumbers->GetAt(i)->Value != StringReference(newStringValue.c_str())) { - MemorizedNumbers->GetAt(i)->Value = Utils::LRO + ref new String(newStringValue.c_str()) + Utils::PDF; + MemorizedNumbers->GetAt(i)->Value = ref new String(newStringValue.c_str()); } - } } } @@ -448,7 +444,7 @@ void StandardCalculatorViewModel::FtoEButtonToggled() void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) { - DisplayExpressionToken^ displayExpressionToken = ExpressionTokens->GetAt(m_tokenPosition); + DisplayExpressionToken ^ displayExpressionToken = ExpressionTokens->GetAt(m_TokenPosition); if (displayExpressionToken == nullptr) { return; @@ -537,7 +533,7 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) { if (commandIndex == 0) { - delete [] temp; + delete[] temp; return; } @@ -558,7 +554,7 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) length = m_selectedExpressionLastData->Length() + 1; if (length > 50) { - delete [] temp; + delete[] temp; return; } for (; i < length; ++i) @@ -575,8 +571,8 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) } } - String^ updatedData = ref new String(temp); - UpdateOperand(m_tokenPosition, updatedData); + String ^ updatedData = ref new String(temp); + UpdateOperand(m_TokenPosition, updatedData); displayExpressionToken->Token = updatedData; IsOperandUpdatedUsingViewModel = true; displayExpressionToken->CommandIndex = commandIndex; @@ -584,10 +580,10 @@ void StandardCalculatorViewModel::HandleUpdatedOperandData(Command cmdenum) bool StandardCalculatorViewModel::IsOperator(Command cmdenum) { - if ((cmdenum == Command::Command0) || (cmdenum == Command::Command1) || (cmdenum == Command::Command2) || (cmdenum == Command::Command3) || (cmdenum == Command::Command4) || (cmdenum == Command::Command5) - || (cmdenum == Command::Command6) || (cmdenum == Command::Command7) || (cmdenum == Command::Command8) || (cmdenum == Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK) - || (cmdenum == Command::CommandEXP) || (cmdenum == Command::CommandFE) || (cmdenum == Command::ModeBasic) || (cmdenum == Command::ModeProgrammer) || (cmdenum == Command::ModeScientific) - || (cmdenum == Command::CommandINV) || (cmdenum == Command::CommandCENTR) || (cmdenum == Command::CommandDEG) || (cmdenum == Command::CommandRAD) || (cmdenum == Command::CommandGRAD) + if ((cmdenum >= Command::Command0 && cmdenum <= Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK) + || (cmdenum == Command::CommandEXP) || (cmdenum == Command::CommandFE) || (cmdenum == Command::ModeBasic) || (cmdenum == Command::ModeProgrammer) + || (cmdenum == Command::ModeScientific) || (cmdenum == Command::CommandINV) || (cmdenum == Command::CommandCENTR) || (cmdenum == Command::CommandDEG) + || (cmdenum == Command::CommandRAD) || (cmdenum == Command::CommandGRAD) || ((cmdenum >= Command::CommandBINEDITSTART) && (cmdenum <= Command::CommandBINEDITEND))) { return false; @@ -595,41 +591,35 @@ bool StandardCalculatorViewModel::IsOperator(Command cmdenum) return true; } -void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter) +void StandardCalculatorViewModel::OnButtonPressed(Object ^ parameter) { m_feedbackForButtonPress = CalculatorButtonPressedEventArgs::GetAuditoryFeedbackFromCommandParameter(parameter); NumbersAndOperatorsEnum numOpEnum = CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(parameter); Command cmdenum = ConvertToOperatorsEnum(numOpEnum); - TraceLogger::GetInstance().UpdateFunctionUsage((int)numOpEnum); - if (IsInError) { m_standardCalculatorManager.SendCommand(Command::CommandCLEAR); - if (!IsRecoverableCommand((int)numOpEnum)) + if (!IsRecoverableCommand(static_cast(numOpEnum))) { return; } } - if (IsEditingEnabled && - numOpEnum != NumbersAndOperatorsEnum::IsScientificMode && - numOpEnum != NumbersAndOperatorsEnum::IsStandardMode && - numOpEnum != NumbersAndOperatorsEnum::IsProgrammerMode && - numOpEnum != NumbersAndOperatorsEnum::FToE && - (numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) && (numOpEnum != NumbersAndOperatorsEnum::Grads)) + if (IsEditingEnabled && numOpEnum != NumbersAndOperatorsEnum::IsScientificMode && numOpEnum != NumbersAndOperatorsEnum::IsStandardMode + && numOpEnum != NumbersAndOperatorsEnum::IsProgrammerMode && numOpEnum != NumbersAndOperatorsEnum::FToE + && (numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) && (numOpEnum != NumbersAndOperatorsEnum::Grads)) { - if (!m_keyPressed) + if (!m_KeyPressed) { SaveEditedCommand(m_selectedExpressionToken->TokenPosition, cmdenum); } } else { - if (numOpEnum == NumbersAndOperatorsEnum::IsStandardMode || - numOpEnum == NumbersAndOperatorsEnum::IsScientificMode || - numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode) + if (numOpEnum == NumbersAndOperatorsEnum::IsStandardMode || numOpEnum == NumbersAndOperatorsEnum::IsScientificMode + || numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode) { IsEditingEnabled = false; } @@ -639,10 +629,8 @@ void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter) } else { - if (numOpEnum == NumbersAndOperatorsEnum::Clear || - numOpEnum == NumbersAndOperatorsEnum::ClearEntry || - numOpEnum == NumbersAndOperatorsEnum::IsStandardMode || - numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode) + if (numOpEnum == NumbersAndOperatorsEnum::Clear || numOpEnum == NumbersAndOperatorsEnum::ClearEntry + || numOpEnum == NumbersAndOperatorsEnum::IsStandardMode || numOpEnum == NumbersAndOperatorsEnum::IsProgrammerMode) { // On Clear('C') the F-E button needs to be UnChecked if it in Checked state. // Also, the Primary Display Value should not show in exponential format. @@ -658,8 +646,8 @@ void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter) { m_CurrentAngleType = numOpEnum; } - if ((cmdenum == Command::Command0) || (cmdenum == Command::Command1) || (cmdenum == Command::Command2) || (cmdenum == Command::Command3) || (cmdenum == Command::Command4) || (cmdenum == Command::Command5) - || (cmdenum == Command::Command6) || (cmdenum == Command::Command7) || (cmdenum == Command::Command8) || (cmdenum == Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK) || (cmdenum == Command::CommandEXP)) + if ((cmdenum >= Command::Command0 && cmdenum <= Command::Command9) || (cmdenum == Command::CommandPNT) || (cmdenum == Command::CommandBACK) + || (cmdenum == Command::CommandEXP)) { IsOperatorCommand = false; } @@ -668,72 +656,49 @@ void StandardCalculatorViewModel::OnButtonPressed(Object^ parameter) IsOperatorCommand = true; } - if (m_isLastOperationHistoryLoad && - ((numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) && (numOpEnum != NumbersAndOperatorsEnum::Grads))) + if (m_isLastOperationHistoryLoad + && ((numOpEnum != NumbersAndOperatorsEnum::Degree) && (numOpEnum != NumbersAndOperatorsEnum::Radians) + && (numOpEnum != NumbersAndOperatorsEnum::Grads))) { IsFToEEnabled = true; m_isLastOperationHistoryLoad = false; } + TraceLogger::GetInstance()->UpdateButtonUsage(numOpEnum, GetCalculatorMode()); m_standardCalculatorManager.SendCommand(cmdenum); } } } -int StandardCalculatorViewModel::GetBitLengthType() +RadixType StandardCalculatorViewModel::GetRadixTypeFromNumberBase(NumberBase base) { - if (IsQwordEnabled) + switch (base) { - return QwordType; - } - else if (IsDwordEnabled) - { - return DwordType; - } - else if (IsWordEnabled) - { - return WordType; - } - else - { - return ByteType; + case NumberBase::BinBase: + return RadixType::Binary; + case NumberBase::HexBase: + return RadixType::Hex; + case NumberBase::OctBase: + return RadixType::Octal; + default: + return RadixType::Decimal; } } -int StandardCalculatorViewModel::GetNumberBase() -{ - if (CurrentRadixType == HEX_RADIX) - { - return HexBase; - } - else if (CurrentRadixType == DEC_RADIX) - { - return DecBase; - } - else if (CurrentRadixType == OCT_RADIX) - { - return OctBase; - } - else - { - return BinBase; - } - -} - -void StandardCalculatorViewModel::OnCopyCommand(Object^ parameter) +void StandardCalculatorViewModel::OnCopyCommand(Object ^ parameter) { CopyPasteManager::CopyToClipboard(GetRawDisplayValue()); - String^ announcement = AppResourceProvider::GetInstance().GetResourceString(CalculatorResourceKeys::DisplayCopied); + String ^ announcement = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::DisplayCopied); Announcement = CalculatorAnnouncement::GetDisplayCopiedAnnouncement(announcement); } -void StandardCalculatorViewModel::OnPasteCommand(Object^ parameter) +void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter) { + auto that(this); ViewMode mode; - int NumberBase = -1; - int bitLengthType = -1; + BitLength bitLengthType = BitLength::BitLengthUnknown; + NumberBase numberBase = NumberBase::Unknown; if (IsScientific) { mode = ViewMode::Scientific; @@ -741,8 +706,8 @@ void StandardCalculatorViewModel::OnPasteCommand(Object^ parameter) else if (IsProgrammer) { mode = ViewMode::Programmer; - NumberBase = GetNumberBase(); - bitLengthType = GetBitLengthType(); + bitLengthType = m_valueBitLength; + numberBase = CurrentRadixType; } else { @@ -755,11 +720,8 @@ void StandardCalculatorViewModel::OnPasteCommand(Object^ parameter) } // Ensure that the paste happens on the UI thread - CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), NumberBase, bitLengthType).then( - [this, mode](String^ pastedString) - { - OnPaste(pastedString, mode); - }, concurrency::task_continuation_context::use_current()); + create_task(CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), numberBase, bitLengthType)) + .then([that, mode](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation) @@ -767,16 +729,16 @@ CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum( return safe_cast(operation); } -void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode) +void StandardCalculatorViewModel::OnPaste(String ^ pastedString) { // If pastedString is invalid("NoOp") then display pasteError else process the string - if (pastedString == StringReference(CopyPasteManager::PasteErrorString)) + if (CopyPasteManager::IsErrorMessage(pastedString)) { this->DisplayPasteError(); return; } - TraceLogger::GetInstance().LogValidInputPasted(mode); + TraceLogger::GetInstance()->LogInputPasted(GetCalculatorMode()); bool isFirstLegalChar = true; m_standardCalculatorManager.SendCommand(Command::CommandCENTR); bool sendNegate = false; @@ -792,9 +754,16 @@ void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode) while (it != pastedString->End()) { bool sendCommand = true; - bool canSendNegate = false; + auto buttonInfo = MapCharacterToButtonId(*it); - NumbersAndOperatorsEnum mappedNumOp = MapCharacterToButtonId(*it, canSendNegate); + NumbersAndOperatorsEnum mappedNumOp = buttonInfo.buttonId; + bool canSendNegate = buttonInfo.canSendNegate; + + if (mappedNumOp == NumbersAndOperatorsEnum::None) + { + ++it; + continue; + } if (isFirstLegalChar || isPreviousOperator) { @@ -817,86 +786,94 @@ void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode) } } - if (mappedNumOp != NumbersAndOperatorsEnum::None) + switch (mappedNumOp) { - switch (mappedNumOp) + // Opening parenthesis starts a new expression and pushes negation state onto the stack + case NumbersAndOperatorsEnum::OpenParenthesis: + negateStack.push_back(sendNegate); + sendNegate = false; + break; + + // Closing parenthesis pops the negation state off the stack and sends it down to the calc engine + case NumbersAndOperatorsEnum::CloseParenthesis: + if (!negateStack.empty()) { - // Opening parenthesis starts a new expression and pushes negation state onto the stack - case NumbersAndOperatorsEnum::OpenParenthesis: - negateStack.push_back(sendNegate); - sendNegate = false; - break; - - // Closing parenthesis pops the negation state off the stack and sends it down to the calc engine - case NumbersAndOperatorsEnum::CloseParenthesis: - if (!negateStack.empty()) - { - sendNegate = negateStack.back(); - negateStack.pop_back(); - canSendNegate = true; - } - else - { - // Don't send a closing parenthesis if a matching opening parenthesis hasn't been sent already - sendCommand = false; - } - break; - - case NumbersAndOperatorsEnum::Zero: - case NumbersAndOperatorsEnum::One: - case NumbersAndOperatorsEnum::Two: - case NumbersAndOperatorsEnum::Three: - case NumbersAndOperatorsEnum::Four: - case NumbersAndOperatorsEnum::Five: - case NumbersAndOperatorsEnum::Six: - case NumbersAndOperatorsEnum::Seven: - case NumbersAndOperatorsEnum::Eight: - case NumbersAndOperatorsEnum::Nine: - processedDigit = true; - break; - - case NumbersAndOperatorsEnum::Add: - case NumbersAndOperatorsEnum::Subtract: - case NumbersAndOperatorsEnum::Multiply: - case NumbersAndOperatorsEnum::Divide: - isPreviousOperator = true; - break; + sendNegate = negateStack.back(); + negateStack.pop_back(); + canSendNegate = true; } - - if (sendCommand) + else { - sentEquals = (mappedNumOp == NumbersAndOperatorsEnum::Equals); - Command cmdenum = ConvertToOperatorsEnum(mappedNumOp); - m_standardCalculatorManager.SendCommand(cmdenum); + // Don't send a closing parenthesis if a matching opening parenthesis hasn't been sent already + sendCommand = false; + } + break; - // The CalcEngine state machine won't allow the negate command to be sent before any - // other digits, so instead a flag is set and the command is sent after the first appropriate - // command. - if (sendNegate) + case NumbersAndOperatorsEnum::Zero: + case NumbersAndOperatorsEnum::One: + case NumbersAndOperatorsEnum::Two: + case NumbersAndOperatorsEnum::Three: + case NumbersAndOperatorsEnum::Four: + case NumbersAndOperatorsEnum::Five: + case NumbersAndOperatorsEnum::Six: + case NumbersAndOperatorsEnum::Seven: + case NumbersAndOperatorsEnum::Eight: + case NumbersAndOperatorsEnum::Nine: + processedDigit = true; + break; + + case NumbersAndOperatorsEnum::Add: + case NumbersAndOperatorsEnum::Subtract: + case NumbersAndOperatorsEnum::Multiply: + case NumbersAndOperatorsEnum::Divide: + isPreviousOperator = true; + break; + } + + if (sendCommand) + { + sentEquals = (mappedNumOp == NumbersAndOperatorsEnum::Equals); + Command cmdenum = ConvertToOperatorsEnum(mappedNumOp); + m_standardCalculatorManager.SendCommand(cmdenum); + + // The CalcEngine state machine won't allow the negate command to be sent before any + // other digits, so instead a flag is set and the command is sent after the first appropriate + // command. + if (sendNegate) + { + if (canSendNegate) { - if (canSendNegate) - { - Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); - m_standardCalculatorManager.SendCommand(cmdNegate); - } + Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); + m_standardCalculatorManager.SendCommand(cmdNegate); + } - // Can't send negate on a leading zero, so wait until the appropriate time to send it. - if (NumbersAndOperatorsEnum::Zero != mappedNumOp && NumbersAndOperatorsEnum::Decimal != mappedNumOp) - { - sendNegate = false; - } + // Can't send negate on a leading zero, so wait until the appropriate time to send it. + if (NumbersAndOperatorsEnum::Zero != mappedNumOp && NumbersAndOperatorsEnum::Decimal != mappedNumOp) + { + sendNegate = false; } } } - // Handle exponent and exponent sign (...e+... or ...e-...) + // Handle exponent and exponent sign (...e+... or ...e-... or ...e...) if (mappedNumOp == NumbersAndOperatorsEnum::Exp) { - ++it; - if (!(MapCharacterToButtonId(*it, canSendNegate) == NumbersAndOperatorsEnum::Add)) + // Check the following item + switch (MapCharacterToButtonId(*(it + 1)).buttonId) + { + case NumbersAndOperatorsEnum::Subtract: { Command cmdNegate = ConvertToOperatorsEnum(NumbersAndOperatorsEnum::Negate); m_standardCalculatorManager.SendCommand(cmdNegate); + ++it; + } + break; + case NumbersAndOperatorsEnum::Add: + { + // Nothing to do, skip to the next item + ++it; + } + break; } } @@ -904,40 +881,24 @@ void StandardCalculatorViewModel::OnPaste(String^ pastedString, ViewMode mode) } } -void StandardCalculatorViewModel::OnClearMemoryCommand( - Object^ parameter) +void StandardCalculatorViewModel::OnClearMemoryCommand(Object ^ parameter) { m_standardCalculatorManager.MemorizedNumberClearAll(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().LogMemoryClearAll(windowId); + TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode()); - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(CalculatorResourceKeys::MemoryCleared, m_localizedMemoryCleared); - Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement); + if (m_localizedMemoryCleared == nullptr) + { + m_localizedMemoryCleared = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryCleared); + } + Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(m_localizedMemoryCleared); } -void StandardCalculatorViewModel::OnPinUnpinCommand(Object^ parameter) +ButtonInfo StandardCalculatorViewModel::MapCharacterToButtonId(char16 ch) { - SetViewPinnedState(!IsViewPinned()); -} - -bool StandardCalculatorViewModel::IsViewPinned() -{ - return m_IsCurrentViewPinned; -} - -void StandardCalculatorViewModel::SetViewPinnedState( - bool pinned) -{ - IsCurrentViewPinned = pinned; -} - -NumbersAndOperatorsEnum StandardCalculatorViewModel::MapCharacterToButtonId( - const wchar_t ch, - bool& canSendNegate) -{ - NumbersAndOperatorsEnum mappedValue = NumbersAndOperatorsEnum::None; - canSendNegate = false; + ButtonInfo result; + result.buttonId = NumbersAndOperatorsEnum::None; + result.canSendNegate = false; switch (ch) { @@ -951,109 +912,127 @@ NumbersAndOperatorsEnum StandardCalculatorViewModel::MapCharacterToButtonId( case '7': case '8': case '9': - mappedValue = NumbersAndOperatorsEnum::Zero + static_cast(ch - L'0'); - canSendNegate = true; + result.buttonId = NumbersAndOperatorsEnum::Zero + static_cast(ch - L'0'); + result.canSendNegate = true; break; case '*': - mappedValue = NumbersAndOperatorsEnum::Multiply; + result.buttonId = NumbersAndOperatorsEnum::Multiply; break; case '+': - mappedValue = NumbersAndOperatorsEnum::Add; + result.buttonId = NumbersAndOperatorsEnum::Add; break; case '-': - mappedValue = NumbersAndOperatorsEnum::Subtract; + result.buttonId = NumbersAndOperatorsEnum::Subtract; break; case '/': - mappedValue = NumbersAndOperatorsEnum::Divide; + result.buttonId = NumbersAndOperatorsEnum::Divide; + break; + + case '^': + if (IsScientific) + { + result.buttonId = NumbersAndOperatorsEnum::XPowerY; + } + break; + + case '%': + if (IsScientific || IsProgrammer) + { + result.buttonId = NumbersAndOperatorsEnum::Mod; + } break; case '=': - mappedValue = NumbersAndOperatorsEnum::Equals; + result.buttonId = NumbersAndOperatorsEnum::Equals; break; case '(': - mappedValue = NumbersAndOperatorsEnum::OpenParenthesis; + result.buttonId = NumbersAndOperatorsEnum::OpenParenthesis; break; case ')': - mappedValue = NumbersAndOperatorsEnum::CloseParenthesis; + result.buttonId = NumbersAndOperatorsEnum::CloseParenthesis; break; case 'a': case 'A': - mappedValue = NumbersAndOperatorsEnum::A; + result.buttonId = NumbersAndOperatorsEnum::A; break; case 'b': case 'B': - mappedValue = NumbersAndOperatorsEnum::B; + result.buttonId = NumbersAndOperatorsEnum::B; break; case 'c': case 'C': - mappedValue = NumbersAndOperatorsEnum::C; + result.buttonId = NumbersAndOperatorsEnum::C; break; case 'd': case 'D': - mappedValue = NumbersAndOperatorsEnum::D; + result.buttonId = NumbersAndOperatorsEnum::D; break; case 'e': case 'E': // Only allow scientific notation in scientific mode if (IsProgrammer) { - mappedValue = NumbersAndOperatorsEnum::E; + result.buttonId = NumbersAndOperatorsEnum::E; } else { - mappedValue = NumbersAndOperatorsEnum::Exp; + result.buttonId = NumbersAndOperatorsEnum::Exp; } break; case 'f': case 'F': - mappedValue = NumbersAndOperatorsEnum::F; + result.buttonId = NumbersAndOperatorsEnum::F; break; default: // For the decimalSeparator, we need to respect the user setting. if (ch == m_decimalSeparator) { - mappedValue = NumbersAndOperatorsEnum::Decimal; + result.buttonId = NumbersAndOperatorsEnum::Decimal; } break; } - if (mappedValue == NumbersAndOperatorsEnum::None) + if (result.buttonId == NumbersAndOperatorsEnum::None) { if (LocalizationSettings::GetInstance().IsLocalizedDigit(ch)) { - mappedValue = NumbersAndOperatorsEnum::Zero + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0')); - canSendNegate = true; + result.buttonId = + NumbersAndOperatorsEnum::Zero + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0')); + result.canSendNegate = true; } } // Negate cannot be sent for leading zeroes - if (NumbersAndOperatorsEnum::Zero == mappedValue) + if (NumbersAndOperatorsEnum::Zero == result.buttonId) { - canSendNegate = false; + result.canSendNegate = false; } - return mappedValue; + return result; +} +void StandardCalculatorViewModel::OnInputChanged() +{ + IsInputEmpty = m_standardCalculatorManager.IsInputEmpty(); } void StandardCalculatorViewModel::OnMemoryButtonPressed() { m_standardCalculatorManager.MemorizeNumber(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); - - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::MemorySave, - m_localizedMemorySavedAutomationFormat, - m_DisplayValue->Data()); + TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::Memory, GetCalculatorMode()); + if (m_localizedMemorySavedAutomationFormat == nullptr) + { + m_localizedMemorySavedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemorySave); + } + String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedMemorySavedAutomationFormat, m_DisplayValue); Announcement = CalculatorAnnouncement::GetMemoryItemAddedAnnouncement(announcement); } @@ -1061,82 +1040,63 @@ void StandardCalculatorViewModel::OnMemoryItemChanged(unsigned int indexOfMemory { if (indexOfMemory < MemorizedNumbers->Size) { - MemoryItemViewModel^ memSlot = MemorizedNumbers->GetAt(indexOfMemory); - String^ localizedValue = memSlot->Value; + MemoryItemViewModel ^ memSlot = MemorizedNumbers->GetAt(indexOfMemory); + String ^ localizedValue = memSlot->Value; wstring localizedIndex = to_wstring(indexOfMemory + 1); LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex); - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::MemoryItemChanged, - m_localizedMemoryItemChangedAutomationFormat, - localizedIndex.c_str(), - localizedValue->Data()); - + if (m_localizedMemoryItemChangedAutomationFormat == nullptr) + { + m_localizedMemoryItemChangedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryItemChanged); + } + String ^ announcement = + LocalizationStringUtil::GetLocalizedString(m_localizedMemoryItemChangedAutomationFormat, StringReference(localizedIndex.c_str()), localizedValue); Announcement = CalculatorAnnouncement::GetMemoryItemChangedAnnouncement(announcement); } } -void StandardCalculatorViewModel::OnMemoryItemPressed(Object^ memoryItemPosition) +void StandardCalculatorViewModel::OnMemoryItemPressed(Object ^ memoryItemPosition) { if (MemorizedNumbers && MemorizedNumbers->Size > 0) { - auto boxedPosition = safe_cast^>(memoryItemPosition); + auto boxedPosition = safe_cast ^>(memoryItemPosition); m_standardCalculatorManager.MemorizedNumberLoad(boxedPosition->Value); HideMemoryClicked(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); + + auto mode = IsStandard ? ViewMode::Standard : IsScientific ? ViewMode::Scientific : ViewMode::Programmer; + TraceLogger::GetInstance()->LogMemoryItemLoad(mode, MemorizedNumbers->Size, boxedPosition->Value); } } -void StandardCalculatorViewModel::OnMemoryAdd(Object^ memoryItemPosition) +void StandardCalculatorViewModel::OnMemoryAdd(Object ^ memoryItemPosition) { // M+ will add display to memorylist if memory list is empty. - int windowId = Utils::GetWindowId(); if (MemorizedNumbers) { - auto boxedPosition = safe_cast^>(memoryItemPosition); - if (MemorizedNumbers->Size > 0) - { - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer); - } - else - { - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); - } + auto boxedPosition = safe_cast ^>(memoryItemPosition); + TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryAdd, GetCalculatorMode()); m_standardCalculatorManager.MemorizedNumberAdd(boxedPosition->Value); } } -void StandardCalculatorViewModel::OnMemorySubtract(Object^ memoryItemPosition) +void StandardCalculatorViewModel::OnMemorySubtract(Object ^ memoryItemPosition) { - int windowId = Utils::GetWindowId(); - // M- will add negative of displayed number to memorylist if memory list is empty. if (MemorizedNumbers) { - auto boxedPosition = safe_cast^>(memoryItemPosition); - if (MemorizedNumbers->Size > 0) - { - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer); - } - else - { - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); - } + auto boxedPosition = safe_cast ^>(memoryItemPosition); + TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemorySubtract, GetCalculatorMode()); m_standardCalculatorManager.MemorizedNumberSubtract(boxedPosition->Value); } } -void StandardCalculatorViewModel::OnMemoryClear(_In_ Object^ memoryItemPosition) +void StandardCalculatorViewModel::OnMemoryClear(_In_ Object ^ memoryItemPosition) { if (MemorizedNumbers && MemorizedNumbers->Size > 0) { - int windowId = Utils::GetWindowId(); - auto boxedPosition = safe_cast^>(memoryItemPosition); + auto boxedPosition = safe_cast ^>(memoryItemPosition); if (boxedPosition->Value >= 0) { @@ -1153,155 +1113,69 @@ void StandardCalculatorViewModel::OnMemoryClear(_In_ Object^ memoryItemPosition) { IsMemoryEmpty = true; } - - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().DeleteFromMemoryMap(windowId, boxedPosition->Value); + TraceLogger::GetInstance()->UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode()); wstring localizedIndex = to_wstring(boxedPosition->Value + 1); LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex); - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::MemoryItemCleared, - m_localizedMemoryItemClearedAutomationFormat, - localizedIndex.c_str()); + if (m_localizedMemoryItemClearedAutomationFormat == nullptr) + { + m_localizedMemoryItemClearedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MemoryItemCleared); + } + String ^ announcement = + LocalizationStringUtil::GetLocalizedString(m_localizedMemoryItemClearedAutomationFormat, StringReference(localizedIndex.c_str())); Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement); } } } -Array^ StandardCalculatorViewModel::Serialize() +void StandardCalculatorViewModel::OnPropertyChanged(String ^ propertyname) { - DataWriter^ writer = ref new DataWriter(); - writer->WriteUInt32(static_cast(m_CurrentAngleType)); - writer->WriteBoolean(IsFToEChecked); - writer->WriteBoolean(IsCurrentViewPinned); - writer->WriteUInt32(static_cast(m_standardCalculatorManager.SerializeSavedDegreeMode())); - - // Serialize Memory - vector serializedMemory; - serializedMemory = m_standardCalculatorManager.GetSerializedMemory(); - size_t lengthOfSerializedMemory = serializedMemory.size(); - writer->WriteUInt32(static_cast(lengthOfSerializedMemory)); - for (auto data : serializedMemory) - { - writer->WriteInt32(data); - } - - // Serialize Primary Display - vector serializedPrimaryDisplay = m_standardCalculatorManager.GetSerializedPrimaryDisplay(); - writer->WriteUInt32(static_cast(serializedPrimaryDisplay.size())); - for (auto data : serializedPrimaryDisplay) - { - writer->WriteInt32(data); - } - - // For ProgrammerMode - writer->WriteUInt32(static_cast(CurrentRadixType)); - - // Serialize commands of calculator manager - vector serializedCommand = m_standardCalculatorManager.SerializeCommands(); - writer->WriteUInt32(static_cast(serializedCommand.size())); - writer->WriteBytes(ref new Array(serializedCommand.data(), static_cast(serializedCommand.size()))); - - if (IsInError) - { - Utils::SerializeCommandsAndTokens(m_tokens, m_commands, writer); - } - - // Convert viewmodel data in writer to bytes - IBuffer^ buffer = writer->DetachBuffer(); - DataReader^ reader = DataReader::FromBuffer(buffer); - Platform::Array^ viewModelDataAsBytes = ref new Array(buffer->Length); - reader->ReadBytes(viewModelDataAsBytes); - - // Return byte array - return viewModelDataAsBytes; -} - -void StandardCalculatorViewModel::Deserialize(Array^ state) -{ - // Read byte array into a buffer - DataWriter^ writer = ref new DataWriter(); - writer->WriteBytes(state); - IBuffer^ buffer = writer->DetachBuffer(); - - // Read view model data - if (buffer->Length != 0) - { - DataReader^ reader = DataReader::FromBuffer(buffer); - m_CurrentAngleType = ConvertIntegerToNumbersAndOperatorsEnum(reader->ReadUInt32()); - - IsFToEChecked = reader->ReadBoolean(); - IsCurrentViewPinned = reader->ReadBoolean(); - Command serializedDegreeMode = static_cast(reader->ReadUInt32()); - - m_standardCalculatorManager.SendCommand(serializedDegreeMode); - - // Deserialize Memory - UINT32 memoryDataLength = reader->ReadUInt32(); - vector serializedMemory; - for (unsigned int i = 0; i < memoryDataLength; i++) - { - serializedMemory.push_back(reader->ReadInt32()); - } - m_standardCalculatorManager.DeSerializeMemory(serializedMemory); - - // Serialize Primary Display - UINT32 serializedPrimaryDisplayLength = reader->ReadUInt32(); - vector serializedPrimaryDisplay; - for (unsigned int i = 0; i < serializedPrimaryDisplayLength; i++) - { - serializedPrimaryDisplay.push_back(reader->ReadInt32()); - } - m_standardCalculatorManager.DeSerializePrimaryDisplay(serializedPrimaryDisplay); - - CurrentRadixType = reader->ReadUInt32(); - // Read command data and Deserialize - UINT32 modeldatalength = reader->ReadUInt32(); - Array^ modelDataAsBytes = ref new Array(modeldatalength); - reader->ReadBytes(modelDataAsBytes); - m_standardCalculatorManager.DeSerializeCommands(vector(modelDataAsBytes->begin(), modelDataAsBytes->end())); - - // After recalculation. If there is an error then - // IsInError should be set synchronously. - if (IsInError) - { - shared_ptr>> commandVector = Utils::DeserializeCommands(reader); - shared_ptr>> tokenVector = Utils::DeserializeTokens(reader); - SetExpressionDisplay(tokenVector, commandVector); - } - } -} - -void StandardCalculatorViewModel::OnPropertyChanged(String^ propertyname) -{ - if (propertyname == CalculatorViewModelProperties::IsScientific) + if (propertyname == IsScientificPropertyName) { if (IsScientific) { OnButtonPressed(NumbersAndOperatorsEnum::IsScientificMode); } } - else if (propertyname == CalculatorViewModelProperties::IsProgrammer) + else if (propertyname == IsProgrammerPropertyName) { if (IsProgrammer) { OnButtonPressed(NumbersAndOperatorsEnum::IsProgrammerMode); } } - else if (propertyname == CalculatorViewModelProperties::IsStandard) + else if (propertyname == IsStandardPropertyName) { if (IsStandard) { OnButtonPressed(NumbersAndOperatorsEnum::IsStandardMode); } } - else if (propertyname == CalculatorViewModelProperties::DisplayValue) + else if (propertyname == DisplayValuePropertyName) { - RaisePropertyChanged(CalculationResultAutomationName_PropertyName); + RaisePropertyChanged(CalculationResultAutomationNamePropertyName); Announcement = GetDisplayUpdatedNarratorAnnouncement(); } + else if (propertyname == IsBitFlipCheckedPropertyName) + { + TraceLogger::GetInstance()->UpdateButtonUsage( + IsBitFlipChecked ? NumbersAndOperatorsEnum::BitflipButton : NumbersAndOperatorsEnum::FullKeypadButton, ViewMode::Programmer); + } + else if (propertyname == IsAlwaysOnTopPropertyName) + { + String ^ announcement; + if (IsAlwaysOnTop) + { + announcement = AppResourceProvider::GetInstance()->GetResourceString(CalcAlwaysOnTop); + } + else + { + announcement = AppResourceProvider::GetInstance()->GetResourceString(CalcBackToFullView); + } + Announcement = CalculatorAnnouncement::GetAlwaysOnTopChangedAnnouncement(announcement); + } } void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState) @@ -1334,31 +1208,31 @@ void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState) } } -Platform::String^ StandardCalculatorViewModel::GetRawDisplayValue() +String ^ StandardCalculatorViewModel::GetRawDisplayValue() { - wstring rawValue; - - LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue->Data(), DisplayValue->Length(), &rawValue); - - return ref new Platform::String(rawValue.c_str()); + if (IsInError) + { + return DisplayValue; + } + else + { + return LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue); + } } // Given a format string, returns a string with the input display value inserted. // 'format' is a localized string containing a %1 formatting mark where the display value should be inserted. // 'displayValue' is a localized string containing a numerical value to be displayed to the user. -String^ StandardCalculatorViewModel::GetLocalizedStringFormat(String^ format, String^ displayValue) +String ^ StandardCalculatorViewModel::GetLocalizedStringFormat(String ^ format, String ^ displayValue) { - String^ localizedString = ref new String(LocalizationStringUtil::GetLocalizedString(format->Data(), displayValue->Data()).c_str()); - return localizedString; + return LocalizationStringUtil::GetLocalizedString(format, displayValue); } - void StandardCalculatorViewModel::ResetDisplay() { AreHEXButtonsEnabled = false; - CurrentRadixType = (int)RADIX_TYPE::DEC_RADIX; - m_standardCalculatorManager.SetRadix(DEC_RADIX); - ProgModeRadixChange(); + CurrentRadixType = NumberBase::DecBase; + m_standardCalculatorManager.SetRadix(RadixType::Decimal); } void StandardCalculatorViewModel::SetPrecision(int32_t precision) @@ -1366,17 +1240,16 @@ void StandardCalculatorViewModel::SetPrecision(int32_t precision) m_standardCalculatorManager.SetPrecision(precision); } -void StandardCalculatorViewModel::SwitchProgrammerModeBase(RADIX_TYPE radixType) +void StandardCalculatorViewModel::SwitchProgrammerModeBase(NumberBase numberBase) { if (IsInError) { m_standardCalculatorManager.SendCommand(Command::CommandCLEAR); } - AreHEXButtonsEnabled = (radixType == RADIX_TYPE::HEX_RADIX); - CurrentRadixType = (int)radixType; - m_standardCalculatorManager.SetRadix(radixType); - ProgModeRadixChange(); + AreHEXButtonsEnabled = numberBase == NumberBase::HexBase; + CurrentRadixType = numberBase; + m_standardCalculatorManager.SetRadix(GetRadixTypeFromNumberBase(numberBase)); } void StandardCalculatorViewModel::SetMemorizedNumbersString() @@ -1384,16 +1257,16 @@ void StandardCalculatorViewModel::SetMemorizedNumbersString() m_standardCalculatorManager.SetMemorizedNumbersString(); } -ANGLE_TYPE GetAngleTypeFromCommand(Command command) +AngleType GetAngleTypeFromCommand(Command command) { switch (command) { case Command::CommandDEG: - return ANGLE_DEG; + return AngleType::Degrees; case Command::CommandRAD: - return ANGLE_RAD; + return AngleType::Radians; case Command::CommandGRAD: - return ANGLE_GRAD; + return AngleType::Gradians; default: throw ref new Exception(E_FAIL, L"Invalid command type"); } @@ -1401,72 +1274,66 @@ ANGLE_TYPE GetAngleTypeFromCommand(Command command) void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosition, _In_ Command command) { - pair token; bool handleOperand = false; - int nOpCode = static_cast(command); - wstring updatedToken = L""; + wstring updatedToken; - shared_ptr tokenCommand; - IFTPlatformException(m_tokens->GetAt(tokenPosition, &token)); + const pair& token = m_tokens->at(tokenPosition); + const shared_ptr& tokenCommand = m_commands->at(token.second); - unsigned int tokenCommandIndex = token.second; - IFTPlatformException(m_commands->GetAt(tokenCommandIndex, &tokenCommand)); - - if (IsUnaryOp(nOpCode) && command != Command::CommandSIGN) + if (IsUnaryOp(command) && command != Command::CommandSIGN) { int angleCmd = static_cast(m_standardCalculatorManager.GetCurrentDegreeMode()); - ANGLE_TYPE angleType = GetAngleTypeFromCommand(static_cast(angleCmd)); + AngleType angleType = GetAngleTypeFromCommand(static_cast(angleCmd)); - if (IsTrigOp(nOpCode)) + if (IsTrigOp(command)) { shared_ptr spUnaryCommand = dynamic_pointer_cast(tokenCommand); - spUnaryCommand->SetCommands(angleCmd, nOpCode); + spUnaryCommand->SetCommands(angleCmd, static_cast(command)); } else { shared_ptr spUnaryCommand = dynamic_pointer_cast(tokenCommand); - spUnaryCommand->SetCommand(nOpCode); + spUnaryCommand->SetCommand(static_cast(command)); } - switch (nOpCode) + switch (command) { - case static_cast(Command::CommandASIN) : + case Command::CommandASIN: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSIN), true, angleType); break; - case static_cast(Command::CommandACOS) : + case Command::CommandACOS: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOS), true, angleType); break; - case static_cast(Command::CommandATAN) : + case Command::CommandATAN: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTAN), true, angleType); break; - case static_cast(Command::CommandASINH) : + case Command::CommandASINH: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandSINH), true, angleType); break; - case static_cast(Command::CommandACOSH) : + case Command::CommandACOSH: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandCOSH), true, angleType); break; - case static_cast(Command::CommandATANH) : + case Command::CommandATANH: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandTANH), true, angleType); break; - case static_cast(Command::CommandPOWE) : + case Command::CommandPOWE: updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(Command::CommandLN), true, angleType); break; default: - updatedToken = CCalcEngine::OpCodeToUnaryString(nOpCode, false, angleType); + updatedToken = CCalcEngine::OpCodeToUnaryString(static_cast(command), false, angleType); } if ((token.first.length() > 0) && (token.first[token.first.length() - 1] == L'(')) { - wstring chOpenBrace = L"("; - updatedToken.append(chOpenBrace); + updatedToken += L'('; } } - else if (IsBinOp(nOpCode)) + else if (IsBinOp(command)) { shared_ptr spBinaryCommand = dynamic_pointer_cast(tokenCommand); - spBinaryCommand->SetCommand(nOpCode); - updatedToken = CCalcEngine::OpCodeToString(nOpCode); + spBinaryCommand->SetCommand(static_cast(command)); + updatedToken = CCalcEngine::OpCodeToString(static_cast(command)); } - else if (IsOpnd(nOpCode) || command == Command::CommandBACK) + else if (IsOpnd(command) || command == Command::CommandBACK) { HandleUpdatedOperandData(command); handleOperand = true; @@ -1475,8 +1342,8 @@ void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosit { if (tokenCommand->GetCommandType() == CommandType::UnaryCommand) { - shared_ptr spSignCommand = make_shared(nOpCode); - IFTPlatformException(m_commands->InsertAt(tokenCommandIndex + 1, spSignCommand)); + shared_ptr spSignCommand = make_shared(static_cast(command)); + m_commands->insert(m_commands->begin() + token.second + 1, spSignCommand); } else { @@ -1489,14 +1356,10 @@ void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosit if (!handleOperand) { - IFTPlatformException(m_commands->SetAt(tokenCommandIndex, tokenCommand)); + (*m_commands)[token.second] = tokenCommand; + (*m_tokens)[tokenPosition].first = updatedToken; - pair < wstring, int> selectedToken; - IFTPlatformException(m_tokens->GetAt(tokenPosition, &selectedToken)); - selectedToken.first = updatedToken; - IFTPlatformException(m_tokens->SetAt(tokenPosition, selectedToken)); - - DisplayExpressionToken^ displayExpressionToken = ExpressionTokens->GetAt(tokenPosition); + DisplayExpressionToken ^ displayExpressionToken = ExpressionTokens->GetAt(tokenPosition); displayExpressionToken->Token = ref new Platform::String(updatedToken.c_str()); // Special casing @@ -1512,30 +1375,21 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) { // Recalculate Command currentDegreeMode = m_standardCalculatorManager.GetCurrentDegreeMode(); - shared_ptr >> savedCommands = make_shared >>(); - + shared_ptr>> savedCommands = make_shared>>(); vector currentCommands; - unsigned int commandListCount; - m_commands->GetSize(&commandListCount); - for (unsigned int i = 0; i < commandListCount; i++) - { - shared_ptr command; - IFTPlatformException(m_commands->GetAt(i, &command)); - savedCommands->Append(command); + for (const auto& command : *m_commands) + { + savedCommands->push_back(command); CommandType commandType = command->GetCommandType(); if (commandType == CommandType::UnaryCommand) { shared_ptr spCommand = dynamic_pointer_cast(command); - shared_ptr> unaryCommands = spCommand->GetCommands(); - unsigned int unaryCommandCount; - unaryCommands->GetSize(&unaryCommandCount); + const shared_ptr>& unaryCommands = spCommand->GetCommands(); - int nUCode; - for (unsigned int j = 0; j < unaryCommandCount; ++j) + for (int nUCode : *unaryCommands) { - IFTPlatformException(unaryCommands->GetAt(j, &nUCode)); currentCommands.push_back(nUCode); } } @@ -1555,15 +1409,11 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) if (commandType == CommandType::OperandCommand) { shared_ptr spCommand = dynamic_pointer_cast(command); - shared_ptr> opndCommands = spCommand->GetCommands(); - unsigned int opndCommandCount; - opndCommands->GetSize(&opndCommandCount); + const shared_ptr>& opndCommands = spCommand->GetCommands(); bool fNeedIDCSign = spCommand->IsNegative(); - int nOCode; - for (unsigned int j = 0; j < opndCommandCount; ++j) + for (int nOCode : *opndCommands) { - IFTPlatformException(opndCommands->GetAt(j, &nOCode)); currentCommands.push_back(nOCode); if (fNeedIDCSign && nOCode != IDC_0) @@ -1574,16 +1424,12 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) } } } - shared_ptr>> savedTokens = make_shared>>(); - unsigned int tokenCount; - IFTPlatformException(m_tokens->GetSize(&tokenCount)); + shared_ptr>> savedTokens = make_shared>>(); - for (unsigned int i = 0; i < tokenCount; ++i) + for (const auto& currentToken : *m_tokens) { - pair currentToken; - IFTPlatformException(m_tokens->GetAt(i, ¤tToken)); - savedTokens->Append(currentToken); + savedTokens->push_back(currentToken); } m_standardCalculatorManager.Reset(false); @@ -1598,13 +1444,13 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) } m_standardCalculatorManager.SendCommand(currentDegreeMode); - size_t currentCommandsSize = currentCommands.size(); - for (size_t i = 0; i < currentCommandsSize; i++) + + for (int command : currentCommands) { - m_standardCalculatorManager.SendCommand(static_cast(currentCommands[i])); + m_standardCalculatorManager.SendCommand(static_cast(command)); } - if (fromHistory) // This is for the cases where the expression is loaded from history + if (fromHistory) // This is for the cases where the expression is loaded from history { // To maintain F-E state of the engine, as the last operand hasn't reached engine by now m_standardCalculatorManager.SendCommand(Command::CommandFE); @@ -1619,75 +1465,27 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) } } -CommandType StandardCalculatorViewModel::GetSelectedTokenType(_In_ unsigned int tokenPosition) +bool StandardCalculatorViewModel::IsOpnd(Command command) { - pairtoken; - shared_ptr tokenCommand; - IFTPlatformException(m_tokens->GetAt(tokenPosition, &token)); + static constexpr Command opnd[] = { Command::Command0, Command::Command1, Command::Command2, Command::Command3, Command::Command4, Command::Command5, + Command::Command6, Command::Command7, Command::Command8, Command::Command9, Command::CommandPNT }; - unsigned int tokenCommandIndex = token.second; - IFTPlatformException(m_commands->GetAt(tokenCommandIndex, &tokenCommand)); - - return tokenCommand->GetCommandType(); + return find(begin(opnd), end(opnd), command) != end(opnd); } -bool StandardCalculatorViewModel::IsOpnd(int nOpCode) +bool StandardCalculatorViewModel::IsUnaryOp(Command command) { + static constexpr Command unaryOp[] = { Command::CommandSQRT, Command::CommandFAC, Command::CommandSQR, Command::CommandLOG, + Command::CommandPOW10, Command::CommandPOWE, Command::CommandLN, Command::CommandREC, + Command::CommandSIGN, Command::CommandSINH, Command::CommandASINH, Command::CommandCOSH, + Command::CommandACOSH, Command::CommandTANH, Command::CommandATANH, Command::CommandCUB }; - static Command opnd[] = { - Command::Command0, - Command::Command1, - Command::Command2, - Command::Command3, - Command::Command4, - Command::Command5, - Command::Command6, - Command::Command7, - Command::Command8, - Command::Command9, - Command::CommandPNT - }; - - for (int i = 0; i < size(opnd); i++) + if (find(begin(unaryOp), end(unaryOp), command) != end(unaryOp)) { - if (nOpCode == static_cast(opnd[i])) - { - return true; - } - } - return false; -} - -bool StandardCalculatorViewModel::IsUnaryOp(int nOpCode) -{ - static Command unaryOp[] = { - Command::CommandSQRT, - Command::CommandFAC, - Command::CommandSQR, - Command::CommandLOG, - Command::CommandPOW10, - Command::CommandPOWE, - Command::CommandLN, - Command::CommandREC, - Command::CommandSIGN, - Command::CommandSINH, - Command::CommandASINH, - Command::CommandCOSH, - Command::CommandACOSH, - Command::CommandTANH, - Command::CommandATANH, - Command::CommandCUB - }; - - for (int i = 0; i < size(unaryOp); i++) - { - if (nOpCode == static_cast(unaryOp[i])) - { - return true; - } + return true; } - if (IsTrigOp(nOpCode)) + if (IsTrigOp(command)) { return true; } @@ -1695,100 +1493,49 @@ bool StandardCalculatorViewModel::IsUnaryOp(int nOpCode) return false; } -bool StandardCalculatorViewModel::IsTrigOp(int nOpCode) +bool StandardCalculatorViewModel::IsTrigOp(Command command) { - static Command trigOp[] = { - Command::CommandSIN, - Command::CommandCOS, - Command::CommandTAN, - Command::CommandASIN, - Command::CommandACOS, - Command::CommandATAN - }; + static constexpr Command trigOp[] = { Command::CommandSIN, Command::CommandCOS, Command::CommandTAN, + Command::CommandASIN, Command::CommandACOS, Command::CommandATAN }; - for (int i = 0; i < size(trigOp); i++) - { - if (nOpCode == static_cast(trigOp[i])) - { - return true; - } - } - return false; + return find(begin(trigOp), end(trigOp), command) != end(trigOp); } -bool StandardCalculatorViewModel::IsBinOp(int nOpCode) +bool StandardCalculatorViewModel::IsBinOp(Command command) { - static Command binOp[] = { - Command::CommandADD, - Command::CommandSUB, - Command::CommandMUL, - Command::CommandDIV, - Command::CommandEXP, - Command::CommandROOT, - Command::CommandMOD, - Command::CommandPWR - }; + static constexpr Command binOp[] = { Command::CommandADD, Command::CommandSUB, Command::CommandMUL, Command::CommandDIV, + Command::CommandEXP, Command::CommandROOT, Command::CommandMOD, Command::CommandPWR }; - for (int i = 0; i < size(binOp); i++) - { - if (nOpCode == static_cast(binOp[i])) - { - return true; - } - } - return false; + return find(begin(binOp), end(binOp), command) != end(binOp); } -bool StandardCalculatorViewModel::IsRecoverableCommand(int nOpCode) +bool StandardCalculatorViewModel::IsRecoverableCommand(Command command) { - if (IsOpnd(nOpCode)) + if (IsOpnd(command)) { return true; } // Programmer mode, bit flipping - int minBinPos = static_cast(Command::CommandBINEDITSTART); - int maxBinPos = static_cast(Command::CommandBINEDITEND); - if (minBinPos <= nOpCode && nOpCode <= maxBinPos) + if (Command::CommandBINEDITSTART <= command && command <= Command::CommandBINEDITEND) { return true; } - static Command recoverableCommands[] = { - Command::CommandA, - Command::CommandB, - Command::CommandC, - Command::CommandD, - Command::CommandE, - Command::CommandF - }; + static constexpr Command recoverableCommands[] = { Command::CommandA, Command::CommandB, Command::CommandC, + Command::CommandD, Command::CommandE, Command::CommandF }; - for (int i = 0; i < size(recoverableCommands); i++) - { - if (nOpCode == static_cast(recoverableCommands[i])) - { - return true; - } - } - return false; + return find(begin(recoverableCommands), end(recoverableCommands), command) != end(recoverableCommands); } size_t StandardCalculatorViewModel::LengthWithoutPadding(wstring str) { - size_t count = 0; - for (size_t i = 0; i < str.length(); i++) - { - if (str[i] != L' ') - { - count++; - } - } - return count; + return str.length() - count(str.begin(), str.end(), L' '); } wstring StandardCalculatorViewModel::AddPadding(wstring binaryString) { - if (LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(binaryString) == L"0") + if (LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(StringReference(binaryString.c_str())) == L"0") { return binaryString; } @@ -1797,16 +1544,12 @@ wstring StandardCalculatorViewModel::AddPadding(wstring binaryString) { pad = 0; } - wstring padString = L""; - for (size_t i = 0; i < pad; i++) - { - padString += L"0"; - } - return padString + binaryString; + return wstring(pad, L'0') + binaryString; } void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() { + constexpr int32_t precision = 64; wstring hexDisplayString; wstring decimalDisplayString; wstring octalDisplayString; @@ -1814,8 +1557,7 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() if (!IsInError) { // we want the precision to be set to maximum value so that the autoconversions result as desired - int32_t precision = 64; - if (m_standardCalculatorManager.GetResultForRadix(16, precision) == L"") + if ((hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision, true)) == L"") { hexDisplayString = DisplayValue->Data(); decimalDisplayString = DisplayValue->Data(); @@ -1824,10 +1566,9 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() } else { - hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision); - decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision); - octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision); - binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision); + decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision, true); + octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision, true); + binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision, true); } } const auto& localizer = LocalizationSettings::GetInstance(); @@ -1838,14 +1579,25 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() localizer.LocalizeDisplayValue(&octalDisplayString); localizer.LocalizeDisplayValue(&binaryDisplayString); - HexDisplayValue = Utils::LRO + ref new Platform::String(hexDisplayString.c_str()) + Utils::PDF; - DecimalDisplayValue = Utils::LRO + ref new Platform::String(decimalDisplayString.c_str()) + Utils::PDF; - OctalDisplayValue = Utils::LRO + ref new Platform::String(octalDisplayString.c_str()) + Utils::PDF; - BinaryDisplayValue = Utils::LRO + ref new Platform::String(binaryDisplayString.c_str()) + Utils::PDF; + HexDisplayValue = ref new Platform::String(hexDisplayString.c_str()); + DecimalDisplayValue = ref new Platform::String(decimalDisplayString.c_str()); + OctalDisplayValue = ref new Platform::String(octalDisplayString.c_str()); + BinaryDisplayValue = ref new Platform::String(binaryDisplayString.c_str()); HexDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedHexaDecimalAutomationFormat, GetNarratorStringReadRawNumbers(HexDisplayValue)); DecDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedDecimalAutomationFormat, DecimalDisplayValue); OctDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedOctalAutomationFormat, GetNarratorStringReadRawNumbers(OctalDisplayValue)); BinDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedBinaryAutomationFormat, GetNarratorStringReadRawNumbers(BinaryDisplayValue)); + + auto binaryValueArray = ref new Vector(64, false); + auto binaryValue = m_standardCalculatorManager.GetResultForRadix(2, precision, false); + int i = 0; + + // To get bit 0, grab from opposite end of string. + for (std::wstring::reverse_iterator it = binaryValue.rbegin(); it != binaryValue.rend(); ++it) + { + binaryValueArray->SetAt(i++, *it == L'1'); + } + BinaryDigits = binaryValueArray; } void StandardCalculatorViewModel::SwitchAngleType(NumbersAndOperatorsEnum num) @@ -1853,42 +1605,20 @@ void StandardCalculatorViewModel::SwitchAngleType(NumbersAndOperatorsEnum num) OnButtonPressed(num); } -NumbersAndOperatorsEnum StandardCalculatorViewModel::ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter) +void StandardCalculatorViewModel::UpdateOperand(int pos, String ^ text) { - NumbersAndOperatorsEnum angletype; - switch (parameter) - { - case 321: - angletype = NumbersAndOperatorsEnum::Degree; - break; - case 322: - angletype = NumbersAndOperatorsEnum::Radians; - break; - case 323: - angletype = NumbersAndOperatorsEnum::Grads; - break; - default: - angletype = NumbersAndOperatorsEnum::Degree; - }; - return angletype; -} + pair p = m_tokens->at(pos); -void StandardCalculatorViewModel::UpdateOperand(int pos, String^ text) -{ - pair p; - m_tokens->GetAt(pos, &p); - - String^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(text->Data()); + String ^ englishString = LocalizationSettings::GetInstance().GetEnglishValueFromLocalizedDigits(text); p.first = englishString->Data(); int commandPos = p.second; - shared_ptr exprCmd; - m_commands->GetAt(commandPos, &exprCmd); + const shared_ptr& exprCmd = m_commands->at(commandPos); auto operandCommand = std::dynamic_pointer_cast(exprCmd); if (operandCommand != nullptr) { - shared_ptr> commands = make_shared>(); + shared_ptr> commands = make_shared>(); size_t length = p.first.length(); if (length > 0) { @@ -1926,90 +1656,24 @@ void StandardCalculatorViewModel::UpdateOperand(int pos, String^ text) continue; } } - commands->Append(num); + commands->push_back(num); } } else { - commands->Append(0); + commands->push_back(0); } operandCommand->SetCommands(commands); } } -void StandardCalculatorViewModel::UpdatecommandsInRecordingMode() -{ - vector savedCommands = m_standardCalculatorManager.GetSavedCommands(); - shared_ptr> commands = make_shared>(); - bool isDecimal = false; - bool isNegative = false; - bool isExpMode = false; - bool ePlusMode = false; - bool eMinusMode = false; - - int num = 0; - Command val; - for (unsigned int i = 0; i < savedCommands.size(); ++i) - { - val = safe_cast(savedCommands[i]); - num = static_cast(val); - if (val == Command::CommandSIGN) - { - isNegative = true; - continue; - } - else if ((val >= Command::Command0 && val <= Command::Command9)) - { - } - else if (val == Command::CommandPNT) - { - isDecimal = true; - } - else if (val == Command::CommandEXP) - { - isExpMode = true; - } - else if (isExpMode && !ePlusMode && (val == Command::CommandMPLUS)) - { - ePlusMode = true; - continue; - } - else if (isExpMode && !eMinusMode && (val == Command::CommandMMINUS)) - { - eMinusMode = true; - continue; - } - else - { - // Reset all vars - isDecimal = false; - isNegative = false; - isExpMode = false; - ePlusMode = false; - eMinusMode = false; - commands->Clear(); - continue; - } - commands->Append(num); - } - - unsigned int size = 0; - commands->GetSize(&size); - if (size > 0) - { - shared_ptr sp = make_shared(commands, isNegative, isDecimal, isExpMode); - m_commands->Append(sp); - } - Recalculate(); -} - void StandardCalculatorViewModel::OnMaxDigitsReached() { - String^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::MaxDigitsReachedFormat, - m_localizedMaxDigitsReachedAutomationFormat, - m_CalculationResultAutomationName->Data()); - + if (m_localizedMaxDigitsReachedAutomationFormat == nullptr) + { + m_localizedMaxDigitsReachedAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::MaxDigitsReachedFormat); + } + String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedMaxDigitsReachedAutomationFormat, m_CalculationResultAutomationName); Announcement = CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(announcement); } @@ -2018,20 +1682,22 @@ void StandardCalculatorViewModel::OnBinaryOperatorReceived() Announcement = GetDisplayUpdatedNarratorAnnouncement(); } -NarratorAnnouncement^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnnouncement() +NarratorAnnouncement ^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnnouncement() { - String^ announcement; + String ^ announcement; if (m_feedbackForButtonPress == nullptr || m_feedbackForButtonPress->IsEmpty()) { announcement = m_CalculationResultAutomationName; } else { - announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( - CalculatorResourceKeys::ButtonPressFeedbackFormat, - m_localizedButtonPressFeedbackAutomationFormat, - m_CalculationResultAutomationName->Data(), - m_feedbackForButtonPress->Data()); + if (m_localizedButtonPressFeedbackAutomationFormat == nullptr) + { + m_localizedButtonPressFeedbackAutomationFormat = + AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::ButtonPressFeedbackFormat); + } + announcement = LocalizationStringUtil::GetLocalizedString( + m_localizedButtonPressFeedbackAutomationFormat, m_CalculationResultAutomationName, m_feedbackForButtonPress); } // Make sure we don't accidentally repeat an announcement. @@ -2039,3 +1705,67 @@ NarratorAnnouncement^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnno return CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(announcement); } + +ViewMode StandardCalculatorViewModel::GetCalculatorMode() +{ + if (IsStandard) + { + return ViewMode::Standard; + } + else if (IsScientific) + { + return ViewMode::Scientific; + } + return ViewMode::Programmer; +} + +void StandardCalculatorViewModel::ValueBitLength::set(CalculatorApp::Common::BitLength value) +{ + if (m_valueBitLength != value) + { + m_valueBitLength = value; + RaisePropertyChanged(L"ValueBitLength"); + + switch (value) + { + case BitLength::BitLengthQWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Qword); + break; + case BitLength::BitLengthDWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Dword); + break; + case BitLength::BitLengthWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Word); + break; + case BitLength::BitLengthByte: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Byte); + break; + } + + // update memory list according to bit length + SetMemorizedNumbersString(); + } +} + +void StandardCalculatorViewModel::SelectHistoryItem(HistoryItemViewModel ^ item) +{ + SetHistoryExpressionDisplay(item->GetTokens(), item->GetCommands()); + SetExpressionDisplay(item->GetTokens(), item->GetCommands()); + SetPrimaryDisplay(item->Result, false); + IsFToEEnabled = false; +} + +void StandardCalculatorViewModel::ResetCalcManager(bool clearMemory) +{ + m_standardCalculatorManager.Reset(clearMemory); +} + +void StandardCalculatorViewModel::SendCommandToCalcManager(int commandId) +{ + m_standardCalculatorManager.SendCommand(static_cast(commandId)); +} + +void StandardCalculatorViewModel::SetBitshiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement) +{ + Announcement = CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(announcement); +} diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index d7d3abde..d60122bf 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -9,16 +9,12 @@ #include "Common/CalculatorButtonUser.h" #include "HistoryViewModel.h" #include "MemoryItemViewModel.h" - -namespace CalculatorFunctionalTests -{ - class HistoryTests; -} +#include "Common/BitLength.h" +#include "Common/NumberBase.h" namespace CalculatorUnitTests { class MultiWindowUnitTests; - class TimerTests; } namespace CalculatorApp @@ -30,117 +26,106 @@ namespace CalculatorApp { #define ASCII_0 48 public delegate void HideMemoryClickedHandler(); - public delegate void ProgModeRadixChangeHandler(); - namespace CalculatorViewModelProperties - { - extern Platform::StringReference IsMemoryEmpty; - extern Platform::StringReference IsInError; - extern Platform::StringReference BinaryDisplayValue; - extern Platform::StringReference OpenParenthesisCount; - } - [Windows::UI::Xaml::Data::Bindable] - public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + public value struct ButtonInfo + { + NumbersAndOperatorsEnum buttonId; + bool canSendNegate; + }; + + [Windows::UI::Xaml::Data::Bindable] public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: StandardCalculatorViewModel(); - void UpdateOperand(int pos, Platform::String^ text); - void UpdatecommandsInRecordingMode(); - int GetBitLengthType(); - int GetNumberBase(); + void UpdateOperand(int pos, Platform::String ^ text); OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); - OBSERVABLE_PROPERTY_RW(Platform::String^, DisplayValue); - OBSERVABLE_PROPERTY_RW(HistoryViewModel^, HistoryVM); - OBSERVABLE_PROPERTY_RW(bool, IsInError); - OBSERVABLE_PROPERTY_RW(bool, IsOperatorCommand); - OBSERVABLE_PROPERTY_RW(Platform::String^, DisplayStringExpression); - OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IVector^, ExpressionTokens); - OBSERVABLE_PROPERTY_RW(Platform::String^, DecimalDisplayValue); - OBSERVABLE_PROPERTY_RW(Platform::String^, HexDisplayValue); - OBSERVABLE_PROPERTY_RW(Platform::String^, OctalDisplayValue); - OBSERVABLE_PROPERTY_RW(Platform::String^, BinaryDisplayValue); - OBSERVABLE_PROPERTY_RW(Platform::String^, HexDisplayValue_AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, DecDisplayValue_AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, OctDisplayValue_AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, BinDisplayValue_AutomationName); - OBSERVABLE_PROPERTY_RW(bool, IsBinaryOperatorEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsUnaryOperatorEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsNegateEnabled); + OBSERVABLE_PROPERTY_RW(Platform::String ^, DisplayValue); + OBSERVABLE_PROPERTY_R(HistoryViewModel ^, HistoryVM); + OBSERVABLE_PROPERTY_RW(bool, IsAlwaysOnTop); + OBSERVABLE_PROPERTY_R(bool, IsBinaryBitFlippingEnabled); + PROPERTY_R(bool, IsOperandUpdatedUsingViewModel); + PROPERTY_R(int, TokenPosition); + PROPERTY_R(bool, IsOperandTextCompletelySelected); + PROPERTY_R(bool, KeyPressed); + PROPERTY_R(Platform::String ^, SelectedExpressionLastData); + OBSERVABLE_NAMED_PROPERTY_R(bool, IsInError); + OBSERVABLE_PROPERTY_R(bool, IsOperatorCommand); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, ExpressionTokens); + OBSERVABLE_PROPERTY_R(Platform::String ^, DecimalDisplayValue); + OBSERVABLE_PROPERTY_R(Platform::String ^, HexDisplayValue); + OBSERVABLE_PROPERTY_R(Platform::String ^, OctalDisplayValue); + OBSERVABLE_NAMED_PROPERTY_R(Platform::String ^, BinaryDisplayValue); + OBSERVABLE_NAMED_PROPERTY_R(Windows::Foundation::Collections::IVector ^, BinaryDigits); + OBSERVABLE_PROPERTY_R(Platform::String ^, HexDisplayValue_AutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, DecDisplayValue_AutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, OctDisplayValue_AutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, BinDisplayValue_AutomationName); + OBSERVABLE_PROPERTY_R(bool, IsBinaryOperatorEnabled); + OBSERVABLE_PROPERTY_R(bool, IsUnaryOperatorEnabled); + OBSERVABLE_PROPERTY_R(bool, IsNegateEnabled); OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsCurrentViewPinned); - OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IVector^, MemorizedNumbers); - OBSERVABLE_PROPERTY_RW(bool, IsMemoryEmpty); - OBSERVABLE_PROPERTY_RW(bool, IsFToEChecked); - OBSERVABLE_PROPERTY_RW(bool, IsFToEEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsHyperbolicChecked); - OBSERVABLE_PROPERTY_RW(bool, AreHEXButtonsEnabled); - NAMED_OBSERVABLE_PROPERTY_RW(Platform::String^, CalculationResultAutomationName); - NAMED_OBSERVABLE_PROPERTY_RW(Platform::String^, CalculationExpressionAutomationName); - OBSERVABLE_PROPERTY_RW(bool, IsShiftProgrammerChecked); - OBSERVABLE_PROPERTY_RW(bool, IsQwordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsDwordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsWordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsByteEnabled); - OBSERVABLE_PROPERTY_RW(Platform::String^, OpenParenthesisCount); - OBSERVABLE_PROPERTY_RW(int, CurrentRadixType); - OBSERVABLE_PROPERTY_RW(bool, AreTokensUpdated); - OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); - OBSERVABLE_PROPERTY_RW(bool, AreProgrammerRadixOperatorsEnabled); - OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IVector ^, MemorizedNumbers); + OBSERVABLE_NAMED_PROPERTY_RW(bool, IsMemoryEmpty); + OBSERVABLE_PROPERTY_R(bool, IsFToEChecked); + OBSERVABLE_PROPERTY_R(bool, IsFToEEnabled); + OBSERVABLE_PROPERTY_R(bool, AreHEXButtonsEnabled); + OBSERVABLE_PROPERTY_R(Platform::String ^, CalculationResultAutomationName); + OBSERVABLE_PROPERTY_R(Platform::String ^, CalculationExpressionAutomationName); + OBSERVABLE_PROPERTY_R(bool, IsShiftProgrammerChecked); + OBSERVABLE_PROPERTY_R(CalculatorApp::Common::NumberBase, CurrentRadixType); + OBSERVABLE_PROPERTY_R(bool, AreTokensUpdated); + OBSERVABLE_PROPERTY_R(bool, AreAlwaysOnTopResultsUpdated); + OBSERVABLE_PROPERTY_R(bool, AreProgrammerRadixOperatorsVisible); + OBSERVABLE_PROPERTY_R(bool, IsInputEmpty); + OBSERVABLE_PROPERTY_R(CalculatorApp::Common::Automation::NarratorAnnouncement ^, Announcement); + OBSERVABLE_PROPERTY_R(unsigned int, OpenParenthesisCount); COMMAND_FOR_METHOD(CopyCommand, StandardCalculatorViewModel::OnCopyCommand); COMMAND_FOR_METHOD(PasteCommand, StandardCalculatorViewModel::OnPasteCommand); COMMAND_FOR_METHOD(ButtonPressed, StandardCalculatorViewModel::OnButtonPressed); COMMAND_FOR_METHOD(ClearMemoryCommand, StandardCalculatorViewModel::OnClearMemoryCommand); - COMMAND_FOR_METHOD(PinUnpinAppBarButtonOnClicked, StandardCalculatorViewModel::OnPinUnpinCommand); COMMAND_FOR_METHOD(MemoryItemPressed, StandardCalculatorViewModel::OnMemoryItemPressed); COMMAND_FOR_METHOD(MemoryAdd, StandardCalculatorViewModel::OnMemoryAdd); COMMAND_FOR_METHOD(MemorySubtract, StandardCalculatorViewModel::OnMemorySubtract); - event HideMemoryClickedHandler^ HideMemoryClicked; - event ProgModeRadixChangeHandler^ ProgModeRadixChange; + event HideMemoryClickedHandler ^ HideMemoryClicked; - property bool IsShiftChecked { - bool get() { return m_isShiftChecked; } - void set(bool value) + property bool IsBitFlipChecked + { + bool get() { - if (m_isShiftChecked != value) - { - m_isShiftChecked = value; - RaisePropertyChanged(L"IsShiftChecked"); - } + return m_isBitFlipChecked; } - } - - property bool IsBitFlipChecked { - bool get() { return m_isBitFlipChecked; } void set(bool value) { if (m_isBitFlipChecked != value) { m_isBitFlipChecked = value; IsBinaryBitFlippingEnabled = IsProgrammer && m_isBitFlipChecked; - AreProgrammerRadixOperatorsEnabled = IsProgrammer && !m_isBitFlipChecked; + AreProgrammerRadixOperatorsVisible = IsProgrammer && !m_isBitFlipChecked; RaisePropertyChanged(L"IsBitFlipChecked"); } } } + static property Platform::String + ^ IsBitFlipCheckedPropertyName { Platform::String ^ get() { return Platform::StringReference(L"IsBitFlipChecked"); } } - property bool IsBinaryBitFlippingEnabled { - bool get() { return m_isBinaryBitFlippingEnabled; } - void set(bool value) + property CalculatorApp::Common::BitLength ValueBitLength + { + CalculatorApp::Common::BitLength get() { - if (m_isBinaryBitFlippingEnabled != value) - { - m_isBinaryBitFlippingEnabled = value; - RaisePropertyChanged(L"IsBinaryBitFlippingEnabled"); - } + return m_valueBitLength; } + void set(CalculatorApp::Common::BitLength value); } - property bool IsStandard { - bool get() { return m_isStandard; } + property bool IsStandard + { + bool get() + { + return m_isStandard; + } void set(bool value) { if (m_isStandard != value) @@ -156,8 +141,12 @@ namespace CalculatorApp } } - property bool IsScientific { - bool get() { return m_isScientific; } + property bool IsScientific + { + bool get() + { + return m_isScientific; + } void set(bool value) { if (m_isScientific != value) @@ -173,8 +162,12 @@ namespace CalculatorApp } } - property bool IsProgrammer { - bool get() { return m_isProgrammer; } + property bool IsProgrammer + { + bool get() + { + return m_isProgrammer; + } void set(bool value) { if (m_isProgrammer != value) @@ -185,7 +178,7 @@ namespace CalculatorApp IsBitFlipChecked = false; } IsBinaryBitFlippingEnabled = m_isProgrammer && IsBitFlipChecked; - AreProgrammerRadixOperatorsEnabled = m_isProgrammer && !IsBitFlipChecked; + AreProgrammerRadixOperatorsVisible = m_isProgrammer && !IsBitFlipChecked; if (value) { IsStandard = false; @@ -195,13 +188,19 @@ namespace CalculatorApp } } } + static property Platform::String + ^ IsProgrammerPropertyName { Platform::String ^ get() { return Platform::StringReference(L"IsProgrammer"); } } - property bool IsEditingEnabled { - bool get() { return m_isEditingEnabled; } - void set(bool value) { + property bool IsEditingEnabled + { + bool get() + { + return m_isEditingEnabled; + } + void set(bool value) + { if (m_isEditingEnabled != value) { - // Numbers::Common::KeyboardShortcutManager::IsCalculatorInEditingMode = value; m_isEditingEnabled = value; bool currentEditToggleValue = !m_isEditingEnabled; IsBinaryOperatorEnabled = currentEditToggleValue; @@ -214,13 +213,22 @@ namespace CalculatorApp } } - property bool IsEngineRecording { - bool get() { return m_standardCalculatorManager.IsEngineRecording(); } + property bool IsEngineRecording + { + bool get() + { + return m_standardCalculatorManager.IsEngineRecording(); + } } - property bool IsOperandEnabled { - bool get() { return m_isOperandEnabled; } - void set(bool value) { + property bool IsOperandEnabled + { + bool get() + { + return m_isOperandEnabled; + } + void set(bool value) + { if (m_isOperandEnabled != value) { m_isOperandEnabled = value; @@ -232,173 +240,138 @@ namespace CalculatorApp } } - property int TokenPosition - { - int get() { return m_tokenPosition; } - void set(int value) { m_tokenPosition = value; } - } - - property Platform::String^ SelectedExpressionLastData - { - Platform::String^ get() { return m_selectedExpressionLastData; } - void set(Platform::String^ value) { m_selectedExpressionLastData = value; } - } - - property bool KeyPressed - { - bool get() { return m_keyPressed; } - void set(bool value) { m_keyPressed = value; } - } - - property bool IsOperandUpdatedUsingViewModel - { - bool get() { return m_operandUpdated; } - void set(bool value) { m_operandUpdated = value; } - } - - property bool IsOperandTextCompletelySelected - { - bool get() { return m_completeTextSelection; } - void set(bool value) { m_completeTextSelection = value; } - } - - property Platform::String^ LeftParenthesisAutomationName - { - Platform::String^ get() - { - return GetLeftParenthesisAutomationName(); - } - } + // Used by unit tests + void ResetCalcManager(bool clearMemory); + void SendCommandToCalcManager(int command); internal: - void OnPaste(Platform::String^ pastedString, CalculatorApp::Common::ViewMode mode); - void OnCopyCommand(Platform::Object^ parameter); - void OnPasteCommand(Platform::Object^ parameter); + void OnPaste(Platform::String ^ pastedString); + void OnCopyCommand(Platform::Object ^ parameter); + void OnPasteCommand(Platform::Object ^ parameter); - NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate); + ButtonInfo MapCharacterToButtonId(char16 ch); // Memory feature related methods. They are internal because they need to called from the MainPage code-behind void OnMemoryButtonPressed(); - void OnMemoryItemPressed(Platform::Object^ memoryItemPosition); - void OnMemoryAdd(Platform::Object^ memoryItemPosition); - void OnMemorySubtract(Platform::Object^ memoryItemPosition); - void OnMemoryClear(_In_ Platform::Object^ memoryItemPosition); - void OnPinUnpinCommand(Platform::Object^ parameter); + void OnMemoryItemPressed(Platform::Object ^ memoryItemPosition); + void OnMemoryAdd(Platform::Object ^ memoryItemPosition); + void OnMemorySubtract(Platform::Object ^ memoryItemPosition); + void OnMemoryClear(_In_ Platform::Object ^ memoryItemPosition); - void SetPrimaryDisplay(_In_ std::wstring const&displayString, _In_ bool isError); + void OnInputChanged(); void DisplayPasteError(); - void SetTokens(_Inout_ std::shared_ptr>> const &tokens); - void SetExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands); - void SetHistoryExpressionDisplay(_Inout_ std::shared_ptr>> const &tokens, _Inout_ std::shared_ptr>> const &commands); - void SetParenthesisCount(_In_ const std::wstring& parenthesisCount); + void SetParenthesisCount(_In_ unsigned int parenthesisCount); void SetOpenParenthesisCountNarratorAnnouncement(); void OnNoRightParenAdded(); void SetNoParenAddedNarratorAnnouncement(); void OnMaxDigitsReached(); void OnBinaryOperatorReceived(); void OnMemoryItemChanged(unsigned int indexOfMemory); + void SetBitshiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement); - Platform::Array^ Serialize(); - void Deserialize(Platform::Array^ state); - - Platform::String^ GetLocalizedStringFormat(Platform::String^ format, Platform::String^ displayValue); - void OnPropertyChanged(Platform::String^ propertyname); + Platform::String ^ GetLocalizedStringFormat(Platform::String ^ format, Platform::String ^ displayValue); + void OnPropertyChanged(Platform::String ^ propertyname); void SetCalculatorType(CalculatorApp::Common::ViewMode targetState); - Platform::String^ GetRawDisplayValue(); + Platform::String ^ GetRawDisplayValue(); void Recalculate(bool fromHistory = false); bool IsOperator(CalculationManager::Command cmdenum); void FtoEButtonToggled(); - void SwitchProgrammerModeBase(RADIX_TYPE calculatorBase); + void SwitchProgrammerModeBase(CalculatorApp::Common::NumberBase calculatorBase); void SetMemorizedNumbersString(); void SwitchAngleType(NumbersAndOperatorsEnum num); void ResetDisplay(); - RADIX_TYPE GetCurrentRadixType() { return (RADIX_TYPE)m_CurrentRadixType; } + void SetPrecision(int32_t precision); - void UpdateMaxIntDigits() { m_standardCalculatorManager.UpdateMaxIntDigits(); } - NumbersAndOperatorsEnum GetCurrentAngleType() { return m_CurrentAngleType; } - + void UpdateMaxIntDigits() + { + m_standardCalculatorManager.UpdateMaxIntDigits(); + } + NumbersAndOperatorsEnum GetCurrentAngleType() + { + return m_CurrentAngleType; + } + void SelectHistoryItem(HistoryItemViewModel ^ item); private: void SetMemorizedNumbers(const std::vector& memorizedNumbers); void UpdateProgrammerPanelDisplay(); void HandleUpdatedOperandData(CalculationManager::Command cmdenum); + void SetPrimaryDisplay(_In_ Platform::String ^ displayStringValue, _In_ bool isError); + void SetExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands); + void SetHistoryExpressionDisplay( + _Inout_ std::shared_ptr>> const& tokens, + _Inout_ std::shared_ptr>> const& commands); + void SetTokens(_Inout_ std::shared_ptr>> const& tokens); NumbersAndOperatorsEnum ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter); + static RadixType GetRadixTypeFromNumberBase(CalculatorApp::Common::NumberBase base); NumbersAndOperatorsEnum m_CurrentAngleType; wchar_t m_decimalSeparator; CalculatorDisplay m_calculatorDisplay; CalculatorApp::EngineResourceProvider m_resourceProvider; CalculationManager::CalculatorManager m_standardCalculatorManager; - Platform::String^ m_expressionAutomationNameFormat; - Platform::String^ m_localizedCalculationResultAutomationFormat; - Platform::String^ m_localizedCalculationResultDecimalAutomationFormat; - Platform::String^ m_localizedHexaDecimalAutomationFormat; - Platform::String^ m_localizedDecimalAutomationFormat; - Platform::String^ m_localizedOctalAutomationFormat; - Platform::String^ m_localizedBinaryAutomationFormat; - Platform::String^ m_localizedMaxDigitsReachedAutomationFormat; - Platform::String^ m_localizedButtonPressFeedbackAutomationFormat; - Platform::String^ m_localizedMemorySavedAutomationFormat; - Platform::String^ m_localizedMemoryItemChangedAutomationFormat; - Platform::String^ m_localizedMemoryItemClearedAutomationFormat; - Platform::String^ m_localizedMemoryCleared; - Platform::String^ m_localizedOpenParenthesisCountChangedAutomationFormat; - Platform::String^ m_localizedNoRightParenthesisAddedFormat; + Platform::String ^ m_expressionAutomationNameFormat; + Platform::String ^ m_localizedCalculationResultAutomationFormat; + Platform::String ^ m_localizedCalculationResultDecimalAutomationFormat; + Platform::String ^ m_localizedHexaDecimalAutomationFormat; + Platform::String ^ m_localizedDecimalAutomationFormat; + Platform::String ^ m_localizedOctalAutomationFormat; + Platform::String ^ m_localizedBinaryAutomationFormat; + Platform::String ^ m_localizedMaxDigitsReachedAutomationFormat; + Platform::String ^ m_localizedButtonPressFeedbackAutomationFormat; + Platform::String ^ m_localizedMemorySavedAutomationFormat; + Platform::String ^ m_localizedMemoryItemChangedAutomationFormat; + Platform::String ^ m_localizedMemoryItemClearedAutomationFormat; + Platform::String ^ m_localizedMemoryCleared; + Platform::String ^ m_localizedOpenParenthesisCountChangedAutomationFormat; + Platform::String ^ m_localizedNoRightParenthesisAddedFormat; - bool m_pinned; bool m_isOperandEnabled; bool m_isEditingEnabled; bool m_isStandard; bool m_isScientific; bool m_isProgrammer; - bool m_isBinaryBitFlippingEnabled; bool m_isBitFlipChecked; - bool m_isShiftChecked; bool m_isRtlLanguage; - int m_tokenPosition; - bool m_keyPressed; bool m_operandUpdated; - bool m_completeTextSelection; bool m_isLastOperationHistoryLoad; - Platform::String^ m_selectedExpressionLastData; - Common::DisplayExpressionToken^ m_selectedExpressionToken; - Platform::String^ m_leftParenthesisAutomationFormat; + CalculatorApp::Common::BitLength m_valueBitLength; + Platform::String ^ m_selectedExpressionLastData; + Common::DisplayExpressionToken ^ m_selectedExpressionToken; - Platform::String^ LocalizeDisplayValue(_In_ std::wstring const &displayValue, _In_ bool isError); - Platform::String^ CalculateNarratorDisplayValue(_In_ std::wstring const &displayValue, _In_ Platform::String^ localizedDisplayValue, _In_ bool isError); - CalculatorApp::Common::Automation::NarratorAnnouncement^ GetDisplayUpdatedNarratorAnnouncement(); - Platform::String^ GetCalculatorExpressionAutomationName(); - Platform::String^ GetNarratorStringReadRawNumbers(_In_ Platform::String^ localizedDisplayValue); + Platform::String ^ LocalizeDisplayValue(_In_ std::wstring const& displayValue); + Platform::String + ^ CalculateNarratorDisplayValue(_In_ std::wstring const& displayValue, _In_ Platform::String ^ localizedDisplayValue); + CalculatorApp::Common::Automation::NarratorAnnouncement ^ GetDisplayUpdatedNarratorAnnouncement(); + Platform::String ^ GetCalculatorExpressionAutomationName(); + Platform::String ^ GetNarratorStringReadRawNumbers(_In_ Platform::String ^ localizedDisplayValue); CalculationManager::Command ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation); void DisableButtons(CalculationManager::CommandType selectedExpressionCommandType); - Platform::String^ GetLeftParenthesisAutomationName(); - Platform::String^ m_feedbackForButtonPress; - void OnButtonPressed(Platform::Object^ parameter); - void OnClearMemoryCommand(Platform::Object^ parameter); + Platform::String ^ m_feedbackForButtonPress; + void OnButtonPressed(Platform::Object ^ parameter); + void OnClearMemoryCommand(Platform::Object ^ parameter); std::wstring AddPadding(std::wstring); size_t LengthWithoutPadding(std::wstring); - std::shared_ptr>> m_tokens; - std::shared_ptr>> m_commands; + std::shared_ptr>> m_tokens; + std::shared_ptr>> m_commands; // Token types - bool IsUnaryOp(int nOpCode); - bool IsBinOp(int nOpcode); - bool IsTrigOp(int nOpCode); - bool IsOpnd(int nOpCode); - bool IsRecoverableCommand(int nOpCode); + bool IsUnaryOp(CalculationManager::Command command); + bool IsBinOp(CalculationManager::Command command); + bool IsTrigOp(CalculationManager::Command command); + bool IsOpnd(CalculationManager::Command command); + bool IsRecoverableCommand(CalculationManager::Command command); - CalculationManager::CommandType GetSelectedTokenType(_In_ unsigned int); void SaveEditedCommand(_In_ unsigned int index, _In_ CalculationManager::Command command); - bool IsViewPinned(); - void SetViewPinnedState(bool pinned); + CalculatorApp::Common::ViewMode GetCalculatorMode(); friend class CalculatorDisplay; - friend class CalculatorFunctionalTests::HistoryTests; friend class CalculatorUnitTests::MultiWindowUnitTests; - friend class CalculatorUnitTests::TimerTests; }; } } diff --git a/src/CalcViewModel/UnitConverterViewModel.cpp b/src/CalcViewModel/UnitConverterViewModel.cpp index 7ae1eb2b..c2ad77e4 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -45,7 +45,7 @@ constexpr unsigned int TIMER_INTERVAL_IN_MS = 10000; const TimeSpan SUPPLEMENTARY_VALUES_INTERVAL = { 10 * TIMER_INTERVAL_IN_MS }; -static Unit^ EMPTY_UNIT = ref new Unit(UCM::EMPTY_UNIT); +static Unit ^ EMPTY_UNIT = ref new Unit(UCM::EMPTY_UNIT); constexpr size_t UNIT_LIST = 0; constexpr size_t SELECTED_SOURCE_UNIT = 1; @@ -53,109 +53,101 @@ constexpr size_t SELECTED_TARGET_UNIT = 2; // x millisecond delay before we consider conversion to be final constexpr unsigned int CONVERSION_FINALIZED_DELAY_IN_MS = 1000; +const wregex regexTrimSpacesStart = wregex(L"^\\s+"); +const wregex regexTrimSpacesEnd = wregex(L"\\s+$"); -namespace CalculatorApp::ViewModel +namespace { - namespace UnitConverterViewModelProperties - { - StringReference CurrentCategory(L"CurrentCategory"); - StringReference Unit1(L"Unit1"); - StringReference Unit2(L"Unit2"); - StringReference Value1Active(L"Value1Active"); - StringReference Value2Active(L"Value2Active"); - StringReference Value1(L"Value1"); - StringReference Value2(L"Value2"); - StringReference Value1AutomationName(L"Value1AutomationName"); - StringReference Value2AutomationName(L"Value2AutomationName"); - StringReference SupplementaryVisibility(L"SupplementaryVisibility"); - StringReference SupplementaryResults(L"SupplementaryResults"); - StringReference Unit1AutomationName(L"Unit1AutomationName"); - StringReference Unit2AutomationName(L"Unit2AutomationName"); - StringReference CurrencySymbol1(L"CurrencySymbol1"); - StringReference CurrencySymbol2(L"CurrencySymbol2"); - StringReference CurrencySymbolVisibility(L"CurrencySymbolVisibility"); - StringReference CurrencyRatioEquality(L"CurrencyRatioEquality"); - StringReference CurrencyRatioEqualityAutomationName(L"CurrencyRatioEqualityAutomationName"); - StringReference NetworkBehavior(L"NetworkBehavior"); - StringReference CurrencyDataLoadFailed(L"CurrencyDataLoadFailed"); - StringReference CurrencyDataIsWeekOld(L"CurrencyDataIsWeekOld"); - StringReference IsCurrencyLoadingVisible(L"IsCurrencyLoadingVisible"); - } - - namespace UnitConverterResourceKeys - { - StringReference ValueFromFormat(L"Format_ValueFrom"); - StringReference ValueFromDecimalFormat(L"Format_ValueFrom_Decimal"); - StringReference ValueToFormat(L"Format_ValueTo"); - StringReference ConversionResultFormat(L"Format_ConversionResult"); - StringReference InputUnit_Name(L"InputUnit_Name"); - StringReference OutputUnit_Name(L"OutputUnit_Name"); - StringReference MaxDigitsReachedFormat(L"Format_MaxDigitsReached"); - StringReference UpdatingCurrencyRates(L"UpdatingCurrencyRates"); - StringReference CurrencyRatesUpdated(L"CurrencyRatesUpdated"); - StringReference CurrencyRatesUpdateFailed(L"CurrencyRatesUpdateFailed"); - } + StringReference CurrentCategoryPropertyName(L"CurrentCategory"); + StringReference Unit1AutomationNamePropertyName(L"Unit1AutomationName"); + StringReference Unit2AutomationNamePropertyName(L"Unit2AutomationName"); + StringReference Unit1PropertyName(L"Unit1"); + StringReference Unit2PropertyName(L"Unit2"); + StringReference Value1PropertyName(L"Value1"); + StringReference Value2PropertyName(L"Value2"); + StringReference Value1ActivePropertyName(L"Value1Active"); + StringReference Value2ActivePropertyName(L"Value2Active"); + StringReference Value1AutomationNamePropertyName(L"Value1AutomationName"); + StringReference Value2AutomationNamePropertyName(L"Value2AutomationName"); + StringReference CurrencySymbol1PropertyName(L"CurrencySymbol1"); + StringReference CurrencySymbol2PropertyName(L"CurrencySymbol2"); + StringReference CurrencySymbolVisibilityPropertyName(L"CurrencySymbolVisibility"); + StringReference SupplementaryVisibilityPropertyName(L"SupplementaryVisibility"); } -UnitConverterViewModel::UnitConverterViewModel(const shared_ptr& model) : - m_model(model), - m_resettingTimer(false), - m_value1cp(ConversionParameter::Source), - m_Value1Active(true), - m_Value2Active(false), - m_Value1("0"), - m_Value2("0"), - m_valueToUnlocalized(L"0"), - m_valueFromUnlocalized(L"0"), - m_relocalizeStringOnSwitch(false), - m_Categories(ref new Vector()), - m_Units(ref new Vector()), - m_SupplementaryResults(ref new Vector), - m_IsDropDownOpen(false), - m_IsDropDownEnabled(true), - m_IsCurrencyLoadingVisible(false), - m_isCurrencyDataLoaded(false), - m_lastAnnouncedFrom(L""), - m_lastAnnouncedTo(L""), - m_lastAnnouncedConversionResult(L""), - m_isValue1Updating(false), - m_isValue2Updating(false), - m_Announcement(nullptr), - m_Mode(ViewMode::None), - m_CurrencySymbol1(L""), - m_CurrencySymbol2(L""), - m_IsCurrencyCurrentCategory(false), - m_CurrencyRatioEquality(L""), - m_CurrencyRatioEqualityAutomationName(L""), - m_isInputBlocked(false), - m_CurrencyDataLoadFailed(false) +namespace CalculatorApp::ViewModel::UnitConverterResourceKeys { + StringReference ValueFromFormat(L"Format_ValueFrom"); + StringReference ValueFromDecimalFormat(L"Format_ValueFrom_Decimal"); + StringReference ValueToFormat(L"Format_ValueTo"); + StringReference ConversionResultFormat(L"Format_ConversionResult"); + StringReference InputUnit_Name(L"InputUnit_Name"); + StringReference OutputUnit_Name(L"OutputUnit_Name"); + StringReference MaxDigitsReachedFormat(L"Format_MaxDigitsReached"); + StringReference UpdatingCurrencyRates(L"UpdatingCurrencyRates"); + StringReference CurrencyRatesUpdated(L"CurrencyRatesUpdated"); + StringReference CurrencyRatesUpdateFailed(L"CurrencyRatesUpdateFailed"); +} + +UnitConverterViewModel::UnitConverterViewModel(const shared_ptr& model) + : m_model(model) + , m_resettingTimer(false) + , m_value1cp(ConversionParameter::Source) + , m_Value1Active(true) + , m_Value2Active(false) + , m_Value1("0") + , m_Value2("0") + , m_valueToUnlocalized(L"0") + , m_valueFromUnlocalized(L"0") + , m_relocalizeStringOnSwitch(false) + , m_Categories(ref new Vector()) + , m_Units(ref new Vector()) + , m_SupplementaryResults(ref new Vector) + , m_IsDropDownOpen(false) + , m_IsDropDownEnabled(true) + , m_IsCurrencyLoadingVisible(false) + , m_isCurrencyDataLoaded(false) + , m_lastAnnouncedFrom(L"") + , m_lastAnnouncedTo(L"") + , m_lastAnnouncedConversionResult(L"") + , m_isValue1Updating(false) + , m_isValue2Updating(false) + , m_Announcement(nullptr) + , m_Mode(ViewMode::None) + , m_CurrencySymbol1(L"") + , m_CurrencySymbol2(L"") + , m_IsCurrencyCurrentCategory(false) + , m_CurrencyRatioEquality(L"") + , m_CurrencyRatioEqualityAutomationName(L"") + , m_isInputBlocked(false) + , m_CurrencyDataLoadFailed(false) +{ + auto localizationService = LocalizationService::GetInstance(); m_model->SetViewModelCallback(make_shared(this)); m_model->SetViewModelCurrencyCallback(make_shared(this)); - m_decimalFormatter = LocalizationService::GetRegionalSettingsAwareDecimalFormatter(); + m_decimalFormatter = localizationService->GetRegionalSettingsAwareDecimalFormatter(); m_decimalFormatter->FractionDigits = 0; m_decimalFormatter->IsGrouped = true; m_decimalSeparator = LocalizationSettings::GetInstance().GetDecimalSeparator(); - m_currencyFormatter = LocalizationService::GetRegionalSettingsAwareCurrencyFormatter(); + m_currencyFormatter = localizationService->GetRegionalSettingsAwareCurrencyFormatter(); m_currencyFormatter->IsGrouped = true; m_currencyFormatter->Mode = CurrencyFormatterMode::UseCurrencyCode; m_currencyFormatter->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown); m_currencyMaxFractionDigits = m_currencyFormatter->FractionDigits; auto resourceLoader = AppResourceProvider::GetInstance(); - m_localizedValueFromFormat = resourceLoader.GetResourceString(UnitConverterResourceKeys::ValueFromFormat); - m_localizedValueToFormat = resourceLoader.GetResourceString(UnitConverterResourceKeys::ValueToFormat); - m_localizedConversionResultFormat = resourceLoader.GetResourceString(UnitConverterResourceKeys::ConversionResultFormat); - m_localizedValueFromDecimalFormat = resourceLoader.GetResourceString(UnitConverterResourceKeys::ValueFromDecimalFormat); - m_localizedInputUnitName = resourceLoader.GetResourceString(UnitConverterResourceKeys::InputUnit_Name); - m_localizedOutputUnitName = resourceLoader.GetResourceString(UnitConverterResourceKeys::OutputUnit_Name); + m_localizedValueFromFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ValueFromFormat); + m_localizedValueToFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ValueToFormat); + m_localizedConversionResultFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ConversionResultFormat); + m_localizedValueFromDecimalFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ValueFromDecimalFormat); + m_localizedInputUnitName = resourceLoader->GetResourceString(UnitConverterResourceKeys::InputUnit_Name); + m_localizedOutputUnitName = resourceLoader->GetResourceString(UnitConverterResourceKeys::OutputUnit_Name); Unit1AutomationName = m_localizedInputUnitName; Unit2AutomationName = m_localizedOutputUnitName; IsDecimalEnabled = true; - m_IsFirstTime = true; m_model->Initialize(); PopulateData(); } @@ -163,7 +155,6 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptrSendCommand(UCM::Command::Reset); - m_IsFirstTime = true; OnCategoryChanged(nullptr); } @@ -172,13 +163,14 @@ void UnitConverterViewModel::PopulateData() InitializeView(); } -void UnitConverterViewModel::OnCategoryChanged(Object^ parameter) +void UnitConverterViewModel::OnCategoryChanged(Object ^ parameter) { - UCM::Category currentCategory = CurrentCategory->GetModelCategory(); - IsCurrencyCurrentCategory = currentCategory.id == NavCategory::Serialize(ViewMode::Currency); - m_model->SendCommand(UCM::Command::Clear); + ResetCategory(); +} +void UnitConverterViewModel::ResetCategory() +{ m_isInputBlocked = false; SetSelectedUnits(); @@ -202,12 +194,10 @@ void UnitConverterViewModel::BuildUnitList(const vector& modelUnitLis m_Units->Clear(); for (const UCM::Unit& modelUnit : modelUnitList) { - if (modelUnit.isWhimsical) + if (!modelUnit.isWhimsical) { - continue; + m_Units->Append(ref new Unit(modelUnit)); } - - m_Units->Append(ref new Unit(modelUnit)); } if (m_Units->Size == 0) @@ -216,9 +206,9 @@ void UnitConverterViewModel::BuildUnitList(const vector& modelUnitLis } } -Unit^ UnitConverterViewModel::FindUnitInList(UCM::Unit target) +Unit ^ UnitConverterViewModel::FindUnitInList(UCM::Unit target) { - for (Unit^ vmUnit : m_Units) + for (Unit ^ vmUnit : m_Units) { UCM::Unit modelUnit = vmUnit->GetModelUnit(); if (modelUnit.id == target.id) @@ -230,7 +220,7 @@ Unit^ UnitConverterViewModel::FindUnitInList(UCM::Unit target) return EMPTY_UNIT; } -void UnitConverterViewModel::OnUnitChanged(Object^ parameter) +void UnitConverterViewModel::OnUnitChanged(Object ^ parameter) { if ((m_Unit1 == nullptr) || (m_Unit2 == nullptr)) { @@ -244,18 +234,11 @@ void UnitConverterViewModel::OnUnitChanged(Object^ parameter) // End timer to show results immediately m_supplementaryResultsTimer->Cancel(); } - if (!m_IsFirstTime) - { - SaveUserPreferences(); - } - else - { - RestoreUserPreferences(); - m_IsFirstTime = false; - } + + SaveUserPreferences(); } -void UnitConverterViewModel::OnSwitchActive(Platform::Object^ unused) +void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused) { // this can be false if this switch occurs without the user having explicitly updated any strings // (for example, during deserialization). We only want to try this cleanup if there's actually @@ -278,19 +261,19 @@ void UnitConverterViewModel::OnSwitchActive(Platform::Object^ unused) } m_valueFromUnlocalized.swap(m_valueToUnlocalized); - Utils::Swap(&m_localizedValueFromFormat, &m_localizedValueToFormat); + swap(m_localizedValueFromFormat, m_localizedValueToFormat); - Utils::Swap(&m_Unit1AutomationName, &m_Unit2AutomationName); - RaisePropertyChanged(UnitConverterViewModelProperties::Unit1AutomationName); - RaisePropertyChanged(UnitConverterViewModelProperties::Unit2AutomationName); + swap(m_Unit1AutomationName, m_Unit2AutomationName); + RaisePropertyChanged(Unit1AutomationNamePropertyName); + RaisePropertyChanged(Unit2AutomationNamePropertyName); m_isInputBlocked = false; m_model->SwitchActive(m_valueFromUnlocalized); } -String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings) +String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings) { - Platform::String^ result; + Platform::String ^ result; if (stringToLocalize.empty()) { @@ -348,10 +331,15 @@ String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& str if (pos != wstring::npos) { currencyResult.erase(pos, currencyCode.length()); - pos = currencyResult.find(L'\u00a0'); // non-breaking space - if (pos != wstring::npos) + std::wsmatch sm; + if (regex_search(currencyResult, sm, regexTrimSpacesStart)) { - currencyResult.erase(pos, 1); + currencyResult.erase(sm.prefix().length(), sm.length()); + } + + if (regex_search(currencyResult, sm, regexTrimSpacesEnd)) + { + currencyResult.erase(sm.prefix().length(), sm.length()); } } @@ -369,7 +357,7 @@ String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& str // Since the output from GetLocaleInfoEx() and DecimalFormatter are differing for decimal string // we are adding the below work-around of editing the string returned by DecimalFormatter // and replacing the decimal separator with the one returned by GetLocaleInfoEx() - String^ formattedSampleString = m_decimalFormatter->Format(stod("1.1")); + String ^ formattedSampleString = m_decimalFormatter->Format(stod("1.1")); wstring formattedSampleWString = wstring(formattedSampleString->Data()); wstring resultWithDecimal = wstring(result->Data()); @@ -393,13 +381,12 @@ String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& str } result = L"-" + result; } - result = Utils::LRE + result + Utils::PDF; return result; } void UnitConverterViewModel::DisplayPasteError() { - String^ errorMsg = AppResourceProvider::GetInstance().GetCEngineString(SIDS_DOMAIN); /*SIDS_DOMAIN is for "invalid input"*/ + String ^ errorMsg = AppResourceProvider::GetInstance()->GetCEngineString(StringReference(SIDS_DOMAIN)); /*SIDS_DOMAIN is for "invalid input"*/ Value1 = errorMsg; Value2 = errorMsg; m_relocalizeStringOnSwitch = false; @@ -407,9 +394,9 @@ void UnitConverterViewModel::DisplayPasteError() void UnitConverterViewModel::UpdateDisplay(const wstring& from, const wstring& to) { - String^ fromStr = this->ConvertToLocalizedString(from, true); + String ^ fromStr = this->ConvertToLocalizedString(from, true); UpdateInputBlocked(from); - String^ toStr = this->ConvertToLocalizedString(to, true); + String ^ toStr = this->ConvertToLocalizedString(to, true); bool updatedValueFrom = ValueFrom != fromStr; bool updatedValueTo = ValueTo != toStr; @@ -469,12 +456,12 @@ void UnitConverterViewModel::UpdateSupplementaryResults(const std::vectorIsActive = true; } -void UnitConverterViewModel::OnButtonPressed(Platform::Object^ parameter) +void UnitConverterViewModel::OnButtonPressed(Platform::Object ^ parameter) { NumbersAndOperatorsEnum numOpEnum = CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(parameter); UCM::Command command = CommandFromButtonId(numOpEnum); @@ -485,43 +472,25 @@ void UnitConverterViewModel::OnButtonPressed(Platform::Object^ parameter) return; } - static const vector OPERANDS = { - UCM::Command::Zero, - UCM::Command::One, - UCM::Command::Two, - UCM::Command::Three, - UCM::Command::Four, - UCM::Command::Five, - UCM::Command::Six, - UCM::Command::Seven, - UCM::Command::Eight, - UCM::Command::Nine - }; + static constexpr UCM::Command OPERANDS[] = { UCM::Command::Zero, UCM::Command::One, UCM::Command::Two, UCM::Command::Three, UCM::Command::Four, + UCM::Command::Five, UCM::Command::Six, UCM::Command::Seven, UCM::Command::Eight, UCM::Command::Nine }; + if (m_isInputBlocked) + { + return; + } + m_model->SendCommand(command); - if (find(begin(OPERANDS), end(OPERANDS), command) != OPERANDS.end()) - { - if (m_isInputBlocked) - { - return; - } - - if (m_IsCurrencyCurrentCategory) - { - StartConversionResultTimer(); - } - } - - m_model->SendCommand(command); + TraceLogger::GetInstance()->LogConverterInputReceived(Mode); } -void UnitConverterViewModel::OnCopyCommand(Platform::Object^ parameter) +void UnitConverterViewModel::OnCopyCommand(Platform::Object ^ parameter) { - //EventWriteClipboardCopy_Start(); + // EventWriteClipboardCopy_Start(); CopyPasteManager::CopyToClipboard(ref new Platform::String(m_valueFromUnlocalized.c_str())); - //EventWriteClipboardCopy_Stop(); + // EventWriteClipboardCopy_Stop(); } -void UnitConverterViewModel::OnPasteCommand(Platform::Object^ parameter) +void UnitConverterViewModel::OnPasteCommand(Platform::Object ^ parameter) { // if there's nothing to copy early out if (!CopyPasteManager::HasStringToPaste()) @@ -530,13 +499,12 @@ void UnitConverterViewModel::OnPasteCommand(Platform::Object^ parameter) } // Ensure that the paste happens on the UI thread - //EventWriteClipboardPaste_Start(); + // EventWriteClipboardPaste_Start(); // Any converter ViewMode is fine here. - CopyPasteManager::GetStringToPaste(m_Mode, NavCategory::GetGroupType(m_Mode)).then( - [this](String^ pastedString) - { - OnPaste(pastedString, m_Mode); - }, concurrency::task_continuation_context::use_current()); + + auto that(this); + create_task(CopyPasteManager::GetStringToPaste(m_Mode, NavCategory::GetGroupType(m_Mode), NumberBase::Unknown, BitLength::BitLengthUnknown)) + .then([that](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } void UnitConverterViewModel::InitializeView() @@ -544,7 +512,7 @@ void UnitConverterViewModel::InitializeView() vector categories = m_model->GetCategories(); for (UINT i = 0; i < categories.size(); i++) { - Category^ category = ref new Category(categories[i]); + Category ^ category = ref new Category(categories[i]); m_Categories->Append(category); } @@ -552,17 +520,17 @@ void UnitConverterViewModel::InitializeView() CurrentCategory = ref new Category(m_model->GetCurrentCategory()); } -void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) +void UnitConverterViewModel::OnPropertyChanged(Platform::String ^ prop) { static bool isCategoryChanging = false; - if (prop->Equals(UnitConverterViewModelProperties::CurrentCategory)) + if (prop == CurrentCategoryPropertyName) { isCategoryChanging = true; CategoryChanged->Execute(nullptr); isCategoryChanging = false; } - else if (prop->Equals(UnitConverterViewModelProperties::Unit1) || prop->Equals(UnitConverterViewModelProperties::Unit2)) + else if (prop == Unit1PropertyName || prop == Unit2PropertyName) { // Category changes will handle updating units after they've both been updated. // This event should only be used to update units from explicit user interaction. @@ -571,7 +539,7 @@ void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) UnitChanged->Execute(nullptr); } // Get the localized automation name for each CalculationResults field - if (prop->Equals(UnitConverterViewModelProperties::Unit1)) + if (prop == Unit1PropertyName) { UpdateValue1AutomationName(); } @@ -580,15 +548,15 @@ void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) UpdateValue2AutomationName(); } } - else if (prop->Equals(UnitConverterViewModelProperties::Value1)) + else if (prop == Value1PropertyName) { UpdateValue1AutomationName(); } - else if (prop->Equals(UnitConverterViewModelProperties::Value2)) + else if (prop == Value2PropertyName) { UpdateValue2AutomationName(); } - else if (prop->Equals(UnitConverterViewModelProperties::Value1Active) || prop->Equals(UnitConverterViewModelProperties::Value2Active)) + else if (prop == Value1ActivePropertyName || prop == Value2ActivePropertyName) { // if one of the values is activated, and as a result both are true, it means // that we're trying to switch. @@ -600,11 +568,11 @@ void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) UpdateValue1AutomationName(); UpdateValue2AutomationName(); } - else if (prop->Equals(UnitConverterViewModelProperties::SupplementaryResults)) + else if (prop == SupplementaryResultsPropertyName) { - RaisePropertyChanged(UnitConverterViewModelProperties::SupplementaryVisibility); + RaisePropertyChanged(SupplementaryVisibilityPropertyName); } - else if (prop->Equals(UnitConverterViewModelProperties::Value1AutomationName)) + else if (prop == Value1AutomationNamePropertyName) { m_isValue1Updating = false; if (!m_isValue2Updating) @@ -612,7 +580,7 @@ void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) AnnounceConversionResult(); } } - else if (prop->Equals(UnitConverterViewModelProperties::Value2AutomationName)) + else if (prop == Value2AutomationNamePropertyName) { m_isValue2Updating = false; if (!m_isValue1Updating) @@ -620,66 +588,18 @@ void UnitConverterViewModel::OnPropertyChanged(Platform::String^ prop) AnnounceConversionResult(); } } - else if (prop->Equals(UnitConverterViewModelProperties::CurrencySymbol1) || prop->Equals(UnitConverterViewModelProperties::CurrencySymbol2)) + else if (prop == CurrencySymbol1PropertyName || prop == CurrencySymbol2PropertyName) { - RaisePropertyChanged(UnitConverterViewModelProperties::CurrencySymbolVisibility); + RaisePropertyChanged(CurrencySymbolVisibilityPropertyName); } } -String^ UnitConverterViewModel::Serialize() -{ - wstringstream out(wstringstream::out); - const wchar_t * delimiter = L"[;;;]"; - out << std::to_wstring(m_resettingTimer) << delimiter; - out << std::to_wstring(static_cast(m_value1cp)) << delimiter; - out << m_Value1Active << delimiter << m_Value2Active << delimiter; - out << m_Value1->Data() << delimiter << m_Value2->Data() << delimiter; - out << m_valueFromUnlocalized << delimiter << m_valueToUnlocalized << delimiter << L"[###]"; - wstring unitConverterSerializedData = m_model->Serialize(); - - if (!unitConverterSerializedData.empty()) - { - out << m_model->Serialize() << L"[###]"; - String^ serializedData = ref new String(wstring(out.str()).c_str()); - return serializedData; - } - else - { - return nullptr; - } -} - -void UnitConverterViewModel::Deserialize(Platform::String^ state) -{ - wstring serializedData = wstring(state->Data()); - vector tokens = UCM::UnitConverter::StringToVector(serializedData, L"[###]"); - assert(tokens.size() >= 2); - vector viewModelData = UCM::UnitConverter::StringToVector(tokens[0], L"[;;;]"); - assert(viewModelData.size() == EXPECTEDVIEWMODELDATATOKENS); - m_resettingTimer = (viewModelData[0].compare(L"1") == 0); - m_value1cp = (ConversionParameter)_wtoi(viewModelData[1].c_str()); - m_Value1Active = (viewModelData[2].compare(L"1") == 0); - m_Value2Active = (viewModelData[3].compare(L"1") == 0); - m_Value1 = ref new String(viewModelData[4].c_str()); - m_Value2 = ref new String(viewModelData[5].c_str()); - m_valueFromUnlocalized = viewModelData[6]; - m_valueToUnlocalized = viewModelData[7]; - wstringstream modelData(wstringstream::out); - for (unsigned int i = 1; i < tokens.size(); i++) - { - modelData << tokens[i] << L"[###]"; - } - m_model->DeSerialize(modelData.str()); - InitializeView(); - RaisePropertyChanged(nullptr); // Update since all props have been updated. -} - // Saving User Preferences of Category and Associated-Units across Sessions. void UnitConverterViewModel::SaveUserPreferences() { if (UnitsAreValid()) { - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (!m_IsCurrencyCurrentCategory) { auto userPreferences = m_model->SaveUserPreferences(); @@ -700,10 +620,10 @@ void UnitConverterViewModel::RestoreUserPreferences() { if (!IsCurrencyCurrentCategory) { - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; if (localSettings->Values->HasKey(ref new String(L"UnitConverterPreferences"))) { - String^ userPreferences = safe_cast(localSettings->Values->Lookup(ref new String(L"UnitConverterPreferences"))); + String ^ userPreferences = safe_cast(localSettings->Values->Lookup(ref new String(L"UnitConverterPreferences"))); m_model->RestoreUserPreferences(userPreferences->Data()); } } @@ -713,10 +633,12 @@ void UnitConverterViewModel::OnCurrencyDataLoadFinished(bool didLoad) { m_isCurrencyDataLoaded = true; CurrencyDataLoadFailed = !didLoad; - ResetView(); + m_model->ResetCategoriesAndRatios(); + m_model->Calculate(); + ResetCategory(); StringReference key = didLoad ? UnitConverterResourceKeys::CurrencyRatesUpdated : UnitConverterResourceKeys::CurrencyRatesUpdateFailed; - String^ announcement = AppResourceProvider::GetInstance().GetResourceString(key); + String ^ announcement = AppResourceProvider::GetInstance()->GetResourceString(key); Announcement = CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(announcement); } @@ -731,18 +653,20 @@ void UnitConverterViewModel::RefreshCurrencyRatios() m_isCurrencyDataLoaded = false; IsCurrencyLoadingVisible = true; - String^ announcement = AppResourceProvider::GetInstance().GetResourceString(UnitConverterResourceKeys::UpdatingCurrencyRates); + String ^ announcement = AppResourceProvider::GetInstance()->GetResourceString(UnitConverterResourceKeys::UpdatingCurrencyRates); Announcement = CalculatorAnnouncement::GetUpdateCurrencyRatesAnnouncement(announcement); - auto refreshTask = create_task(m_model->RefreshCurrencyRatios()); - refreshTask.then([this](const pair& refreshResult) - { - bool didLoad = refreshResult.first; - wstring timestamp = refreshResult.second; + auto that(this); + auto refreshTask = create_task([that] { return that->m_model->RefreshCurrencyRatios().get(); }); + refreshTask.then( + [that](const pair& refreshResult) { + bool didLoad = refreshResult.first; + wstring timestamp = refreshResult.second; - OnCurrencyTimestampUpdated(timestamp, false /*isWeekOldData*/); - OnCurrencyDataLoadFinished(didLoad); - }, task_continuation_context::use_current()); + that->OnCurrencyTimestampUpdated(timestamp, false /*isWeekOldData*/); + that->OnCurrencyDataLoadFinished(didLoad); + }, + task_continuation_context::use_current()); } void UnitConverterViewModel::OnNetworkBehaviorChanged(_In_ NetworkAccessBehavior newBehavior) @@ -807,12 +731,12 @@ UnitConversionManager::Command UnitConverterViewModel::CommandFromButtonId(Numbe return command; } -void UnitConverterViewModel::SupplementaryResultsTimerTick(ThreadPoolTimer^ timer) +void UnitConverterViewModel::SupplementaryResultsTimerTick(ThreadPoolTimer ^ timer) { timer->Cancel(); } -void UnitConverterViewModel::SupplementaryResultsTimerCancel(ThreadPoolTimer^ timer) +void UnitConverterViewModel::SupplementaryResultsTimerCancel(ThreadPoolTimer ^ timer) { if (!m_resettingTimer) { @@ -825,14 +749,12 @@ void UnitConverterViewModel::RefreshSupplementaryResults() m_cacheMutex.lock(); m_SupplementaryResults->Clear(); - vector whimsicals; + vector whimsicals; for (tuple suggestedValue : m_cachedSuggestedValues) { - SupplementaryResult^ result = - ref new SupplementaryResult( - this->ConvertToLocalizedString(get<0>(suggestedValue), false), - ref new Unit(get<1>(suggestedValue))); + SupplementaryResult ^ result = + ref new SupplementaryResult(this->ConvertToLocalizedString(get<0>(suggestedValue), false), ref new Unit(get<1>(suggestedValue))); if (result->IsWhimsical()) { whimsicals.push_back(result); @@ -849,8 +771,8 @@ void UnitConverterViewModel::RefreshSupplementaryResults() } m_cacheMutex.unlock(); - RaisePropertyChanged(UnitConverterViewModelProperties::SupplementaryResults); - //EventWriteConverterSupplementaryResultsUpdated(); + RaisePropertyChanged(SupplementaryResultsPropertyName); + // EventWriteConverterSupplementaryResultsUpdated(); } // When UpdateDisplay is called, the ViewModel will remember the From/To unlocalized display values @@ -859,16 +781,13 @@ void UnitConverterViewModel::RefreshSupplementaryResults() // values are not both zero. void UnitConverterViewModel::AnnounceConversionResult() { - if ((m_valueFromUnlocalized != m_lastAnnouncedFrom - || m_valueToUnlocalized != m_lastAnnouncedTo) - && Unit1 != nullptr - && Unit2 != nullptr) + if ((m_valueFromUnlocalized != m_lastAnnouncedFrom || m_valueToUnlocalized != m_lastAnnouncedTo) && Unit1 != nullptr && Unit2 != nullptr) { m_lastAnnouncedFrom = m_valueFromUnlocalized; m_lastAnnouncedTo = m_valueToUnlocalized; - Unit^ unitFrom = Value1Active ? Unit1 : Unit2; - Unit^ unitTo = (unitFrom == Unit1) ? Unit2 : Unit1; + Unit ^ unitFrom = Value1Active ? Unit1 : Unit2; + Unit ^ unitTo = (unitFrom == Unit1) ? Unit2 : Unit1; m_lastAnnouncedConversionResult = GetLocalizedConversionResultStringFormat(ValueFrom, unitFrom->Name, ValueTo, unitTo->Name); Announcement = CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(m_lastAnnouncedConversionResult); @@ -879,19 +798,14 @@ void UnitConverterViewModel::UpdateInputBlocked(_In_ const wstring& currencyInpu { // currencyInput is in en-US and has the default decimal separator, so this is safe to do. auto posOfDecimal = currencyInput.find(L'.'); + m_isInputBlocked = false; if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory) { m_isInputBlocked = (posOfDecimal + static_cast(m_currencyMaxFractionDigits) + 1 == currencyInput.length()); } - else - { - m_isInputBlocked = false; - } } -NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId( - const wchar_t ch, - bool& canSendNegate) +NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate) { static_assert(NumbersAndOperatorsEnum::Zero < NumbersAndOperatorsEnum::One, "NumbersAndOperatorsEnum order is invalid"); static_assert(NumbersAndOperatorsEnum::One < NumbersAndOperatorsEnum::Two, "NumbersAndOperatorsEnum order is invalid"); @@ -941,7 +855,8 @@ NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId( { if (LocalizationSettings::GetInstance().IsLocalizedDigit(ch)) { - mappedValue = NumbersAndOperatorsEnum::Zero + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'0')); + mappedValue = NumbersAndOperatorsEnum::Zero + + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'0')); canSendNegate = true; } } @@ -949,25 +864,25 @@ NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId( return mappedValue; } -void UnitConverterViewModel::OnPaste(String^ stringToPaste, ViewMode mode) +void UnitConverterViewModel::OnPaste(String ^ stringToPaste) { // If pastedString is invalid("NoOp") then display pasteError else process the string - if (stringToPaste == StringReference(CopyPasteManager::PasteErrorString)) + if (CopyPasteManager::IsErrorMessage(stringToPaste)) { this->DisplayPasteError(); return; } - TraceLogger::GetInstance().LogValidInputPasted(mode); + TraceLogger::GetInstance()->LogInputPasted(Mode); bool isFirstLegalChar = true; bool sendNegate = false; - wstring accumulation = L""; + wstring accumulation; - for (auto it = stringToPaste->Begin(); it != stringToPaste->End(); it++) + for (const auto ch : stringToPaste) { bool canSendNegate = false; - NumbersAndOperatorsEnum op = MapCharacterToButtonId(*it, canSendNegate); + NumbersAndOperatorsEnum op = MapCharacterToButtonId(ch, canSendNegate); if (NumbersAndOperatorsEnum::None != op) { @@ -988,7 +903,7 @@ void UnitConverterViewModel::OnPaste(String^ stringToPaste, ViewMode mode) } // Negate is only allowed if it's the first legal character, which is handled above. - if (NumbersAndOperatorsEnum::None != op && NumbersAndOperatorsEnum::Negate != op) + if (NumbersAndOperatorsEnum::Negate != op) { UCM::Command cmd = CommandFromButtonId(op); m_model->SendCommand(cmd); @@ -1003,7 +918,7 @@ void UnitConverterViewModel::OnPaste(String^ stringToPaste, ViewMode mode) } } - accumulation += *it; + accumulation += ch; UpdateInputBlocked(accumulation); if (m_isInputBlocked) { @@ -1017,9 +932,9 @@ void UnitConverterViewModel::OnPaste(String^ stringToPaste, ViewMode mode) } } -String^ UnitConverterViewModel::GetLocalizedAutomationName(_In_ String^ displayvalue, _In_ String^ unitname, _In_ String^ format) +String ^ UnitConverterViewModel::GetLocalizedAutomationName(_In_ String ^ displayvalue, _In_ String ^ unitname, _In_ String ^ format) { - String^ valueToLocalize = displayvalue; + String ^ valueToLocalize = displayvalue; if (displayvalue == ValueFrom && Utils::IsLastCharacterTarget(m_valueFromUnlocalized, m_decimalSeparator)) { // Need to compute a second localized value for the automation @@ -1028,14 +943,17 @@ String^ UnitConverterViewModel::GetLocalizedAutomationName(_In_ String^ displayv format = m_localizedValueFromDecimalFormat; } - wstring localizedResult = LocalizationStringUtil::GetLocalizedString(format->Data(), displayvalue->Data(), unitname->Data()); - return ref new String(localizedResult.c_str()); + return LocalizationStringUtil::GetLocalizedString(format, displayvalue, unitname); } -String^ UnitConverterViewModel::GetLocalizedConversionResultStringFormat(_In_ String^ fromValue, _In_ String^ fromUnit, _In_ String^ toValue, _In_ String^ toUnit) +String + ^ UnitConverterViewModel::GetLocalizedConversionResultStringFormat( + _In_ String ^ fromValue, + _In_ String ^ fromUnit, + _In_ String ^ toValue, + _In_ String ^ toUnit) { - String^ localizedString = ref new String(LocalizationStringUtil::GetLocalizedString(m_localizedConversionResultFormat->Data(), fromValue->Data(), fromUnit->Data(), toValue->Data(), toUnit->Data()).c_str()); - return localizedString; + return LocalizationStringUtil::GetLocalizedString(m_localizedConversionResultFormat, fromValue, fromUnit, toValue, toUnit); } void UnitConverterViewModel::UpdateValue1AutomationName() @@ -1056,9 +974,9 @@ void UnitConverterViewModel::UpdateValue2AutomationName() void UnitConverterViewModel::OnMaxDigitsReached() { - String^ format = AppResourceProvider::GetInstance().GetResourceString(UnitConverterResourceKeys::MaxDigitsReachedFormat); - const wstring& announcement = LocalizationStringUtil::GetLocalizedString(format->Data(), m_lastAnnouncedConversionResult->Data()); - Announcement = CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(StringReference(announcement.c_str())); + String ^ format = AppResourceProvider::GetInstance()->GetResourceString(UnitConverterResourceKeys::MaxDigitsReachedFormat); + auto announcement = LocalizationStringUtil::GetLocalizedString(format, m_lastAnnouncedConversionResult); + Announcement = CalculatorAnnouncement::GetMaxDigitsReachedAnnouncement(announcement); } bool UnitConverterViewModel::UnitsAreValid() @@ -1066,29 +984,8 @@ bool UnitConverterViewModel::UnitsAreValid() return UnitFrom != nullptr && !UnitFrom->Abbreviation->IsEmpty() && UnitTo != nullptr && !UnitTo->Abbreviation->IsEmpty(); } -void UnitConverterViewModel::StartConversionResultTimer() +String ^ SupplementaryResult::GetLocalizedAutomationName() { - m_conversionResultTaskHelper = make_unique( - CONVERSION_FINALIZED_DELAY_IN_MS, [this]() - { - if (UnitsAreValid()) - { - String^ valueFrom = m_Value1Active ? m_Value1 : m_Value2; - String^ valueTo = m_Value1Active ? m_Value2 : m_Value1; - TraceLogger::GetInstance().LogConversionResult( - valueFrom->Data(), - UnitFrom->ToString()->Data(), - valueTo->Data(), - UnitTo->ToString()->Data()); - } - }); -} - -String^ SupplementaryResult::GetLocalizedAutomationName() -{ - auto format = AppResourceProvider::GetInstance().GetResourceString("SupplementaryUnit_AutomationName"); - return ref new String(LocalizationStringUtil::GetLocalizedString( - format->Data(), - this->Value->Data(), - this->Unit->Name->Data()).c_str()); + auto format = AppResourceProvider::GetInstance()->GetResourceString("SupplementaryUnit_AutomationName"); + return LocalizationStringUtil::GetLocalizedString(format, this->Value, this->Unit->Name); } diff --git a/src/CalcViewModel/UnitConverterViewModel.h b/src/CalcViewModel/UnitConverterViewModel.h index bd7b8bd5..1f95a47a 100644 --- a/src/CalcViewModel/UnitConverterViewModel.h +++ b/src/CalcViewModel/UnitConverterViewModel.h @@ -7,7 +7,6 @@ #include "Common/Utils.h" #include "Common/NetworkManager.h" #include "Common/Automation/NarratorAnnouncement.h" -#include "Common/ConversionResultTaskHelper.h" #include "Common/CalculatorButtonUser.h" #include "Common/NavCategory.h" @@ -15,99 +14,91 @@ namespace CalculatorApp { namespace ViewModel { - [Windows::UI::Xaml::Data::Bindable] - public ref class Category sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class Category sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - internal: - Category(const UnitConversionManager::Category& category) : - m_original(category) - { } + internal : Category(const UnitConversionManager::Category& category) + : m_original(category) + { + } public: OBSERVABLE_OBJECT(); - property Platform::String^ Name - { - Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); } - } + property Platform::String + ^ Name { Platform::String ^ get() { return ref new Platform::String(m_original.name.c_str()); } } - property Windows::UI::Xaml::Visibility NegateVisibility + property Windows::UI::Xaml::Visibility NegateVisibility { Windows::UI::Xaml::Visibility get() { - return m_original.supportsNegative - ? Windows::UI::Xaml::Visibility::Visible - : Windows::UI::Xaml::Visibility::Collapsed; + return m_original.supportsNegative ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; } } - internal: - const UnitConversionManager::Category& GetModelCategory() const { return m_original; } + internal : const UnitConversionManager::Category& GetModelCategory() const + { + return m_original; + } private: const UnitConversionManager::Category m_original; }; - [Windows::UI::Xaml::Data::Bindable] - public ref class Unit sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class Unit sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - internal: - Unit(const UnitConversionManager::Unit& unit) : - m_original(unit) - { } + internal : Unit(const UnitConversionManager::Unit& unit) + : m_original(unit) + { + } public: OBSERVABLE_OBJECT(); - property Platform::String^ Name - { - Platform::String^ get() { return ref new Platform::String(m_original.name.c_str()); } - } + property Platform::String + ^ Name { Platform::String ^ get() { return ref new Platform::String(m_original.name.c_str()); } } - property Platform::String^ AccessibleName - { - Platform::String^ get() { return ref new Platform::String(m_original.accessibleName.c_str()); } - } + property Platform::String + ^ AccessibleName { Platform::String ^ get() { return ref new Platform::String(m_original.accessibleName.c_str()); } } - property Platform::String^ Abbreviation - { - Platform::String^ get() { return ref new Platform::String(m_original.abbreviation.c_str()); } - } + property Platform::String + ^ Abbreviation { Platform::String ^ get() { return ref new Platform::String(m_original.abbreviation.c_str()); } } - // This method is used to return the desired automation name for default unit in UnitConverter combo box. - Platform::String^ ToString() override + // This method is used to return the desired automation name for default unit in UnitConverter combo box. + Platform::String + ^ ToString() override { return AccessibleName; } - internal: - const UnitConversionManager::Unit& GetModelUnit() const { return m_original; } + internal : const UnitConversionManager::Unit& GetModelUnit() const + { + return m_original; + } private: const UnitConversionManager::Unit m_original; }; - [Windows::UI::Xaml::Data::Bindable] - public ref class SupplementaryResult sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] public ref class SupplementaryResult sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - internal: - SupplementaryResult(Platform::String^ value, Unit^ unit) : - m_Value(value), - m_Unit(unit) - {} + internal : SupplementaryResult(Platform::String ^ value, Unit ^ unit) + : m_Value(value) + , m_Unit(unit) + { + } bool IsWhimsical() const { return m_Unit->GetModelUnit().isWhimsical; } - Platform::String^ GetLocalizedAutomationName(); + Platform::String ^ GetLocalizedAutomationName(); public: OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_R(Platform::String^, Value); - OBSERVABLE_PROPERTY_R(CalculatorApp::ViewModel::Unit^, Unit); + OBSERVABLE_PROPERTY_R(Platform::String ^, Value); + OBSERVABLE_PROPERTY_R(CalculatorApp::ViewModel::Unit ^, Unit); }; interface class IActivatable @@ -115,94 +106,96 @@ namespace CalculatorApp virtual property bool IsActive; }; - template + template ref class Activatable sealed : public IActivatable { private: TActivatable m_activatable; public: - Activatable(TActivatable activatable) : m_activatable(activatable) - { } + Activatable(TActivatable activatable) + : m_activatable(activatable) + { + } virtual property bool IsActive { - bool get() { return m_activatable->IsActive; } - void set(bool value) { m_activatable->IsActive = value; } + bool get() + { + return m_activatable->IsActive; + } + void set(bool value) + { + m_activatable->IsActive = value; + } } }; - template - IActivatable^ AsActivatable(TActivatable activatable) - { - return ref new Activatable(activatable); - } + template + IActivatable + ^ AsActivatable(TActivatable activatable) { return ref new Activatable(activatable); } - namespace UnitConverterViewModelProperties + [Windows::UI::Xaml::Data::Bindable] public ref class UnitConverterViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { - extern Platform::StringReference CurrentCategory; - extern Platform::StringReference Unit1; - extern Platform::StringReference Unit2; - extern Platform::StringReference Value1Active; - extern Platform::StringReference Value2Active; - extern Platform::StringReference SupplementaryVisibility; - extern Platform::StringReference SupplementaryResults; - extern Platform::StringReference CurrencySymbol1; - extern Platform::StringReference CurrencySymbol2; - extern Platform::StringReference CurrencySymbolVisibility; - extern Platform::StringReference CurrencyRatioEquality; - extern Platform::StringReference NetworkBehavior; - extern Platform::StringReference CurrencyDataLoadFailed; - extern Platform::StringReference CurrencyDataIsWeekOld; - extern Platform::StringReference IsCurrencyLoadingVisible; - } - - [Windows::UI::Xaml::Data::Bindable] - public ref class UnitConverterViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged - { - internal: - UnitConverterViewModel(const std::shared_ptr& model); + internal : UnitConverterViewModel(const std::shared_ptr& model); public: OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); - OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector^, Categories); - OBSERVABLE_PROPERTY_RW(Category^, CurrentCategory); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Categories); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, Mode); - OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector^, Units); - OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol1); - OBSERVABLE_PROPERTY_RW(Unit^, Unit1); - OBSERVABLE_PROPERTY_RW(Platform::String^, Value1); - OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencySymbol2); - OBSERVABLE_PROPERTY_RW(Unit^, Unit2); - OBSERVABLE_PROPERTY_RW(Platform::String^, Value2); - OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector^, SupplementaryResults); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Units); + OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencySymbol1); + OBSERVABLE_PROPERTY_RW(Unit ^, Unit1); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Value1); + OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencySymbol2); + OBSERVABLE_PROPERTY_RW(Unit ^, Unit2); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Value2); + OBSERVABLE_NAMED_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, SupplementaryResults); OBSERVABLE_PROPERTY_RW(bool, Value1Active); OBSERVABLE_PROPERTY_RW(bool, Value2Active); - OBSERVABLE_PROPERTY_RW(Platform::String^, Value1AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, Value2AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, Unit1AutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, Unit2AutomationName); - OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement^, Announcement); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Value1AutomationName); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Value2AutomationName); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Unit1AutomationName); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Unit2AutomationName); + OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement ^, Announcement); OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled); OBSERVABLE_PROPERTY_RW(bool, IsDropDownOpen); OBSERVABLE_PROPERTY_RW(bool, IsDropDownEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsCurrencyLoadingVisible); - OBSERVABLE_PROPERTY_RW(bool, IsCurrencyCurrentCategory); - OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEquality); - OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyRatioEqualityAutomationName); - OBSERVABLE_PROPERTY_RW(Platform::String^, CurrencyTimestamp); - OBSERVABLE_PROPERTY_RW(CalculatorApp::NetworkAccessBehavior, NetworkBehavior); - OBSERVABLE_PROPERTY_RW(bool, CurrencyDataLoadFailed); - OBSERVABLE_PROPERTY_RW(bool, CurrencyDataIsWeekOld); + OBSERVABLE_NAMED_PROPERTY_RW(bool, IsCurrencyLoadingVisible); + OBSERVABLE_NAMED_PROPERTY_R(bool, IsCurrencyCurrentCategory); + OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyRatioEquality); + OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyRatioEqualityAutomationName); + OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyTimestamp); + OBSERVABLE_NAMED_PROPERTY_RW(CalculatorApp::NetworkAccessBehavior, NetworkBehavior); + OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataLoadFailed); + OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataIsWeekOld); + + public: + property Category ^ CurrentCategory + { + Category ^ get() { return m_CurrentCategory; } + void set(Category ^ value) + { + if (m_CurrentCategory == value) + { + return; + } + m_CurrentCategory = value; + if (value != nullptr) + { + auto currentCategory = value->GetModelCategory(); + IsCurrencyCurrentCategory = currentCategory.id == CalculatorApp::Common::NavCategory::Serialize(CalculatorApp::Common::ViewMode::Currency); + } + OnPropertyChanged("CurrentCategory"); + } + } property Windows::UI::Xaml::Visibility SupplementaryVisibility { Windows::UI::Xaml::Visibility get() { - return SupplementaryResults->Size > 0 - ? Windows::UI::Xaml::Visibility::Visible - : Windows::UI::Xaml::Visibility::Collapsed; + return SupplementaryResults->Size > 0 ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; } } @@ -210,9 +203,8 @@ namespace CalculatorApp { Windows::UI::Xaml::Visibility get() { - return (CurrencySymbol1->IsEmpty() || CurrencySymbol2->IsEmpty()) - ? Windows::UI::Xaml::Visibility::Collapsed - : Windows::UI::Xaml::Visibility::Visible; + return (CurrencySymbol1->IsEmpty() || CurrencySymbol2->IsEmpty()) ? Windows::UI::Xaml::Visibility::Collapsed + : Windows::UI::Xaml::Visibility::Visible; } } @@ -225,23 +217,26 @@ namespace CalculatorApp void AnnounceConversionResult(); - internal: - void ResetView(); + internal : void ResetView(); void PopulateData(); NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate); void DisplayPasteError(); - void OnValueActivated(IActivatable^ control); - void OnPaste(Platform::String^ stringToPaste, CalculatorApp::Common::ViewMode mode); + void OnValueActivated(IActivatable ^ control); + void OnPaste(Platform::String ^ stringToPaste); - void OnCopyCommand(Platform::Object^ parameter); - void OnPasteCommand(Platform::Object^ parameter); + void OnCopyCommand(Platform::Object ^ parameter); + void OnPasteCommand(Platform::Object ^ parameter); - Platform::String^ GetLocalizedAutomationName(_In_ Platform::String^ displayvalue, _In_ Platform::String^ unitname, _In_ Platform::String^ format); - Platform::String^ GetLocalizedConversionResultStringFormat(_In_ Platform::String^ fromValue, _In_ Platform::String^ fromUnit, _In_ Platform::String^ toValue, _In_ Platform::String^ toUnit); + Platform::String + ^ GetLocalizedAutomationName(_In_ Platform::String ^ displayvalue, _In_ Platform::String ^ unitname, _In_ Platform::String ^ format); + Platform::String + ^ GetLocalizedConversionResultStringFormat( + _In_ Platform::String ^ fromValue, + _In_ Platform::String ^ fromUnit, + _In_ Platform::String ^ toValue, + _In_ Platform::String ^ toUnit); void UpdateValue1AutomationName(); void UpdateValue2AutomationName(); - Platform::String^ Serialize(); - void Deserialize(Platform::String^ state); // Saving And Restoring User Preferences of Category and Associated-Units across Sessions. void SaveUserPreferences(); @@ -267,26 +262,25 @@ namespace CalculatorApp void OnMaxDigitsReached(); void BuildUnitList(const std::vector& modelUnitList); - Unit^ FindUnitInList(UnitConversionManager::Unit target); + Unit ^ FindUnitInList(UnitConversionManager::Unit target); void SetSelectedUnits(); private: void InitializeView(); - void OnPropertyChanged(Platform::String^ prop); - void OnCategoryChanged(Platform::Object^ unused); - void OnUnitChanged(Platform::Object^ unused); - void OnSwitchActive(Platform::Object^ unused); + void OnPropertyChanged(Platform::String ^ prop); + void OnCategoryChanged(Platform::Object ^ unused); + void OnUnitChanged(Platform::Object ^ unused); + void OnSwitchActive(Platform::Object ^ unused); UnitConversionManager::Command CommandFromButtonId(CalculatorApp::NumbersAndOperatorsEnum button); - void SupplementaryResultsTimerTick(Windows::System::Threading::ThreadPoolTimer^ timer); - void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer^ timer); + void SupplementaryResultsTimerTick(Windows::System::Threading::ThreadPoolTimer ^ timer); + void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer ^ timer); void RefreshSupplementaryResults(); void UpdateInputBlocked(_In_ const std::wstring& currencyInput); bool UnitsAreValid(); + void ResetCategory(); - void OnButtonPressed(Platform::Object^ parameter); - Platform::String^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings); - - void StartConversionResultTimer(); + void OnButtonPressed(Platform::Object ^ parameter); + Platform::String ^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings); std::shared_ptr m_model; wchar_t m_decimalSeparator; @@ -323,50 +317,47 @@ namespace CalculatorApp private: bool m_isInputBlocked; - Windows::System::Threading::ThreadPoolTimer^ m_supplementaryResultsTimer; + Windows::System::Threading::ThreadPoolTimer ^ m_supplementaryResultsTimer; bool m_resettingTimer; std::vector> m_cachedSuggestedValues; std::mutex m_cacheMutex; - Windows::Globalization::NumberFormatting::DecimalFormatter^ m_decimalFormatter; - Windows::Globalization::NumberFormatting::CurrencyFormatter^ m_currencyFormatter; + Windows::Globalization::NumberFormatting::DecimalFormatter ^ m_decimalFormatter; + Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter; int m_currencyMaxFractionDigits; std::wstring m_valueFromUnlocalized; std::wstring m_valueToUnlocalized; bool m_relocalizeStringOnSwitch; - // For Saving the User Preferences only if the Unit converter ViewModel is initialised for the first time - bool m_IsFirstTime; - Platform::String^ m_localizedValueFromFormat; - Platform::String^ m_localizedValueFromDecimalFormat; - Platform::String^ m_localizedValueToFormat; - Platform::String^ m_localizedConversionResultFormat; - Platform::String^ m_localizedInputUnitName; - Platform::String^ m_localizedOutputUnitName; + Platform::String ^ m_localizedValueFromFormat; + Platform::String ^ m_localizedValueFromDecimalFormat; + Platform::String ^ m_localizedValueToFormat; + Platform::String ^ m_localizedConversionResultFormat; + Platform::String ^ m_localizedInputUnitName; + Platform::String ^ m_localizedOutputUnitName; bool m_isValue1Updating; bool m_isValue2Updating; std::wstring m_lastAnnouncedFrom; std::wstring m_lastAnnouncedTo; - Platform::String^ m_lastAnnouncedConversionResult; - + Platform::String ^ m_lastAnnouncedConversionResult; + Category ^ m_CurrentCategory; bool m_isCurrencyDataLoaded; - - std::unique_ptr m_conversionResultTaskHelper; }; class UnitConverterVMCallback : public UnitConversionManager::IUnitConverterVMCallback { public: - UnitConverterVMCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel) - {} + UnitConverterVMCallback(UnitConverterViewModel ^ viewModel) + : m_viewModel(viewModel) + { + } void DisplayCallback(const std::wstring& from, const std::wstring& to) override { m_viewModel->UpdateDisplay(from, to); } - void SuggestedValueCallback( - const std::vector>& suggestedValues) override + void SuggestedValueCallback(const std::vector>& suggestedValues) override { m_viewModel->UpdateSupplementaryResults(suggestedValues); } @@ -377,14 +368,16 @@ namespace CalculatorApp } private: - UnitConverterViewModel^ m_viewModel; + UnitConverterViewModel ^ m_viewModel; }; class ViewModelCurrencyCallback : public UnitConversionManager::IViewModelCurrencyCallback { public: - ViewModelCurrencyCallback(UnitConverterViewModel^ viewModel) : m_viewModel(viewModel) - {} + ViewModelCurrencyCallback(UnitConverterViewModel ^ viewModel) + : m_viewModel(viewModel) + { + } void CurrencyDataLoadFinished(bool didLoad) override { @@ -393,8 +386,8 @@ namespace CalculatorApp void CurrencySymbolsCallback(const std::wstring& symbol1, const std::wstring& symbol2) override { - Platform::String^ sym1 = Platform::StringReference(symbol1.c_str()); - Platform::String^ sym2 = Platform::StringReference(symbol2.c_str()); + Platform::String ^ sym1 = Platform::StringReference(symbol1.c_str()); + Platform::String ^ sym2 = Platform::StringReference(symbol2.c_str()); bool value1Active = m_viewModel->Value1Active; m_viewModel->CurrencySymbol1 = value1Active ? sym1 : sym2; @@ -418,7 +411,7 @@ namespace CalculatorApp } private: - UnitConverterViewModel^ m_viewModel; + UnitConverterViewModel ^ m_viewModel; }; } } diff --git a/src/CalcViewModel/ViewState.cpp b/src/CalcViewModel/ViewState.cpp index cadfc299..63a5d30e 100644 --- a/src/CalcViewModel/ViewState.cpp +++ b/src/CalcViewModel/ViewState.cpp @@ -11,7 +11,7 @@ namespace CalculatorApp Platform::StringReference Snap(L"Snap"); Platform::StringReference DockedView(L"DockedView"); - bool IsValidViewState(Platform::String^ viewState) + bool IsValidViewState(Platform::String ^ viewState) { return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView); } diff --git a/src/CalcViewModel/ViewState.h b/src/CalcViewModel/ViewState.h index 8b70cff0..0a103d09 100644 --- a/src/CalcViewModel/ViewState.h +++ b/src/CalcViewModel/ViewState.h @@ -10,6 +10,6 @@ namespace CalculatorApp extern Platform::StringReference Snap; extern Platform::StringReference DockedView; - bool IsValidViewState(Platform::String^ viewState); + bool IsValidViewState(Platform::String ^ viewState); } } diff --git a/src/CalcViewModel/packages.config b/src/CalcViewModel/packages.config deleted file mode 100644 index cef4e13e..00000000 --- a/src/CalcViewModel/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/CalcViewModel/pch.cpp b/src/CalcViewModel/pch.cpp index 7c5e1feb..1da170eb 100644 --- a/src/CalcViewModel/pch.cpp +++ b/src/CalcViewModel/pch.cpp @@ -2,4 +2,3 @@ // Licensed under the MIT License. #include "pch.h" - diff --git a/src/CalcViewModel/pch.h b/src/CalcViewModel/pch.h index 1d03c5e5..40a440aa 100644 --- a/src/CalcViewModel/pch.h +++ b/src/CalcViewModel/pch.h @@ -3,13 +3,16 @@ #pragma once - #include "targetver.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +// Windows headers define min/max macros. +// Disable it for project code. +#define NOMINMAX + #include #include @@ -27,6 +30,9 @@ #include #include #include +#include +#include + // C++\WinRT Headers #include "winrt/base.h" #include "winrt/Windows.Foundation.Diagnostics.h" @@ -34,6 +40,7 @@ #include "winrt/Windows.Globalization.DateTimeFormatting.h" #include "winrt/Windows.System.UserProfile.h" #include "winrt/Windows.UI.Xaml.h" +#include "winrt/Windows.Foundation.Metadata.h" // The following namespaces exist as a convenience to resolve // ambiguity for Windows types in the Windows::UI::Xaml::Automation::Peers @@ -41,5 +48,7 @@ // Once the app switches to min version RS3, the namespaces can be removed. // TODO - MSFT 12735088 namespace StandardPeers = Windows::UI::Xaml::Automation::Peers; -namespace CalculatorApp::Common::Automation {} +namespace CalculatorApp::Common::Automation +{ +} namespace CustomPeers = CalculatorApp::Common::Automation; diff --git a/src/CalcViewModel/targetver.h b/src/CalcViewModel/targetver.h index 18b255ab..221efabb 100644 --- a/src/CalcViewModel/targetver.h +++ b/src/CalcViewModel/targetver.h @@ -9,4 +9,3 @@ // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include - diff --git a/src/Calculator.sln b/src/Calculator.sln index 2d36a93f..09fc81fd 100644 --- a/src/Calculator.sln +++ b/src/Calculator.sln @@ -1,21 +1,32 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29009.5 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3A5DF651-B8A1-45CA-9135-964A6FC7F5D1}" + ProjectSection(SolutionItems) = preProject + ..\.clang-format = ..\.clang-format + nuget.config = nuget.config + EndProjectSection +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Calculator", "Calculator\Calculator.vcxproj", "{9447424A-0E05-4911-BEB8-E0354405F39A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcManager", "CalcManager\CalcManager.vcxproj", "{311E866D-8B93-4609-A691-265941FEE101}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3A5DF651-B8A1-45CA-9135-964A6FC7F5D1}" - ProjectSection(SolutionItems) = preProject - nuget.config = nuget.config - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewModel\CalcViewModel.vcxproj", "{90E9761D-9262-4773-942D-CAEAE75D7140}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITests", "CalculatorUITests\CalculatorUITests.csproj", "{B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITestFramework", "CalculatorUITestFramework\CalculatorUITestFramework.csproj", "{96454213-94AF-457D-9DF9-B14F80E7770F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphingImpl", "GraphingImpl\GraphingImpl.vcxproj", "{52E03A58-B378-4F50-8BFB-F659FB85E790}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphControl", "GraphControl\GraphControl.vcxproj", "{E727A92B-F149-492C-8117-C039A298719B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TraceLogging", "TraceLogging\TraceLogging.vcxproj", "{FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -28,22 +39,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 - {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.ActiveCfg = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Build.0 = Debug|ARM {9447424A-0E05-4911-BEB8-E0354405F39A}.Debug|ARM.Deploy.0 = Debug|ARM @@ -68,6 +63,22 @@ Global {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.ActiveCfg = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Build.0 = Release|Win32 {9447424A-0E05-4911-BEB8-E0354405F39A}.Release|x86.Deploy.0 = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.ActiveCfg = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM.Build.0 = Debug|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|ARM64.Build.0 = Debug|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.ActiveCfg = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x64.Build.0 = Debug|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.ActiveCfg = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Debug|x86.Build.0 = Debug|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.ActiveCfg = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM.Build.0 = Release|ARM + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.ActiveCfg = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|ARM64.Build.0 = Release|ARM64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.ActiveCfg = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x64.Build.0 = Release|x64 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.ActiveCfg = Release|Win32 + {311E866D-8B93-4609-A691-265941FEE101}.Release|x86.Build.0 = Release|Win32 {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.ActiveCfg = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM.Build.0 = Debug|ARM {90E9761D-9262-4773-942D-CAEAE75D7140}.Debug|ARM64.ActiveCfg = Debug|ARM64 @@ -100,10 +111,93 @@ Global {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.ActiveCfg = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Build.0 = Release|Win32 {D3BAED2C-4B07-4E1D-8807-9D6499450349}.Release|x86.Deploy.0 = Release|Win32 + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|ARM64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x64.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Debug|x86.Build.0 = Debug|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|ARM64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x64.Build.0 = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.ActiveCfg = Release|Any CPU + {B2C5ADFF-D6B5-48C1-BB8C-571BFD583D7F}.Release|x86.Build.0 = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|ARM.ActiveCfg = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|ARM.Build.0 = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|ARM64.Build.0 = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|x64.ActiveCfg = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|x64.Build.0 = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|x86.ActiveCfg = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Debug|x86.Build.0 = Debug|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|ARM.ActiveCfg = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|ARM.Build.0 = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|ARM64.ActiveCfg = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|ARM64.Build.0 = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x64.ActiveCfg = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x64.Build.0 = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.ActiveCfg = Release|Any CPU + {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.Build.0 = Release|Any CPU + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.ActiveCfg = Debug|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.Build.0 = Debug|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.Build.0 = Debug|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.ActiveCfg = Debug|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.Build.0 = Debug|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.ActiveCfg = Debug|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.Build.0 = Debug|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.ActiveCfg = Release|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.Build.0 = Release|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.ActiveCfg = Release|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.Build.0 = Release|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.ActiveCfg = Release|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.Build.0 = Release|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.ActiveCfg = Release|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.Build.0 = Release|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.ActiveCfg = Debug|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.Build.0 = Debug|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.Build.0 = Debug|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.ActiveCfg = Debug|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.Build.0 = Debug|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.ActiveCfg = Debug|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.Build.0 = Debug|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.ActiveCfg = Release|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.Build.0 = Release|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.ActiveCfg = Release|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.Build.0 = Release|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x64.ActiveCfg = Release|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x64.Build.0 = Release|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x86.ActiveCfg = Release|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x86.Build.0 = Release|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM.ActiveCfg = Debug|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM.Build.0 = Debug|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM64.Build.0 = Debug|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x64.ActiveCfg = Debug|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x64.Build.0 = Debug|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x86.ActiveCfg = Debug|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x86.Build.0 = Debug|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM.ActiveCfg = Release|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM.Build.0 = Release|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM64.ActiveCfg = Release|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM64.Build.0 = Release|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x64.ActiveCfg = Release|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x64.Build.0 = Release|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x86.ActiveCfg = Release|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {311E866D-8B93-4609-A691-265941FEE101} = {3A5DF651-B8A1-45CA-9135-964A6FC7F5D1} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0EA53F83-5FA6-46A1-A290-A3C6962D2CAC} EndGlobalSection diff --git a/src/Calculator/AboutFlyout.xaml b/src/Calculator/AboutFlyout.xaml index cfce6fbc..50f6f4e1 100644 --- a/src/Calculator/AboutFlyout.xaml +++ b/src/Calculator/AboutFlyout.xaml @@ -4,7 +4,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:CalculatorApp.Common" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - Loaded="UserControl_Loaded" mc:Ignorable="d"> @@ -38,7 +37,7 @@ - + SetVersionString(); - Header->Text = resourceLoader.GetResourceString("AboutButton/Content"); + Header->Text = resourceLoader->GetResourceString("AboutButton/Content"); + + auto copyrightText = + LocalizationStringUtil::GetLocalizedString(resourceLoader->GetResourceString("AboutControlCopyright"), StringReference(to_wstring(BUILD_YEAR).c_str())); + AboutControlCopyrightRun->Text = copyrightText; } -void AboutFlyout::FeedbackButton_Click(_In_ Object^ sender, _In_ RoutedEventArgs^ e) +void AboutFlyout::FeedbackButton_Click(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { PackageVersion version = Package::Current->Id->Version; - String^ versionNumber = ref new String(L"Version "); + String ^ versionNumber = ref new String(L"Version "); versionNumber = versionNumber + version.Major + "." + version.Minor + "." + version.Build + "." + version.Revision; Launcher::LaunchUriAsync(ref new Uri("windows-feedback:?contextid=130&metadata=%7B%22Metadata%22:[%7B%22AppBuild%22:%22" + versionNumber + "%22%7D]%7D")); } @@ -44,7 +53,7 @@ void AboutFlyout::FeedbackButton_Click(_In_ Object^ sender, _In_ RoutedEventArgs void AboutFlyout::SetVersionString() { PackageVersion version = Package::Current->Id->Version; - String^ appName = AppResourceProvider::GetInstance().GetResourceString(L"AppName"); + String ^ appName = AppResourceProvider::GetInstance()->GetResourceString(L"AppName"); AboutFlyoutVersion->Text = appName + L" " + version.Major + L"." + version.Minor + L"." + version.Build + L"." + version.Revision; } @@ -52,8 +61,3 @@ void AboutFlyout::SetDefaultFocus() { AboutFlyoutEULA->Focus(::FocusState::Programmatic); } - -void CalculatorApp::AboutFlyout::UserControl_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - TraceLogger::GetInstance().LogAboutFlyoutOpened(); -} diff --git a/src/Calculator/AboutFlyout.xaml.h b/src/Calculator/AboutFlyout.xaml.h index f7688681..ab0e481e 100644 --- a/src/Calculator/AboutFlyout.xaml.h +++ b/src/Calculator/AboutFlyout.xaml.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #pragma once @@ -7,7 +7,8 @@ namespace CalculatorApp { - public ref class AboutFlyout sealed +public + ref class AboutFlyout sealed { public: AboutFlyout(); @@ -15,8 +16,7 @@ namespace CalculatorApp void SetDefaultFocus(); private: - void FeedbackButton_Click(_In_ Platform::Object^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs^ e); + void FeedbackButton_Click(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e); void SetVersionString(); - void UserControl_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); }; } /* namespace CalculatorApp */ diff --git a/src/Calculator/App.xaml b/src/Calculator/App.xaml index 60103662..4a0cca57 100644 --- a/src/Calculator/App.xaml +++ b/src/Calculator/App.xaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Controls="using:CalculatorApp.Controls" xmlns:common="using:CalculatorApp.Common" - xmlns:converters="using:CalculatorApp.Converters" xmlns:local="using:CalculatorApp"> @@ -17,24 +16,48 @@ 0 #FF000000 #FF2B2B2B + #FF858585 - + - - + + + + + + + + + - + @@ -43,30 +66,73 @@ FallbackColor="{ThemeResource SystemChromeMediumColor}" TintColor="{ThemeResource SystemChromeLowColor}" TintOpacity="0.7"/> + Dark + + + + + + + + + + + + + + + + + + 0,0,0,0 0 #FFF2F2F2 #FFE0E0E0 + #FF858585 - + - - + + + + + + + + + - + @@ -75,27 +141,64 @@ FallbackColor="{ThemeResource SystemChromeMediumColor}" TintColor="{ThemeResource SystemChromeLowColor}" TintOpacity="0.7"/> + + Light + + + + + + + + + + + + + + + + + 0,1,0,0 2 - + - + + + - + + + + + + + + Dark + + + + + @@ -110,6 +213,21 @@ 15 SemiBold + 64 + 24 + 24 + 16 + + 64 + 16 + 20 + 10 + + 38 + 12 + 16 + 8 + 12 15 24 @@ -119,15 +237,21 @@ 40 34 + 38 + 48 24 20 + 22 + 15 + 12 - 15 + 14 + 16 - 72 - 46 - 28 + + + + + + + + + + - + + + + @@ -351,7 +525,7 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + App::App() { - TraceLogger::GetInstance().LogAppLaunchStart(); InitializeComponent(); m_preLaunched = false; @@ -71,57 +69,28 @@ App::App() // Currently this is bugged so the property is only respected from code-behind. this->HighContrastAdjustment = ApplicationHighContrastAdjustment::None; + this->Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending); + #if _DEBUG this->DebugSettings->IsBindingTracingEnabled = true; - this->DebugSettings->BindingFailed += ref new BindingFailedEventHandler([](_In_ Object^ /*sender*/, _In_ BindingFailedEventArgs^ e) - { + this->DebugSettings->BindingFailed += ref new BindingFailedEventHandler([](_In_ Object ^ /*sender*/, _In_ BindingFailedEventArgs ^ e) { if (IsDebuggerPresent()) { - ::Platform::String^ errorMessage = e->Message; + ::Platform::String ^ errorMessage = e->Message; __debugbreak(); } }); #endif } -bool App::m_isAnimationEnabled = true; - -/// -/// Return True if animation is enabled by user setting. -/// -bool App::IsAnimationEnabled() -{ - return App::m_isAnimationEnabled; -} - -/// -/// Return the current application view state. The value -/// will match one of the constants in the ViewState namespace. -/// -String^ App::GetAppViewState() -{ - String^ newViewState; - CoreWindow^ window = CoreWindow::GetForCurrentThread(); - if ((window->Bounds.Width >= 560) && (window->Bounds.Height >= 356)) - { - newViewState = ViewState::DockedView; - } - else - { - newViewState = ViewState::Snap; - } - - return newViewState; -} - -void App::AddWindowToMap(_In_ WindowFrameService^ frameService) +void App::AddWindowToMap(_In_ WindowFrameService ^ frameService) { reader_writer_lock::scoped_lock lock(m_windowsMapLock); m_secondaryWindows[frameService->GetViewId()] = frameService; - TraceLogger::GetInstance().UpdateWindowCount(m_secondaryWindows.size()); + TraceLogger::GetInstance()->UpdateWindowCount(m_secondaryWindows.size()); } -WindowFrameService^ App::GetWindowFromMap(int viewId) +WindowFrameService ^ App::GetWindowFromMap(int viewId) { reader_writer_lock::scoped_lock_read lock(m_windowsMapLock); auto windowMapEntry = m_secondaryWindows.find(viewId); @@ -140,32 +109,33 @@ void App::RemoveWindowFromMap(int viewId) m_secondaryWindows.erase(viewId); } -void App::RemoveWindow(_In_ WindowFrameService^ frameService) +void App::RemoveWindow(_In_ WindowFrameService ^ frameService) { - // Shell does not allow killing the main window. + // Shell does not allow killing the main window. if (m_mainViewId != frameService->GetViewId()) { HandleViewReleaseAndRemoveWindowFromMap(frameService); } } -task App::HandleViewReleaseAndRemoveWindowFromMap(_In_ WindowFrameService^ frameService) +task App::HandleViewReleaseAndRemoveWindowFromMap(_In_ WindowFrameService ^ frameService) { WeakReference weak(this); // Unregister the event handler of the Main Page - auto frame = safe_cast(Window::Current->Content); - auto mainPage = safe_cast(frame->Content); + auto frame = safe_cast(Window::Current->Content); + auto mainPage = safe_cast(frame->Content); mainPage->UnregisterEventHandlers(); - return frameService->HandleViewRelease() - .then([weak, frameService]() - { - auto that = weak.Resolve(); - that->RemoveWindowFromMap(frameService->GetViewId()); - }, task_continuation_context::use_arbitrary()); + return frameService->HandleViewRelease().then( + [weak, frameService]() { + auto that = weak.Resolve(); + that->RemoveWindowFromMap(frameService->GetViewId()); + }, + task_continuation_context::use_arbitrary()); } +#pragma optimize("", off) // Turn off optimizations to work around coroutine optimization bug task App::SetupJumpList() { try @@ -176,12 +146,15 @@ task App::SetupJumpList() jumpList->SystemGroupKind = JumpListSystemGroupKind::None; jumpList->Items->Clear(); - for (NavCategory^ option : calculatorOptions->Categories) + for (NavCategory ^ option : calculatorOptions->Categories) { + if (!option->IsEnabled) + { + continue; + } ViewMode mode = option->Mode; - auto item = JumpListItem::CreateWithArguments(((int)mode).ToString(), L"ms-resource:///Resources/"+ NavCategory::GetNameResourceKey(mode)); + auto item = JumpListItem::CreateWithArguments(((int)mode).ToString(), L"ms-resource:///Resources/" + NavCategory::GetNameResourceKey(mode)); item->Description = L"ms-resource:///Resources/" + NavCategory::GetNameResourceKey(mode); - item->GroupName = L"ms-resource:///Resources/" + NavCategoryGroup::GetHeaderResourceKey(calculatorOptions->GroupType); item->Logo = ref new Uri("ms-appx:///Assets/" + mode.ToString() + ".png"); jumpList->Items->Append(item); @@ -189,10 +162,13 @@ task App::SetupJumpList() co_await jumpList->SaveAsync(); } - catch(...) {} -} + catch (...) + { + } +}; +#pragma optimize("", on) -void App::RemoveSecondaryWindow(_In_ WindowFrameService^ frameService) +void App::RemoveSecondaryWindow(_In_ WindowFrameService ^ frameService) { // Shell does not allow killing the main window. if (m_mainViewId != frameService->GetViewId()) @@ -201,7 +177,7 @@ void App::RemoveSecondaryWindow(_In_ WindowFrameService^ frameService) } } -Frame^ App::CreateFrame() +Frame ^ App::CreateFrame() { auto frame = ref new Frame(); frame->FlowDirection = LocalizationService::GetInstance()->GetFlowDirection(); @@ -215,44 +191,38 @@ Frame^ App::CreateFrame() /// search results, and so forth. /// /// Details about the launch request and process. -void App::OnLaunched(LaunchActivatedEventArgs^ args) +void App::OnLaunched(LaunchActivatedEventArgs ^ args) { - TraceLogger::GetInstance().LogWindowLaunched(); if (args->PrelaunchActivated) { // If the app got pre-launch activated, then save that state in a flag m_preLaunched = true; - TraceLogger::GetInstance().LogAppPrelaunchedBySystem(); } OnAppLaunch(args, args->Arguments); } -void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) +void App::OnAppLaunch(IActivatedEventArgs ^ args, String ^ argument) { - auto previousExecutionState = args->PreviousExecutionState; - TraceLogger::GetInstance().LogOnAppLaunch(previousExecutionState.ToString()->Data()); + // Uncomment the following lines to display frame-rate and per-frame CPU usage info. + //#if _DEBUG + // if (IsDebuggerPresent()) + // { + // DebugSettings->EnableFrameRateCounter = true; + // } + //#endif -#if _DEBUG - if (IsDebuggerPresent()) - { - DebugSettings->EnableFrameRateCounter = true; - } -#endif - auto userSettings = ref new Windows::UI::ViewManagement::UISettings(); - m_isAnimationEnabled = userSettings->AnimationsEnabled; + args->SplashScreen->Dismissed += ref new TypedEventHandler(this, &App::DismissedEventHandler); - args->SplashScreen->Dismissed += ref new TypedEventHandler(this, &App::DismissedEventHandler); - - auto rootFrame = dynamic_cast(Window::Current->Content); + auto rootFrame = dynamic_cast(Window::Current->Content); WeakReference weak(this); float minWindowWidth = static_cast(static_cast(this->Resources->Lookup(ApplicationResourceKeys::AppMinWindowWidth))); float minWindowHeight = static_cast(static_cast(this->Resources->Lookup(ApplicationResourceKeys::AppMinWindowHeight))); Size minWindowSize = SizeHelper::FromDimensions(minWindowWidth, minWindowHeight); - ApplicationView^ appView = ApplicationView::GetForCurrentView(); - ApplicationDataContainer^ localSettings = ApplicationData::Current->LocalSettings; + ApplicationView ^ appView = ApplicationView::GetForCurrentView(); + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; // For very first launch, set the size of the calc as size of the default standard mode if (!localSettings->Values->HasKey(L"VeryFirstLaunch")) { @@ -269,17 +239,17 @@ void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) // just ensure that the window is active if (rootFrame == nullptr) { - if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) // PC Family + if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) // PC Family { - // Disable the system view activation policy during the first launch of the app + // Disable the system view activation policy during the first launch of the app // only for PC family devices and not for phone family devices try { ApplicationViewSwitcher::DisableSystemViewActivationPolicy(); } - catch (Exception^ e) + catch (Exception ^ e) { - // Log that DisableSystemViewActionPolicy didn't work + // Log that DisableSystemViewActionPolicy didn't work } } @@ -303,79 +273,75 @@ void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) else { // For first launch, LaunchStart is logged in constructor, this is for subsequent launches. - TraceLogger::GetInstance().LogAppLaunchStart(); // !Phone check is required because even in continuum mode user interaction mode is Mouse not Touch - if ((UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Mouse) && (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))) + if ((UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Mouse) + && (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))) { // If the pre-launch hasn't happened then allow for the new window/view creation if (!m_preLaunched) { auto newCoreAppView = CoreApplication::CreateNewView(); - newCoreAppView->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([args, argument, minWindowSize, weak]() - { - TraceLogger::GetInstance().LogNewWindowCreationBegin(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); - auto that = weak.Resolve(); - if (that != nullptr) - { - auto rootFrame = App::CreateFrame(); - SetMinWindowSizeAndActivate(rootFrame, minWindowSize); - - if (!rootFrame->Navigate(MainPage::typeid, argument)) + newCoreAppView->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, ref new DispatchedHandler([args, argument, minWindowSize, weak]() { + auto that = weak.Resolve(); + if (that != nullptr) { - // We couldn't navigate to the main page, kill the app so we have a good - // stack to debug - throw std::bad_exception(); - } + auto rootFrame = App::CreateFrame(); + SetMinWindowSizeAndActivate(rootFrame, minWindowSize); - auto frameService = WindowFrameService::CreateNewWindowFrameService(rootFrame, true, weak); - that->AddWindowToMap(frameService); - - auto dispatcher = CoreWindow::GetForCurrentThread()->Dispatcher; - auto safeFrameServiceCreation = std::make_shared(frameService, that); - int newWindowId = ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()); - - ActivationViewSwitcher^ activationViewSwitcher; - auto activateEventArgs = dynamic_cast(args); - if (activateEventArgs != nullptr) - { - activationViewSwitcher = activateEventArgs->ViewSwitcher; - } - - if (activationViewSwitcher != nullptr) - { - activationViewSwitcher->ShowAsStandaloneAsync(newWindowId, ViewSizePreference::Default); - safeFrameServiceCreation->SetOperationSuccess(true); - } - else - { - auto activatedEventArgs = dynamic_cast(args); - if ((activatedEventArgs != nullptr) && (activatedEventArgs->CurrentlyShownApplicationViewId != 0)) + if (!rootFrame->Navigate(MainPage::typeid, argument)) { - create_task(ApplicationViewSwitcher::TryShowAsStandaloneAsync( - frameService->GetViewId(), - ViewSizePreference::Default, - activatedEventArgs->CurrentlyShownApplicationViewId, - ViewSizePreference::Default - )) - .then([safeFrameServiceCreation](bool viewShown) + // We couldn't navigate to the main page, kill the app so we have a good + // stack to debug + throw std::bad_exception(); + } + + auto frameService = WindowFrameService::CreateNewWindowFrameService(rootFrame, true, weak); + that->AddWindowToMap(frameService); + + auto dispatcher = CoreWindow::GetForCurrentThread()->Dispatcher; + auto safeFrameServiceCreation = std::make_shared(frameService, that); + int newWindowId = ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()); + + ActivationViewSwitcher ^ activationViewSwitcher; + auto activateEventArgs = dynamic_cast(args); + if (activateEventArgs != nullptr) + { + activationViewSwitcher = activateEventArgs->ViewSwitcher; + } + + if (activationViewSwitcher != nullptr) + { + activationViewSwitcher->ShowAsStandaloneAsync(newWindowId, ViewSizePreference::Default); + safeFrameServiceCreation->SetOperationSuccess(true); + } + else + { + auto activatedEventArgs = dynamic_cast(args); + if ((activatedEventArgs != nullptr) && (activatedEventArgs->CurrentlyShownApplicationViewId != 0)) { - // SafeFrameServiceCreation is used to automatically remove the frame - // from the list of frames if something goes bad. - safeFrameServiceCreation->SetOperationSuccess(viewShown); - }, task_continuation_context::use_current()); + create_task(ApplicationViewSwitcher::TryShowAsStandaloneAsync( + frameService->GetViewId(), + ViewSizePreference::Default, + activatedEventArgs->CurrentlyShownApplicationViewId, + ViewSizePreference::Default)) + .then( + [safeFrameServiceCreation](bool viewShown) { + // SafeFrameServiceCreation is used to automatically remove the frame + // from the list of frames if something goes bad. + safeFrameServiceCreation->SetOperationSuccess(viewShown); + }, + task_continuation_context::use_current()); + } } } - } - TraceLogger::GetInstance().LogNewWindowCreationEnd(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); - })); + })); } else { - TraceLogger::GetInstance().LogNewWindowCreationBegin(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); - - ActivationViewSwitcher^ activationViewSwitcher; - auto activateEventArgs = dynamic_cast(args); + ActivationViewSwitcher ^ activationViewSwitcher; + auto activateEventArgs = dynamic_cast(args); if (activateEventArgs != nullptr) { activationViewSwitcher = activateEventArgs->ViewSwitcher; @@ -383,13 +349,12 @@ void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) if (activationViewSwitcher != nullptr) { - activationViewSwitcher->ShowAsStandaloneAsync(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()), ViewSizePreference::Default); - TraceLogger::GetInstance().LogNewWindowCreationEnd(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); - TraceLogger::GetInstance().LogPrelaunchedAppActivatedByUser(); + activationViewSwitcher->ShowAsStandaloneAsync( + ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()), ViewSizePreference::Default); } else { - TraceLogger::GetInstance().LogError(L"Null_ActivationViewSwitcher"); + TraceLogger::GetInstance()->LogError(ViewMode::None, L"App::OnAppLaunch", L"Null_ActivationViewSwitcher"); } } // Set the preLaunched flag to false @@ -404,39 +369,43 @@ void App::OnAppLaunch(IActivatedEventArgs^ args, String^ argument) // parameter if (!rootFrame->Navigate(MainPage::typeid, argument)) { - // We couldn't navigate to the main page, + // We couldn't navigate to the main page, // kill the app so we have a good stack to debug throw std::bad_exception(); } } - if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) + if (ApplicationView::GetForCurrentView()->ViewMode != ApplicationViewMode::CompactOverlay) { - // for tablet mode: since system view activation policy is disabled so do ShowAsStandaloneAsync if activationViewSwitcher exists in activationArgs - ActivationViewSwitcher^ activationViewSwitcher; - auto activateEventArgs = dynamic_cast(args); - if (activateEventArgs != nullptr) + if (!Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { - activationViewSwitcher = activateEventArgs->ViewSwitcher; - } - if (activationViewSwitcher != nullptr) - { - auto viewId = safe_cast(args)->CurrentlyShownApplicationViewId; - if (viewId != 0) + // for tablet mode: since system view activation policy is disabled so do ShowAsStandaloneAsync if activationViewSwitcher exists in + // activationArgs + ActivationViewSwitcher ^ activationViewSwitcher; + auto activateEventArgs = dynamic_cast(args); + if (activateEventArgs != nullptr) { - activationViewSwitcher->ShowAsStandaloneAsync(viewId); + activationViewSwitcher = activateEventArgs->ViewSwitcher; + } + if (activationViewSwitcher != nullptr) + { + auto viewId = safe_cast(args)->CurrentlyShownApplicationViewId; + if (viewId != 0) + { + activationViewSwitcher->ShowAsStandaloneAsync(viewId); + } } } + // Ensure the current window is active + Window::Current->Activate(); } - // Ensure the current window is active - Window::Current->Activate(); } } } -void App::SetMinWindowSizeAndActivate(Frame^ rootFrame, Size minWindowSize) +void App::SetMinWindowSizeAndActivate(Frame ^ rootFrame, Size minWindowSize) { // SetPreferredMinSize should always be called before Window::Activate - ApplicationView^ appView = ApplicationView::GetForCurrentView(); + ApplicationView ^ appView = ApplicationView::GetForCurrentView(); appView->SetPreferredMinSize(minWindowSize); // Place the frame in the current Window @@ -449,25 +418,25 @@ void App::RegisterDependencyProperties() NarratorNotifier::RegisterDependencyProperties(); } -void App::OnActivated(IActivatedEventArgs^ args) +void App::OnActivated(IActivatedEventArgs ^ args) { if (args->Kind == ActivationKind::Protocol) { - TraceLogger::GetInstance().LogWindowActivated(); // We currently don't pass the uri as an argument, // and handle any protocol launch as a normal app launch. OnAppLaunch(args, nullptr); } } -void App::DismissedEventHandler(SplashScreen^ sender, Object^ e) +void CalculatorApp::App::OnSuspending(Object ^ sender, SuspendingEventArgs ^ args) +{ + TraceLogger::GetInstance()->LogButtonUsage(); +} + +void App::DismissedEventHandler(SplashScreen ^ sender, Object ^ e) { SetupJumpList(); } -float App::GetAppWindowHeight() -{ - CoreWindow^ window = CoreWindow::GetForCurrentThread(); - return window->Bounds.Height; -} + diff --git a/src/Calculator/App.xaml.h b/src/Calculator/App.xaml.h index 341f9d3c..f11ebef6 100644 --- a/src/Calculator/App.xaml.h +++ b/src/Calculator/App.xaml.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // @@ -26,28 +26,27 @@ namespace CalculatorApp { public: App(); - virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ args) override; - virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args) override; + virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^ args) override; + virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ args) override; internal: - static bool IsAnimationEnabled(); - static Platform::String^ GetAppViewState(); - static float GetAppWindowHeight(); - void RemoveWindow(_In_ WindowFrameService^ frameService); - void RemoveSecondaryWindow(_In_ WindowFrameService^ frameService); + void RemoveWindow(_In_ WindowFrameService ^ frameService); + void RemoveSecondaryWindow(_In_ WindowFrameService ^ frameService); private: - static Windows::UI::Xaml::Controls::Frame^ CreateFrame(); - static void SetMinWindowSizeAndActivate(Windows::UI::Xaml::Controls::Frame^ rootFrame, Windows::Foundation::Size minWindowSize); + static Windows::UI::Xaml::Controls::Frame ^ CreateFrame(); + static void SetMinWindowSizeAndActivate(Windows::UI::Xaml::Controls::Frame ^ rootFrame, Windows::Foundation::Size minWindowSize); - void OnAppLaunch(Windows::ApplicationModel::Activation::IActivatedEventArgs^ args, Platform::String^ argument); - void DismissedEventHandler(Windows::ApplicationModel::Activation::SplashScreen^ sender, Platform::Object^ e); + void OnAppLaunch(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ args, Platform::String ^ argument); + void DismissedEventHandler(Windows::ApplicationModel::Activation::SplashScreen ^ sender, Platform::Object ^ e); void RegisterDependencyProperties(); + void OnSuspending(Platform::Object ^ sender, Windows::ApplicationModel::SuspendingEventArgs ^ args); + class SafeFrameWindowCreation final { public: - SafeFrameWindowCreation(_In_ WindowFrameService^ frameService, App^ parent) + SafeFrameWindowCreation(_In_ WindowFrameService ^ frameService, App ^ parent) : m_frameService(frameService) , m_frameOpenedInWindow(false) , m_parent(parent) @@ -70,25 +69,23 @@ namespace CalculatorApp } private: - WindowFrameService^ m_frameService; + WindowFrameService ^ m_frameService; bool m_frameOpenedInWindow; - App^ m_parent; + App ^ m_parent; }; private: concurrency::reader_writer_lock m_windowsMapLock; - std::unordered_map m_secondaryWindows; + std::unordered_map m_secondaryWindows; concurrency::task SetupJumpList(); - concurrency::task HandleViewReleaseAndRemoveWindowFromMap(_In_ WindowFrameService^ frameService); - void AddWindowToMap(_In_ WindowFrameService^ frameService); - WindowFrameService^ GetWindowFromMap(int viewId); + concurrency::task HandleViewReleaseAndRemoveWindowFromMap(_In_ WindowFrameService ^ frameService); + void AddWindowToMap(_In_ WindowFrameService ^ frameService); + WindowFrameService ^ GetWindowFromMap(int viewId); void RemoveWindowFromMap(int viewId); int m_mainViewId; bool m_preLaunched; - Windows::UI::Xaml::Controls::Primitives::Popup^ m_aboutPopup; - - static bool m_isAnimationEnabled; + Windows::UI::Xaml::Controls::Primitives::Popup ^ m_aboutPopup; }; } diff --git a/src/Calculator/Assets/CalcMDL2.ttf b/src/Calculator/Assets/CalcMDL2.ttf index a4b8862c..9ee5deb0 100644 Binary files a/src/Calculator/Assets/CalcMDL2.ttf and b/src/Calculator/Assets/CalcMDL2.ttf differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorAppList.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorAppList.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorAppList.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorAppList.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorAppList.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorAppList.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorAppList.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorAppList.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorAppList.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorAppList.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorAppList.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorAppList.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorAppList.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorAppList.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorAppList.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorAppList.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorAppList.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorAppList.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorAppList.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorAppList.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-colorize.png deleted file mode 100644 index dd7e068c..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-fullcolor.png deleted file mode 100644 index 595006db..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-16_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-16.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-16.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-16.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-16.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-colorize.png deleted file mode 100644 index 497fb90d..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-fullcolor.png deleted file mode 100644 index 26b7acb0..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-20_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-20.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-20.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-20.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-20.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-colorize.png deleted file mode 100644 index b1e7ccf0..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-fullcolor.png deleted file mode 100644 index b64a299b..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-24_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-24.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-24.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-24.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-24.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-colorize.png deleted file mode 100644 index acb3f81e..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-fullcolor.png deleted file mode 100644 index c0fff46a..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-256.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-256.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-256.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-256.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-colorize.png deleted file mode 100644 index 27060604..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-fullcolor.png deleted file mode 100644 index 32bd4fff..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-30_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-30.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-30.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-30.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-30.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-colorize.png deleted file mode 100644 index e4b2ecbb..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-fullcolor.png deleted file mode 100644 index 99ef41bc..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-32_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-32.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-32.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-32.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-32.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-colorize.png deleted file mode 100644 index c6599e87..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-fullcolor.png deleted file mode 100644 index bc98a3e6..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-36_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-36.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-36.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-36.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-36.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-colorize.png deleted file mode 100644 index f963a7a1..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-fullcolor.png deleted file mode 100644 index 2f7c31c0..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-40_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-40.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-40.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-40.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-40.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-colorize.png deleted file mode 100644 index b801ca63..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-fullcolor.png deleted file mode 100644 index 247025e7..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-48.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-48.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-48.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-48.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-colorize.png deleted file mode 100644 index 50f13e74..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-fullcolor.png deleted file mode 100644 index e1ee8dbc..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-60_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-60.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-60.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-60.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-60.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-colorize.png deleted file mode 100644 index aa476901..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-fullcolor.png deleted file mode 100644 index a6e694ef..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-64_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-64.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-64.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-64.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-64.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-colorize.png deleted file mode 100644 index fa1cb369..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-fullcolor.png deleted file mode 100644 index 67737faa..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-72_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-72.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-72.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-72.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-72.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-colorize.png deleted file mode 100644 index b0fd8d88..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-fullcolor.png deleted file mode 100644 index b2692004..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-80_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-80.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-80.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-80.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-80.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-colorize.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-colorize.png deleted file mode 100644 index 040e9405..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-colorize.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-fullcolor.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-fullcolor.png deleted file mode 100644 index 2855521e..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-fullcolor.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-96.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-96.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-96.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-96.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100.altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100.altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125.altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-125.altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png new file mode 100644 index 00000000..d1f3b077 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150.altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-150.altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png new file mode 100644 index 00000000..dd35d6b2 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200.altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-200.altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png new file mode 100644 index 00000000..045d5cd2 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400.altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-400.altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png new file mode 100644 index 00000000..e9dd7b14 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png new file mode 100644 index 00000000..c09eb4b6 Binary files /dev/null and b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/Graphing.targetsize-16.png b/src/Calculator/Assets/Graphing.targetsize-16.png new file mode 100644 index 00000000..485e7349 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png new file mode 100644 index 00000000..485e7349 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png new file mode 100644 index 00000000..46788045 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20.png b/src/Calculator/Assets/Graphing.targetsize-20.png new file mode 100644 index 00000000..9b1754ed Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png new file mode 100644 index 00000000..9b1754ed Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png new file mode 100644 index 00000000..517e7413 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24.png b/src/Calculator/Assets/Graphing.targetsize-24.png new file mode 100644 index 00000000..2670817a Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png new file mode 100644 index 00000000..2670817a Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png new file mode 100644 index 00000000..f603fc85 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32.png b/src/Calculator/Assets/Graphing.targetsize-32.png new file mode 100644 index 00000000..b8ae7bb5 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png new file mode 100644 index 00000000..b8ae7bb5 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png new file mode 100644 index 00000000..8dd829ac Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64.png b/src/Calculator/Assets/Graphing.targetsize-64.png new file mode 100644 index 00000000..e347e193 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png new file mode 100644 index 00000000..e347e193 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png new file mode 100644 index 00000000..d5cdc05b Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png differ diff --git a/src/Calculator/Calculator.vcxproj b/src/Calculator/Calculator.vcxproj index f29fd3d4..5b8435d4 100644 --- a/src/Calculator/Calculator.vcxproj +++ b/src/Calculator/Calculator.vcxproj @@ -8,13 +8,17 @@ 15.0 true Windows Store - 10.0.17763.0 + 10.0.18362.0 10.0.17134.0 false false 10.0 black + Always + WindowsDev_TemporaryKey.pfx + True + False @@ -58,46 +62,46 @@ Application true - v141 + v142 Application true - v141 + v142 Application true - v141 + v142 Application true - v141 + v142 Application false true - v141 + v142 Application false true - v141 + v142 Application false true - v141 + v142 Application false true - v141 + v142 @@ -126,19 +130,12 @@ - - TemporaryKey.pfx - true - True - False - - - false - Never - + + + - /bigobj /await /std:c++17 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -147,7 +144,7 @@ - /bigobj /await /std:c++17 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -156,7 +153,7 @@ - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -168,7 +165,7 @@ - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -180,7 +177,7 @@ - /bigobj /await /std:c++17 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -189,7 +186,7 @@ - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -201,7 +198,7 @@ - /bigobj /await /std:c++17 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -210,7 +207,7 @@ - /bigobj /await /std:c++17 %(AdditionalOptions) + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -222,12 +219,15 @@ - /DSEND_TELEMETRY %(AdditionalOptions) + /DSEND_DIAGNOSTICS /DIS_STORE_BUILD %(AdditionalOptions) 0.0.0.0 + + true + @@ -236,32 +236,39 @@ AboutFlyout.xaml + + + + - - - - - - + + + + + App.xaml - + + EquationStylePanelControl.xaml + + + Views\Calculator.xaml @@ -286,6 +293,21 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + + + Views\GraphingCalculator\GraphingSettings.xaml + + + Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml + + + Views\GraphingCalculator\GraphingNumPad.xaml + Views\HistoryList.xaml @@ -305,6 +327,7 @@ Views\OperatorsPanel.xaml + Views\SupplementaryResults.xaml @@ -324,6 +347,9 @@ Designer + + Designer + Designer @@ -341,6 +367,11 @@ + + + + + @@ -359,9 +390,7 @@ Designer - - - + @@ -371,25 +400,28 @@ App.xaml + + + - - - - - + + + + + Create Create @@ -400,7 +432,11 @@ Create Create - + + EquationStylePanelControl.xaml + + + Views\Calculator.xaml @@ -425,6 +461,21 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + + + Views\GraphingCalculator\GraphingSettings.xaml + + + Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml + + + Views\GraphingCalculator\GraphingNumPad.xaml + Views\HistoryList.xaml @@ -444,6 +495,7 @@ Views\OperatorsPanel.xaml + Views\SupplementaryResults.xaml @@ -459,334 +511,368 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -797,6 +883,21 @@ + + + + + + + + + + + + + + + @@ -837,6 +938,9 @@ {90e9761d-9262-4773-942d-caeae75d7140} + + {e727a92b-f149-492c-8117-c039a298719b} + @@ -856,7 +960,7 @@ - + @@ -864,6 +968,6 @@ - + \ No newline at end of file diff --git a/src/Calculator/Calculator.vcxproj.filters b/src/Calculator/Calculator.vcxproj.filters index f17fe368..d657e8d0 100644 --- a/src/Calculator/Calculator.vcxproj.filters +++ b/src/Calculator/Calculator.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -218,6 +218,12 @@ {0120c344-0bc0-4a1d-b82c-df7945f46189} + + {e23e2a6e-491b-4200-9bf7-d355a1ee695b} + + + {b491a249-26b8-4814-9f50-2c3a57018c56} + @@ -225,9 +231,6 @@ - - Common - Controls @@ -243,9 +246,6 @@ Converters - - Converters - Converters @@ -271,12 +271,6 @@ - - Controls - - - Controls - Controls @@ -287,9 +281,6 @@ - - Common - Controls @@ -299,7 +290,6 @@ Views\StateTriggers - @@ -308,6 +298,37 @@ Common + + Controls + + + + Views\StateTriggers + + + Controls + + + Controls + + + + + Controls + + + + Views\GraphingCalculator + + + + Utils + + + Common + + + @@ -315,12 +336,6 @@ Common - - Common - - - Controls - Controls @@ -330,18 +345,12 @@ Controls - - Converters - Converters Converters - - Converters - Converters @@ -367,12 +376,6 @@ - - Controls - - - Controls - Controls @@ -383,12 +386,6 @@ - - Converters - - - Common - Controls @@ -398,7 +395,6 @@ Views\StateTriggers - @@ -407,12 +403,47 @@ Common + + Controls + + + Views\StateTriggers + + + + Controls + + + Controls + + + + + Controls + + + + Views\GraphingCalculator + + + + Utils + + + Common + + + Common + + + + @@ -455,24 +486,43 @@ Views - - Views - Views Views - - Views - Views Views + + Views + + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + Views + + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + + Views\StateTriggers + @@ -484,9 +534,6 @@ Resources\az-Latn-az - - Resources\uz-latn-uz - Resources\af-za @@ -499,9 +546,6 @@ Resources\vi-vn - - Resources\be-by - Resources\am-et @@ -526,12 +570,6 @@ Resources\zh-cn - - Resources\sw-ke - - - Resources\bn-bd - Resources\ta-in @@ -622,9 +660,6 @@ Resources\pt-br - - Resources\ha-latn-ng - Resources\lt-lt @@ -757,9 +792,6 @@ Resources\ru-ru - - Resources\ha-latn-ng - Resources\lo-la @@ -802,9 +834,6 @@ Resources\el-gr - - Resources\sw-ke - Resources\uk-ua @@ -814,9 +843,6 @@ Resources\vi-vn - - Resources\uz-latn-uz - Resources\tr-tr @@ -826,9 +852,6 @@ Resources\zh-tw - - Resources\bn-bd - Resources\cs-cz @@ -850,9 +873,6 @@ Resources\ar-sa - - Resources\be-by - Resources\kk-kz @@ -867,534 +887,6 @@ - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - Assets @@ -1515,48 +1007,552 @@ Assets - + Assets - + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + Assets Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + @@ -1566,4 +1562,8 @@ + + + + \ No newline at end of file diff --git a/src/Calculator/Common/AlwaysSelectedCollectionView.h b/src/Calculator/Common/AlwaysSelectedCollectionView.h index 6e702c79..35d40095 100644 --- a/src/Calculator/Common/AlwaysSelectedCollectionView.h +++ b/src/Calculator/Common/AlwaysSelectedCollectionView.h @@ -3,107 +3,104 @@ #pragma once -namespace CalculatorApp { namespace Common +namespace CalculatorApp { - ref class AlwaysSelectedCollectionView sealed: - public Windows::UI::Xaml::DependencyObject, - public Windows::UI::Xaml::Data::ICollectionView + namespace Common { - internal: - AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector^ source): - m_currentPosition(-1) + ref class AlwaysSelectedCollectionView sealed : public Windows::UI::Xaml::DependencyObject, public Windows::UI::Xaml::Data::ICollectionView { - m_source = source; + internal : AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector ^ source) + : m_currentPosition(-1) + { + m_source = source; - Windows::UI::Xaml::Interop::IBindableObservableVector^ observable = dynamic_cast(source); - if (observable) - { - observable->VectorChanged += - ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged); - } - } - - private: - // ICollectionView - // Not implemented methods - virtual Windows::Foundation::IAsyncOperation^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext - { - throw ref new Platform::NotImplementedException(); - } - virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious - { - throw ref new Platform::NotImplementedException(); - } - property Windows::Foundation::Collections::IObservableVector^ CollectionGroups - { - virtual Windows::Foundation::Collections::IObservableVector^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get - { - return ref new Platform::Collections::Vector(); - } - } - property bool HasMoreItems - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get - { - return false; - } - } - - // Implementented methods - virtual bool MoveCurrentTo(Platform::Object^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo - { - if (item) - { - unsigned int newCurrentPosition = 0; - bool result = m_source->IndexOf(item, &newCurrentPosition); - if (result) + Windows::UI::Xaml::Interop::IBindableObservableVector ^ observable = + dynamic_cast(source); + if (observable) { - m_currentPosition = newCurrentPosition; - m_currentChanged(this, nullptr); - return true; + observable->VectorChanged += ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler( + this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged); } } - // The item is not in the collection - // We're going to schedule a call back later so we - // restore the selection to the way we wanted it to begin with - if (m_currentPosition >= 0 && m_currentPosition < static_cast(m_source->Size)) + private: + // ICollectionView + // Not implemented methods + virtual Windows::Foundation::IAsyncOperation< + Windows::UI::Xaml::Data::LoadMoreItemsResult> ^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync { - this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, - ref new Windows::UI::Core::DispatchedHandler( - [this]() - { - m_currentChanged(this, nullptr); - })); + throw ref new Platform::NotImplementedException(); } - return false; - } - - virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition - { - if (index < 0 || index >= static_cast(m_source->Size)) + virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast + { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext + { + throw ref new Platform::NotImplementedException(); + } + virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious + { + throw ref new Platform::NotImplementedException(); + } + property Windows::Foundation::Collections::IObservableVector ^ CollectionGroups { + virtual Windows::Foundation::Collections::IObservableVector< + Platform::Object ^> ^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get + { + return ref new Platform::Collections::Vector(); + } + } property bool HasMoreItems + { + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get + { + return false; + } + } + + // Implementented methods + virtual bool MoveCurrentTo(Platform::Object ^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo + { + if (item) + { + unsigned int newCurrentPosition = 0; + bool result = m_source->IndexOf(item, &newCurrentPosition); + if (result) + { + m_currentPosition = newCurrentPosition; + m_currentChanged(this, nullptr); + return true; + } + } + + // The item is not in the collection + // We're going to schedule a call back later so we + // restore the selection to the way we wanted it to begin with + if (m_currentPosition >= 0 && m_currentPosition < static_cast(m_source->Size)) + { + this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]() { + m_currentChanged(this, nullptr); + })); + } return false; } - m_currentPosition = index; - m_currentChanged(this, nullptr); - return true; - } + virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition + { + if (index < 0 || index >= static_cast(m_source->Size)) + { + return false; + } - property Platform::Object^ CurrentItem + m_currentPosition = index; + m_currentChanged(this, nullptr); + return true; + } + + property Platform::Object^ CurrentItem { virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get { @@ -116,30 +113,30 @@ namespace CalculatorApp { namespace Common } property int CurrentPosition - { - virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get { - return m_currentPosition; + virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get + { + return m_currentPosition; + } } - } - property bool IsCurrentAfterLast - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get + property bool IsCurrentAfterLast { - return m_currentPosition >= static_cast(m_source->Size); + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get + { + return m_currentPosition >= static_cast(m_source->Size); + } } - } - property bool IsCurrentBeforeFirst - { - virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get + property bool IsCurrentBeforeFirst { - return m_currentPosition < 0; + virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get + { + return m_currentPosition < 0; + } } - } - event Windows::Foundation::EventHandler^ CurrentChanged + event Windows::Foundation::EventHandler^ CurrentChanged { virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::EventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add { @@ -165,63 +162,67 @@ namespace CalculatorApp { namespace Common // IVector // Not implemented methods virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::Append - { - throw ref new Platform::NotImplementedException(); - } - virtual void Clear() = Windows::Foundation::Collections::IVector::Clear - { - throw ref new Platform::NotImplementedException(); - } - virtual unsigned int GetMany(unsigned int /*startIndex*/, Platform::WriteOnlyArray^ /*items*/) = Windows::Foundation::Collections::IVector::GetMany - { - throw ref new Platform::NotImplementedException(); - } - virtual Windows::Foundation::Collections::IVectorView^ GetView() = Windows::Foundation::Collections::IVector::GetView - { - throw ref new Platform::NotImplementedException(); - } - virtual void InsertAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::InsertAt - { - throw ref new Platform::NotImplementedException(); - } - virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector::RemoveAt - { - throw ref new Platform::NotImplementedException(); - } - virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector::RemoveAtEnd - { - throw ref new Platform::NotImplementedException(); - } - virtual void ReplaceAll(const Platform::Array^ /*items*/) = Windows::Foundation::Collections::IVector::ReplaceAll - { - throw ref new Platform::NotImplementedException(); - } - virtual void SetAt(unsigned int /*index*/, Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector::SetAt - { - throw ref new Platform::NotImplementedException(); - } - - // Implemented methods - virtual Platform::Object^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector::GetAt - { - return m_source->GetAt(index); - } - - virtual bool IndexOf(Platform::Object^ item, unsigned int* index) = Windows::Foundation::Collections::IVector::IndexOf - { - return m_source->IndexOf(item, index); - } - - property unsigned int Size - { - virtual unsigned int get() = Windows::Foundation::Collections::IVector::Size::get { - return m_source->Size; + throw ref new Platform::NotImplementedException(); + } + virtual void Clear() = Windows::Foundation::Collections::IVector::Clear + { + throw ref new Platform::NotImplementedException(); + } + virtual unsigned int GetMany( + unsigned int /*startIndex*/, + Platform::WriteOnlyArray ^ /*items*/) = Windows::Foundation::Collections::IVector::GetMany + { + throw ref new Platform::NotImplementedException(); + } + virtual Windows::Foundation::Collections::IVectorView ^ GetView() = Windows::Foundation::Collections::IVector< + Platform::Object ^>::GetView + { + throw ref new Platform::NotImplementedException(); + } + virtual void InsertAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector::InsertAt + { + throw ref new Platform::NotImplementedException(); + } + virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector::RemoveAt + { + throw ref new Platform::NotImplementedException(); + } + virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector::RemoveAtEnd + { + throw ref new Platform::NotImplementedException(); + } + virtual void + ReplaceAll(const Platform::Array ^ /*items*/) = Windows::Foundation::Collections::IVector::ReplaceAll + { + throw ref new Platform::NotImplementedException(); + } + virtual void SetAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector::SetAt + { + throw ref new Platform::NotImplementedException(); } - } - // IObservableVector - event Windows::Foundation::Collections::VectorChangedEventHandler^ VectorChanged + // Implemented methods + virtual Platform::Object ^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector::GetAt + { + return m_source->GetAt(index); + } + + virtual bool IndexOf(Platform::Object ^ item, unsigned int* index) = Windows::Foundation::Collections::IVector::IndexOf + { + return m_source->IndexOf(item, index); + } + + property unsigned int Size + { + virtual unsigned int get() = Windows::Foundation::Collections::IVector::Size::get + { + return m_source->Size; + } + } + + // IObservableVector + event Windows::Foundation::Collections::VectorChangedEventHandler^ VectorChanged { virtual Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler^ handler) = Windows::Foundation::Collections::IObservableVector::VectorChanged::add { @@ -236,52 +237,57 @@ namespace CalculatorApp { namespace Common // IIterable // Not implemented virtual Windows::Foundation::Collections::IIterator^ First() = Windows::Foundation::Collections::IIterable::First - { - throw ref new Platform::NotImplementedException(); - } - - // Event handlers - void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector^ source, Platform::Object^ e) - { - Windows::Foundation::Collections::IVectorChangedEventArgs^ args = safe_cast(e); - m_vectorChanged(this, args); - } - - Windows::UI::Xaml::Interop::IBindableVector^ m_source; - int m_currentPosition; - event Windows::Foundation::EventHandler^ m_currentChanged; - event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ m_currentChanging; - event Windows::Foundation::Collections::VectorChangedEventHandler^ m_vectorChanged; - }; - - public ref class AlwaysSelectedCollectionViewConverter sealed: public Windows::UI::Xaml::Data::IValueConverter - { - public: - AlwaysSelectedCollectionViewConverter() - { } - - private: - virtual Platform::Object^ Convert( - Platform::Object^ value, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert - { - auto result = dynamic_cast(value); - if (result) { - return ref new AlwaysSelectedCollectionView(result); + throw ref new Platform::NotImplementedException(); } - return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert - } - virtual Platform::Object^ ConvertBack( - Platform::Object^ /*value*/, - Windows::UI::Xaml::Interop::TypeName /*targetType*/, - Platform::Object^ /*parameter*/, - Platform::String^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + // Event handlers + void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector ^ source, Platform::Object ^ e) + { + Windows::Foundation::Collections::IVectorChangedEventArgs ^ args = safe_cast(e); + m_vectorChanged(this, args); + } + + Windows::UI::Xaml::Interop::IBindableVector ^ m_source; + int m_currentPosition; + event Windows::Foundation::EventHandler ^ m_currentChanged; + event Windows::UI::Xaml::Data::CurrentChangingEventHandler ^ m_currentChanging; + event Windows::Foundation::Collections::VectorChangedEventHandler ^ m_vectorChanged; + }; + + public + ref class AlwaysSelectedCollectionViewConverter sealed : public Windows::UI::Xaml::Data::IValueConverter { - return Windows::UI::Xaml::DependencyProperty::UnsetValue; - } - }; -}} + public: + AlwaysSelectedCollectionViewConverter() + { + } + + private: + virtual Platform::Object + ^ Convert( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert + { + auto result = dynamic_cast(value); + if (result) + { + return ref new AlwaysSelectedCollectionView(result); + } + return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert + } + + virtual Platform::Object + ^ ConvertBack( + Platform::Object ^ /*value*/, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + { + return Windows::UI::Xaml::DependencyProperty::UnsetValue; + } + }; + } +} diff --git a/src/Calculator/Common/AppLifecycleLogger.cpp b/src/Calculator/Common/AppLifecycleLogger.cpp index 5bfb6141..bc4b760f 100644 --- a/src/Calculator/Common/AppLifecycleLogger.cpp +++ b/src/Calculator/Common/AppLifecycleLogger.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "AppLifecycleLogger.h" -#include "CalcViewModel/Common/TraceActivity.h" +#include using namespace std; using namespace winrt; @@ -15,28 +15,27 @@ using namespace winrt::Windows::UI::ViewManagement; namespace CalculatorApp { - -#ifdef SEND_TELEMETRY +#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_CRITICAL_DATA = 0x0000800000000000; // Bit 47 - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0x0000400000000000; // Bit 46 - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0x0000200000000000; // Bit 45 - constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment) + 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 + constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment) #else - // define all Keyword options as 0 when we do not want to upload app telemetry - constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0; - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0; - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0; + // 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; constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0; #endif #pragma region TraceLogger setup and cleanup - AppLifecycleLogger::AppLifecycleLogger() : - m_appLifecycleProvider( + AppLifecycleLogger::AppLifecycleLogger() + : m_appLifecycleProvider( L"Microsoft.Windows.AppLifeCycle", - LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), // Microsoft Telemetry group + LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), GUID{ 0xef00584a, 0x2655, 0x462c, 0xbc, 0x24, 0xe7, 0xde, 0x63, 0xe, 0x7f, 0xbf }) // Unique provider ID {EF00584A-2655-462C-BC24-E7DE630E7FBF} { } @@ -59,13 +58,15 @@ namespace CalculatorApp #pragma region Tracing methods void AppLifecycleLogger::LogAppLifecycleEvent(hstring const& eventName, LoggingFields const& fields) const { - m_appLifecycleProvider.LogEvent(eventName, fields, LoggingLevel::Information, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY | WINEVENT_KEYWORD_RESPONSE_TIME)); + m_appLifecycleProvider.LogEvent( + eventName, fields, LoggingLevel::Information, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_3 | WINEVENT_KEYWORD_RESPONSE_TIME)); } #pragma endregion void AppLifecycleLogger::LaunchUIResponsive() const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -74,7 +75,8 @@ namespace CalculatorApp void AppLifecycleLogger::LaunchVisibleComplete() const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -83,7 +85,8 @@ namespace CalculatorApp void AppLifecycleLogger::ResumeUIResponsive() const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -92,7 +95,8 @@ namespace CalculatorApp void AppLifecycleLogger::ResumeVisibleComplete() const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -106,7 +110,8 @@ namespace CalculatorApp void AppLifecycleLogger::ResizeUIResponsive(int32_t viewId) const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -121,7 +126,8 @@ namespace CalculatorApp void AppLifecycleLogger::ResizeVisibleComplete(int32_t viewId) const { - if (!GetTraceLoggingProviderEnabled()) return; + if (!GetTraceLoggingProviderEnabled()) + return; LoggingFields fields{}; PopulateAppInfo(fields); @@ -141,6 +147,3 @@ namespace CalculatorApp fields.AddString(L"PsmKey", psmKey); } } - - - diff --git a/src/Calculator/Common/AppLifecycleLogger.h b/src/Calculator/Common/AppLifecycleLogger.h index ca7ac65c..fdb8702e 100644 --- a/src/Calculator/Common/AppLifecycleLogger.h +++ b/src/Calculator/Common/AppLifecycleLogger.h @@ -9,7 +9,7 @@ namespace CalculatorApp { public: AppLifecycleLogger(AppLifecycleLogger const&) = delete; - AppLifecycleLogger const & operator= (AppLifecycleLogger const&) = delete; + AppLifecycleLogger const& operator=(AppLifecycleLogger const&) = delete; ~AppLifecycleLogger(); static AppLifecycleLogger& GetInstance(); bool GetTraceLoggingProviderEnabled() const; @@ -29,10 +29,10 @@ namespace CalculatorApp AppLifecycleLogger(); // Any new Log method should - // a) Decide the level of logging. This will help us in limiting recording of events only up to a certain level. See this link for guidance https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx - // We're using Verbose level for events that are called frequently and needed only for debugging or capturing perf for specific scenarios - // b) Should decide whether or not to log to telemetry and pass TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY) accordingly - // c) Should accept a variable number of additional data arguments if needed + // a) Decide the level of logging. This will help us in limiting recording of events only up to a certain level. See this link for guidance + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx We're using Verbose level for events that are called frequently and + // needed only for debugging or capturing perf for specific scenarios b) Should decide whether or not to log to diagnostics and pass + // TraceLoggingKeyword(MICROSOFT_KEYWORD_LEVEL_3) accordingly c) Should accept a variable number of additional data arguments if needed void LogAppLifecycleEvent(winrt::hstring const& eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields const& fields) const; void PopulateAppInfo(winrt::Windows::Foundation::Diagnostics::LoggingFields& fields) const; diff --git a/src/Calculator/Common/BindableBase.cpp b/src/Calculator/Common/BindableBase.cpp deleted file mode 100644 index b1fdcc78..00000000 --- a/src/Calculator/Common/BindableBase.cpp +++ /dev/null @@ -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; - -/// -/// Notifies listeners that a property value has changed. -/// -/// Name of the property used to notify listeners. -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(); -} diff --git a/src/Calculator/Common/BindableBase.h b/src/Calculator/Common/BindableBase.h deleted file mode 100644 index b6a9f32e..00000000 --- a/src/Calculator/Common/BindableBase.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - /// - /// Implementation of to simplify models. - /// - [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); - }; - } -} diff --git a/src/Calculator/Common/KeyboardShortcutManager.cpp b/src/Calculator/Common/KeyboardShortcutManager.cpp new file mode 100644 index 00000000..bed0c4b2 --- /dev/null +++ b/src/Calculator/Common/KeyboardShortcutManager.cpp @@ -0,0 +1,691 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "KeyboardShortcutManager.h" +#include "CalcViewModel/Common/AppResourceProvider.h" +#include "CalcViewModel/ApplicationViewModel.h" +#include "CalcViewModel/Common/LocalizationSettings.h" + +using namespace Concurrency; +using namespace Platform; +using namespace std; +using namespace chrono; +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); + +map> KeyboardShortcutManager::s_characterForButtons; +map> KeyboardShortcutManager::s_virtualKey; +map> KeyboardShortcutManager::s_VirtualKeyControlChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyShiftChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyAltChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyControlShiftChordsForButtons; + +map KeyboardShortcutManager::s_IsDropDownOpen; +map KeyboardShortcutManager::s_ignoreNextEscape; +map KeyboardShortcutManager::s_keepIgnoringEscape; +map KeyboardShortcutManager::s_fHonorShortcuts; +map KeyboardShortcutManager::s_fDisableShortcuts; +reader_writer_lock KeyboardShortcutManager::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 + void LightUpButtons(const T& buttons) + { + auto iterator = buttons.first; + for (; iterator != buttons.second; ++iterator) + { + auto button = iterator->second.Resolve(); + 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(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([buttonWeakReference, timerWeakReference](Object ^, Object ^) { + auto button = buttonWeakReference.Resolve(); + if (button) + { + VisualStateManager::GoToState(button, "Normal", true); + } + + // Cancel the timer after we're done so it only fires once + auto timer = timerWeakReference.Resolve(); + 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 + void RunFirstEnabledButtonCommand(const T& buttons) + { + auto buttonIterator = buttons.first; + for (; buttonIterator != buttons.second; ++buttonIterator) + { + auto button = buttonIterator->second.Resolve(); + 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(button); + if (radio) + { + radio->IsChecked = true; + return; + } + + auto toggle = dynamic_cast(button); + if (toggle) + { + toggle->IsChecked = !(toggle->IsChecked != nullptr && toggle->IsChecked->Value); + return; + } + } + } + } +} + +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[viewId] = true; + } + + if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) + { + s_keepIgnoringEscape[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[viewId] = false; + } + + if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) + { + s_keepIgnoringEscape[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(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(make_pair(decSep, WeakReference(button))); + } + else + { + iterViewMap->second.insert(make_pair(newValue->Data()[0], WeakReference(button))); + } + } + } + else + { + s_characterForButtons.insert(make_pair(viewId, multimap())); + + if (newValue == L".") + { + wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator(); + s_characterForButtons.find(viewId)->second.insert(make_pair(decSep, WeakReference(button))); + } + else + { + s_characterForButtons.find(viewId)->second.insert(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(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_virtualKey.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_virtualKey.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_virtualKey.insert(make_pair(viewId, multimap())); + s_virtualKey.find(viewId)->second.insert(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(target); + + if (control == nullptr) + { + // Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case + control = safe_cast(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(make_pair(newValue, WeakReference(control))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyControlChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(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(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(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyShiftChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(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(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(make_pair(newValue, WeakReference(navView))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyAltChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(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(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(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyControlShiftChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(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() || currentHonorShortcuts->second) + { + wchar_t character = static_cast(args->KeyCode); + auto buttons = s_characterForButtons.find(viewId)->second.equal_range(character); + RunFirstEnabledButtonCommand(buttons); + + LightUpButtons(buttons); + } +} + +const multimap* KeyboardShortcutManager::GetCurrentKeyDictionary(bool controlKeyPressed, bool shiftKeyPressed, bool altPressed) +{ + int viewId = Utils::GetWindowId(); + + if (controlKeyPressed) + { + if (altPressed) + { + return nullptr; + } + else + { + if (shiftKeyPressed) + { + return &s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second; + } + else + { + return &s_VirtualKeyControlChordsForButtons.find(viewId)->second; + } + } + } + else + { + if (altPressed) + { + if (!shiftKeyPressed) + { + return &s_VirtualKeyAltChordsForButtons.find(viewId)->second; + } + else + { + return nullptr; + } + } + else + { + if (shiftKeyPressed) + { + return &s_VirtualKeyShiftChordsForButtons.find(viewId)->second; + } + else + { + return &s_virtualKey.find(viewId)->second; + } + } + } +} + +void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs ^ args) +{ + if (args->Handled) + { + return; + } + + auto key = args->VirtualKey; + int viewId = Utils::GetWindowId(); + + const bool isControlKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const bool isShiftKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const bool isAltKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + + // Handle Ctrl + E for DateCalculator + if ((key == VirtualKey::E) && isControlKeyPressed && !isShiftKeyPressed && !isAltKeyPressed) + { + const auto lookupMap = GetCurrentKeyDictionary(isControlKeyPressed, isShiftKeyPressed, false); + if (lookupMap == nullptr) + { + return; + } + + auto buttons = lookupMap->equal_range(static_cast(key)); + auto navView = buttons.first->second.Resolve(); + auto appViewModel = safe_cast(navView->DataContext); + appViewModel->Mode = ViewMode::Date; + auto categoryName = AppResourceProvider::GetInstance()->GetResourceString(L"DateCalculationModeText"); + appViewModel->CategoryName = categoryName; + + auto menuItems = static_cast ^>(navView->MenuItemsSource); + auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date); + navView->SelectedItem = menuItems->GetAt(flatIndex); + return; + } + + 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; + } + } + } + + auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId); + if (currentHonorShortcuts != s_fHonorShortcuts.end()) + { + if (currentHonorShortcuts->second) + { + const auto myVirtualKey = static_cast(key); + const auto lookupMap = GetCurrentKeyDictionary(isControlKeyPressed, isShiftKeyPressed, isAltKeyPressed); + if (lookupMap == nullptr) + { + return; + } + + auto buttons = lookupMap->equal_range(myVirtualKey); + if (buttons.first == buttons.second) + { + return; + } + + 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 + auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId); + 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::OnAcceleratorKeyActivated(CoreDispatcher ^, AcceleratorKeyEventArgs ^ args) +{ + if (args->KeyStatus.IsKeyReleased) + { + auto key = args->VirtualKey; + const 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 bool controlKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + // Ctrl is pressed in addition to alt, this means Alt Gr is intended. do not navigate. + if (controlKeyPressed) + { + return; + } + + const bool shiftKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const auto lookupMap = GetCurrentKeyDictionary(controlKeyPressed, shiftKeyPressed, altPressed); + if (lookupMap != nullptr) + { + auto listItems = lookupMap->equal_range(static_cast(key)); + for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator) + { + auto item = listIterator->second.Resolve(); + if (item != nullptr) + { + auto navView = safe_cast(item); + + auto menuItems = static_cast ^>(navView->MenuItemsSource); + if (menuItems != nullptr) + { + auto vm = safe_cast(navView->DataContext); + if (nullptr != vm) + { + ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast(key)); + auto nvi = dynamic_cast(menuItems->GetAt(NavCategory::GetFlatIndex(toMode))); + if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode)) + { + vm->Mode = toMode; + navView->SelectedItem = nvi; + } + } + } + break; + } + } + } + } +} + +void KeyboardShortcutManager::Initialize() +{ + auto coreWindow = Window::Current->CoreWindow; + coreWindow->CharacterReceived += + ref new TypedEventHandler(&KeyboardShortcutManager::OnCharacterReceivedHandler); + coreWindow->KeyDown += ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyDownHandler); + coreWindow->Dispatcher->AcceleratorKeyActivated += + ref new TypedEventHandler(&KeyboardShortcutManager::OnAcceleratorKeyActivated); + + KeyboardShortcutManager::RegisterNewAppViewId(); +} + +void KeyboardShortcutManager::UpdateDropDownState(bool isOpen) +{ + int viewId = Utils::GetWindowId(); + + if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end()) + { + s_IsDropDownOpen[viewId] = isOpen; + } +} + +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()) + { + if (s_fDisableShortcuts.find(viewId) != s_fDisableShortcuts.end()) + { + if (s_fDisableShortcuts[viewId]) + { + s_fHonorShortcuts[viewId] = false; + return; + } + } + + s_fHonorShortcuts[viewId] = allow; + } +} + +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(make_pair(appViewId, multimap())); + } + + if (s_virtualKey.find(appViewId) == s_virtualKey.end()) + { + s_virtualKey.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end()) + { + s_VirtualKeyControlChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end()) + { + s_VirtualKeyShiftChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end()) + { + s_VirtualKeyAltChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end()) + { + s_VirtualKeyControlShiftChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + s_IsDropDownOpen[appViewId] = false; + s_ignoreNextEscape[appViewId] = false; + s_keepIgnoringEscape[appViewId] = false; + s_fHonorShortcuts[appViewId] = true; + s_fDisableShortcuts[appViewId] = false; +} + +void KeyboardShortcutManager::OnWindowClosed(int viewId) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + s_characterForButtons.erase(viewId); + + s_virtualKey.erase(viewId); + s_VirtualKeyControlChordsForButtons.erase(viewId); + s_VirtualKeyShiftChordsForButtons.erase(viewId); + s_VirtualKeyAltChordsForButtons.erase(viewId); + s_VirtualKeyControlShiftChordsForButtons.erase(viewId); + + s_IsDropDownOpen.erase(viewId); + s_ignoreNextEscape.erase(viewId); + s_keepIgnoringEscape.erase(viewId); + s_fHonorShortcuts.erase(viewId); + s_fDisableShortcuts.erase(viewId); +} + +void KeyboardShortcutManager::DisableShortcuts(bool disable) +{ + int viewId = Utils::GetWindowId(); + + if (s_fDisableShortcuts.find(viewId) != s_fDisableShortcuts.end()) + { + s_fDisableShortcuts[viewId] = disable; + } + + HonorShortcuts(!disable); +} diff --git a/src/Calculator/Common/KeyboardShortcutManager.h b/src/Calculator/Common/KeyboardShortcutManager.h new file mode 100644 index 00000000..1ca7e487 --- /dev/null +++ b/src/Calculator/Common/KeyboardShortcutManager.h @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "CalcViewModel/Common/Utils.h" +#include "CalcViewModel/Common/MyVirtualKey.h" + +namespace CalculatorApp +{ + namespace Common + { + public + ref class KeyboardShortcutManager sealed + { + public: + KeyboardShortcutManager() + { + } + + DEPENDENCY_PROPERTY_OWNER(KeyboardShortcutManager); + + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(Platform::String ^, Character); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKey); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlChord); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyShiftChord); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyAltChord); + DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlShiftChord); + + internal : + + static void + Initialize(); + + // Sometimes, like with popups, escape is treated as special and even + // though it is handled we get it passed through to us. In those cases + // we need to be able to ignore it (looking at e->Handled isn't sufficient + // because that always returns true). + // The onlyOnce flag is used to indicate whether we should only ignore the + // next escape, or keep ignoring until you explicitly HonorEscape. + static void IgnoreEscape(bool onlyOnce); + static void HonorEscape(); + static void HonorShortcuts(bool allow); + static void DisableShortcuts(bool disable); + static void UpdateDropDownState(bool); + + static void RegisterNewAppViewId(); + static void OnWindowClosed(int viewId); + + private: + static void OnCharacterPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, Platform::String ^ oldValue, Platform::String ^ newValue); + + static void OnVirtualKeyPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); + + static void OnVirtualKeyControlChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); + + static void OnVirtualKeyShiftChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); + + static void OnVirtualKeyAltChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); + + static void + OnVirtualKeyControlShiftChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); + + static void OnCharacterReceivedHandler(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::CharacterReceivedEventArgs ^ args); + static void OnKeyDownHandler(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args); + static void OnAcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher ^, Windows::UI::Core::AcceleratorKeyEventArgs ^ args); + static const std::multimap* + KeyboardShortcutManager::GetCurrentKeyDictionary(bool controlKeyPressed, bool shiftKeyPressed, bool altPressed); + + private: + static std::map> s_characterForButtons; + static std::map> s_virtualKey; + static std::map> s_VirtualKeyControlChordsForButtons; + static std::map> s_VirtualKeyShiftChordsForButtons; + static std::map> s_VirtualKeyAltChordsForButtons; + static std::map> s_VirtualKeyControlShiftChordsForButtons; + + static std::map s_IsDropDownOpen; + static std::map s_ignoreNextEscape; + static std::map s_keepIgnoringEscape; + static std::map s_fHonorShortcuts; + static std::map s_fDisableShortcuts; + + static Concurrency::reader_writer_lock s_keyboardShortcutMapLock; + }; + } +} diff --git a/src/Calculator/Common/TitleBarHelper.cpp b/src/Calculator/Common/TitleBarHelper.cpp deleted file mode 100644 index 18e8be94..00000000 --- a/src/Calculator/Common/TitleBarHelper.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "pch.h" -#include "TitleBarHelper.h" -#include "Converters/BooleanToVisibilityConverter.h" -#include "CalcViewModel/ViewState.h" - -using namespace CalculatorApp::Common; -using namespace CalculatorApp::Converters; -using namespace Platform; -using namespace std; -using namespace Windows::ApplicationModel::Core; -using namespace Windows::Foundation; -using namespace Windows::UI::Xaml; - -unique_ptr TitleBarHelper::CreateTitleBarHelperIfNotDocked(FrameworkElement^ customTitleBar) -{ - return (App::GetAppViewState() == ViewState::DockedView) - ? nullptr - : CalculatorApp::Common::TitleBarHelper::CreateTitleBarHelper(customTitleBar); -} - -unique_ptr TitleBarHelper::CreateTitleBarHelper(_In_ FrameworkElement^ customTitleBar) -{ - assert(customTitleBar != nullptr); - if (customTitleBar != nullptr) - { - CoreApplicationViewTitleBar^ coreTitleBar = CoreApplication::GetCurrentView()->TitleBar; - assert(coreTitleBar != nullptr); - if (coreTitleBar != nullptr) - { - return make_unique(coreTitleBar, customTitleBar); - } - } - - return nullptr; -} - -TitleBarHelper::TitleBarHelper(_In_ CoreApplicationViewTitleBar^ coreTitleBar, _In_ FrameworkElement^ customTitleBar) : - m_coreTitleBar(coreTitleBar), - m_customTitleBar(customTitleBar) -{ - RegisterForLayoutChanged(); - RegisterForVisibilityChanged(); - SetCustomTitleBar(); -} - -TitleBarHelper::~TitleBarHelper() -{ - m_coreTitleBar->LayoutMetricsChanged -= m_layoutChangedToken; - m_coreTitleBar->IsVisibleChanged -= m_visibilityChangedToken; -} - -void TitleBarHelper::SetTitleBarHeight(double height) -{ - m_customTitleBar->Height = height; -} - -void TitleBarHelper::SetTitleBarVisibility(bool isVisible) -{ - m_customTitleBar->Visibility = BooleanToVisibilityConverter::Convert(isVisible); -} - -void TitleBarHelper::RegisterForLayoutChanged() -{ - m_layoutChangedToken = - m_coreTitleBar->LayoutMetricsChanged += ref new TypedEventHandler( - [this](CoreApplicationViewTitleBar^ cTitleBar, Object^) - { - // Update title bar control size as needed to account for system size changes - SetTitleBarHeight(cTitleBar->Height); - }); -} - -void TitleBarHelper::RegisterForVisibilityChanged() -{ - m_visibilityChangedToken = - m_coreTitleBar->IsVisibleChanged += ref new TypedEventHandler( - [this](CoreApplicationViewTitleBar^ cTitleBar, Object^) - { - // Update title bar visibility - SetTitleBarVisibility(cTitleBar->IsVisible); - }); -} - -void TitleBarHelper::SetCustomTitleBar() -{ - // Set custom XAML Title Bar - m_coreTitleBar->ExtendViewIntoTitleBar = true; - SetTitleBarHeight(m_coreTitleBar->Height); - SetTitleBarVisibility(m_coreTitleBar->IsVisible); - Window::Current->SetTitleBar(m_customTitleBar); -} diff --git a/src/Calculator/Common/TitleBarHelper.h b/src/Calculator/Common/TitleBarHelper.h deleted file mode 100644 index d90ecd54..00000000 --- a/src/Calculator/Common/TitleBarHelper.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - class TitleBarHelper - { - public: - static std::unique_ptr CreateTitleBarHelperIfNotDocked( - _In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar); - - // Prefer CreateTitleBarHelper over constructing your own instance, - // because Create* will nullcheck the parameters. - static std::unique_ptr CreateTitleBarHelper( - _In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar); - - TitleBarHelper( - _In_ Windows::ApplicationModel::Core::CoreApplicationViewTitleBar^ coreTitleBar, - _In_ Windows::UI::Xaml::FrameworkElement^ customTitleBar); - ~TitleBarHelper(); - - void SetTitleBarHeight(double height); - void SetTitleBarVisibility(bool isVisible); - - private: - void RegisterForLayoutChanged(); - void RegisterForVisibilityChanged(); - void SetCustomTitleBar(); - - Platform::Agile m_coreTitleBar; - Windows::UI::Xaml::FrameworkElement^ m_customTitleBar; - Windows::Foundation::EventRegistrationToken m_layoutChangedToken; - Windows::Foundation::EventRegistrationToken m_visibilityChangedToken; - }; - } -} diff --git a/src/Calculator/Common/ValidatingConverters.h b/src/Calculator/Common/ValidatingConverters.h new file mode 100644 index 00000000..bc168502 --- /dev/null +++ b/src/Calculator/Common/ValidatingConverters.h @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +namespace CalculatorApp +{ + namespace Common + { + public + ref class ValidSelectedItemConverter sealed : public Windows::UI::Xaml::Data::IValueConverter + { + public: + ValidSelectedItemConverter() + { + } + + private: + virtual Platform::Object + ^ Convert( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert + { + // Pass through as we don't want to change the value from the source + return value; + } + + virtual Platform::Object + ^ ConvertBack( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + { + if (value) + { + return value; + } + // Stop the binding if the object is nullptr + return Windows::UI::Xaml::DependencyProperty::UnsetValue; + } + }; + + public + ref class ValidSelectedIndexConverter sealed : public Windows::UI::Xaml::Data::IValueConverter + { + public: + ValidSelectedIndexConverter() + { + } + + private: + virtual Platform::Object + ^ Convert( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert + { + // Pass through as we don't want to change the value from the source + return value; + } + + virtual Platform::Object + ^ ConvertBack( + Platform::Object ^ value, + Windows::UI::Xaml::Interop::TypeName /*targetType*/, + Platform::Object ^ /*parameter*/, + Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack + { + // The value to be valid has to be a boxed int32 value + // extract that value and ensure it is valid, ie >= 0 + if (value) + { + auto box = dynamic_cast(value); + if (box && box->Type == Windows::Foundation::PropertyType::Int32) + { + int index = box->GetInt32(); + if (index >= 0) + { + return value; + } + } + } + // The value is not valid therefore stop the binding right here + return Windows::UI::Xaml::DependencyProperty::UnsetValue; + } + }; + } +} diff --git a/src/Calculator/Common/ViewState.cpp b/src/Calculator/Common/ViewState.cpp new file mode 100644 index 00000000..63a5d30e --- /dev/null +++ b/src/Calculator/Common/ViewState.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "ViewState.h" + +namespace CalculatorApp +{ + namespace ViewState + { + Platform::StringReference Snap(L"Snap"); + Platform::StringReference DockedView(L"DockedView"); + + bool IsValidViewState(Platform::String ^ viewState) + { + return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView); + } + } +} diff --git a/src/Calculator/Common/ViewState.h b/src/Calculator/Common/ViewState.h new file mode 100644 index 00000000..0a103d09 --- /dev/null +++ b/src/Calculator/Common/ViewState.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +namespace CalculatorApp +{ + namespace ViewState + { + extern Platform::StringReference Snap; + extern Platform::StringReference DockedView; + + bool IsValidViewState(Platform::String ^ viewState); + } +} diff --git a/src/Calculator/Controls/AppBar.h b/src/Calculator/Controls/AppBar.h deleted file mode 100644 index b6ac3740..00000000 --- a/src/Calculator/Controls/AppBar.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include "Common/KeyboardShortcutManager.h" - -namespace CalculatorApp -{ - namespace Controls - { - public ref class AppBar sealed : public Windows::UI::Xaml::Controls::AppBar - { - public: - AppBar() - {} - - protected: - virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) override - { - Windows::UI::Xaml::Controls::AppBar::OnKeyDown(e); - if (e->Key == Windows::System::VirtualKey::Escape) - { - Common::KeyboardShortcutManager::IgnoreEscape(true); - } - } - }; - } -} diff --git a/src/Calculator/Controls/CalculationResult.cpp b/src/Calculator/Controls/CalculationResult.cpp index 5ee59944..c2542f10 100644 --- a/src/Calculator/Controls/CalculationResult.cpp +++ b/src/Calculator/Controls/CalculationResult.cpp @@ -26,15 +26,12 @@ using namespace Windows::UI::Xaml::Automation::Peers; using namespace std; DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, IsActive); -DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, AccentColor); DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, MinFontSize); +DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, MaxFontSize); DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, DisplayMargin); -DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, MaxExpressionHistoryCharacters); -DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, ExpressionVisibility); DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, DisplayValue); DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, IsInError); DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, IsOperatorCommand); -DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, DisplayStringExpression); #define SCALEFACTOR 0.357143 #define SMALLHEIGHTSCALEFACTOR 0 @@ -45,72 +42,100 @@ DEPENDENCY_PROPERTY_INITIALIZATION(CalculationResult, DisplayStringExpression); #define WIDTHTOFONTOFFSET 3 #define WIDTHCUTOFF 50 #define FONTTOLERANCE 0.001 +#define SCROLL_RATIO 0.7 -StringReference CalculationResult::s_FocusedState(L"Focused"); -StringReference CalculationResult::s_UnfocusedState(L"Unfocused"); +// We need a safety margin to guarantee we correctly always show/hide ScrollLeft and ScrollRight buttons when necessary. +// In rare cases, ScrollViewer::HorizontalOffset is a little low by a few (sub)pixels when users scroll to one of the extremity +// and no events are launched when they scroll again in the same direction +#define SCROLL_BUTTONS_APPROXIMATION_RANGE 4 -CalculationResult::CalculationResult(): - m_startingFontSize(0.0), - m_isScalingText(false), - m_haveCalculatedMax(false) +CalculationResult::CalculationResult() + : m_isScalingText(false) + , m_haveCalculatedMax(false) { } -Platform::String^ CalculationResult::GetRawDisplayValue() +Platform::String ^ CalculationResult::GetRawDisplayValue() { - std::wstring rawValue; - - LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue->Data(), DisplayValue->Length(), &rawValue); - - return ref new Platform::String(rawValue.c_str()); + return LocalizationSettings::GetInstance().RemoveGroupSeparators(DisplayValue); } void CalculationResult::OnApplyTemplate() { - assert((m_scrollLeft == nullptr && m_scrollRight == nullptr) || (m_scrollLeft != nullptr && m_scrollRight != nullptr)); if (m_textContainer) { - m_textContainer->LayoutUpdated -= m_textContainerLayoutChangedToken; + if (m_textContainerLayoutChangedToken.Value != 0) + { + m_textContainer->LayoutUpdated -= m_textContainerLayoutChangedToken; + m_textContainerLayoutChangedToken.Value = 0; + } + if (m_textContainerSizeChangedToken.Value != 0) + { + m_textContainer->SizeChanged -= m_textContainerSizeChangedToken; + m_textContainerSizeChangedToken.Value = 0; + } + if (m_textContainerViewChangedToken.Value != 0) + { + m_textContainer->ViewChanged -= m_textContainerViewChangedToken; + m_textContainerViewChangedToken.Value = 0; + } } - m_textContainer = dynamic_cast(GetTemplateChild("textContainer")); + + if (m_textBlock != nullptr && m_textBlockSizeChangedToken.Value != 0) + { + m_textBlock->SizeChanged -= m_textBlockSizeChangedToken; + m_textBlockSizeChangedToken.Value = 0; + } + + if (m_scrollLeft != nullptr && m_scrollLeftClickToken.Value != 0) + { + m_scrollLeft->Click -= m_scrollLeftClickToken; + m_scrollLeftClickToken.Value = 0; + } + + if (m_scrollRight != nullptr && m_scrollRightClickToken.Value != 0) + { + m_scrollRight->Click -= m_scrollRightClickToken; + m_scrollRightClickToken.Value = 0; + } + + m_textContainer = dynamic_cast(GetTemplateChild("TextContainer")); if (m_textContainer) { - m_textContainer->SizeChanged += ref new SizeChangedEventHandler(this, &CalculationResult::TextContainerSizeChanged); // We want to know when the size of the container changes so // we can rescale the textbox - m_textContainerLayoutChangedToken = m_textContainer->LayoutUpdated += ref new EventHandler(this, &CalculationResult::OnTextContainerLayoutUpdated); + m_textContainerSizeChangedToken = m_textContainer->SizeChanged += ref new SizeChangedEventHandler(this, &CalculationResult::OnTextContainerSizeChanged); - m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth,nullptr,nullptr); - m_scrollLeft = dynamic_cast(GetTemplateChild("scrollLeft")); - m_scrollRight = dynamic_cast(GetTemplateChild("scrollRight")); - auto borderContainer = dynamic_cast(GetTemplateChild("border")); - if (m_scrollLeft && m_scrollRight) + m_textContainerViewChangedToken = m_textContainer->ViewChanged += + ref new Windows::Foundation::EventHandler( + this, &CalculatorApp::Controls::CalculationResult::OnTextContainerOnViewChanged); + + m_textContainerLayoutChangedToken = m_textContainer->LayoutUpdated += + ref new EventHandler(this, &CalculationResult::OnTextContainerLayoutUpdated); + + m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth, nullptr, nullptr); + m_scrollLeft = dynamic_cast(GetTemplateChild("ScrollLeft")); + if (m_scrollLeft) { - m_scrollLeft->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); - m_scrollRight->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollClick); - borderContainer->PointerEntered += ref new PointerEventHandler(this, &CalculationResult::OnPointerEntered); - borderContainer->PointerExited += ref new PointerEventHandler(this, &CalculationResult::OnPointerExited); + m_scrollLeftClickToken = m_scrollLeft->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollLeftClick); } - m_textBlock = dynamic_cast(m_textContainer->FindName("normalOutput")); + m_scrollRight = dynamic_cast(GetTemplateChild("ScrollRight")); + if (m_scrollRight) + { + m_scrollRightClickToken = m_scrollRight->Click += ref new RoutedEventHandler(this, &CalculationResult::OnScrollRightClick); + } + m_textBlock = dynamic_cast(GetTemplateChild("NormalOutput")); if (m_textBlock) { m_textBlock->Visibility = ::Visibility::Visible; - m_startingFontSize = m_textBlock->FontSize; + m_textBlockSizeChangedToken = m_textBlock->SizeChanged += ref new SizeChangedEventHandler(this, &CalculationResult::OnTextBlockSizeChanged); } } - UpdateAllState(); - VisualStateManager::GoToState(this, s_UnfocusedState, false); + UpdateVisualState(); + UpdateTextState(); } -void CalculationResult::OnPointerPressed(PointerRoutedEventArgs^ e) -{ - if (m_scrollLeft && m_scrollRight && e->Pointer->PointerDeviceType == PointerDeviceType::Touch) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); - } -} - -void CalculationResult::OnTextContainerLayoutUpdated(Object^ /*sender*/, Object^ /*e*/) +void CalculationResult::OnTextContainerLayoutUpdated(Object ^ /*sender*/, Object ^ /*e*/) { if (m_isScalingText) { @@ -118,7 +143,7 @@ void CalculationResult::OnTextContainerLayoutUpdated(Object^ /*sender*/, Object^ } } -void CalculationResult::TextContainerSizeChanged(Object^ /*sender*/, SizeChangedEventArgs^ /*e*/) +void CalculationResult::OnTextContainerSizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ /*e*/) { UpdateTextState(); } @@ -128,17 +153,17 @@ void CalculationResult::OnIsActivePropertyChanged(bool /*oldValue*/, bool /*newV UpdateVisualState(); } -void CalculationResult::OnAccentColorPropertyChanged(Brush^ /*oldValue*/, Brush^ /*newValue*/) +void CalculationResult::OnDisplayValuePropertyChanged(String ^ /*oldValue*/, String ^ /*newValue*/) { - // Force the "Active" transition to happen again - if (IsActive) - { - VisualStateManager::GoToState(this, "Normal", true); - VisualStateManager::GoToState(this, "Active", true); - } + UpdateTextState(); } -void CalculationResult::OnDisplayValuePropertyChanged(String^ /*oldValue*/, String^ /*newValue*/) +void CalculationResult::OnMinFontSizePropertyChanged(double /*oldValue*/, double /*newValue*/) +{ + UpdateTextState(); +} + +void CalculationResult::OnMaxFontSizePropertyChanged(double /*oldValue*/, double /*newValue*/) { UpdateTextState(); } @@ -189,8 +214,8 @@ void CalculationResult::UpdateTextState() } auto containerSize = m_textContainer->ActualWidth; - String^ oldText = m_textBlock->Text; - String^ newText = Utils::LRO + DisplayValue + Utils::PDF; + String ^ oldText = m_textBlock->Text; + String ^ newText = DisplayValue; // Initiate the scaling operation // UpdateLayout will keep calling us until we make it through the below 2 if-statements @@ -210,9 +235,9 @@ void CalculationResult::UpdateTextState() if (widthDiff > WIDTHCUTOFF) { - fontSizeChange = min(max(floor(WIDTHTOFONTSCALAR * widthDiff) - WIDTHTOFONTOFFSET, INCREMENTOFFSET), MAXFONTINCREMENT); + fontSizeChange = min(max(floor(WIDTHTOFONTSCALAR * widthDiff) - WIDTHTOFONTOFFSET, INCREMENTOFFSET), MAXFONTINCREMENT); } - if (m_textBlock->ActualWidth < containerSize && abs(m_textBlock->FontSize - m_startingFontSize) > FONTTOLERANCE && !m_haveCalculatedMax) + if (m_textBlock->ActualWidth < containerSize && abs(m_textBlock->FontSize - MaxFontSize) > FONTTOLERANCE && !m_haveCalculatedMax) { ModifyFontAndMargin(m_textBlock, fontSizeChange); m_textBlock->InvalidateArrange(); @@ -228,190 +253,147 @@ void CalculationResult::UpdateTextState() m_textBlock->InvalidateArrange(); return; } - assert(m_textBlock->FontSize >= MinFontSize && m_textBlock->FontSize <= m_startingFontSize); + assert(m_textBlock->FontSize >= MinFontSize && m_textBlock->FontSize <= MaxFontSize); m_isScalingText = false; if (IsOperatorCommand) { - m_textContainer->ChangeView(0.0,nullptr,nullptr); + m_textContainer->ChangeView(0.0, nullptr, nullptr); } else { - m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth,nullptr,nullptr); + m_textContainer->ChangeView(m_textContainer->ExtentWidth - m_textContainer->ViewportWidth, nullptr, nullptr); } - - if (m_scrollLeft && m_scrollRight) - { - if (m_textBlock->ActualWidth < containerSize) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); - } - else - { - if (IsOperatorCommand) - { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Visible); - } - else - { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Collapsed); - } - } - } - m_textBlock->InvalidateArrange(); } } void CalculationResult::ScrollLeft() { + if (m_textContainer == nullptr) + { + return; + } if (m_textContainer->HorizontalOffset > 0) { - double offset = m_textContainer->HorizontalOffset - (scrollRatio * m_textContainer->ViewportWidth); + double offset = m_textContainer->HorizontalOffset - (SCROLL_RATIO * m_textContainer->ViewportWidth); m_textContainer->ChangeView(offset, nullptr, nullptr); - m_textContainer->UpdateLayout(); - UpdateScrollButtons(); } } void CalculationResult::ScrollRight() { + if (m_textContainer == nullptr) + { + return; + } + if (m_textContainer->HorizontalOffset < m_textContainer->ExtentWidth - m_textContainer->ViewportWidth) { - double offset = m_textContainer->HorizontalOffset + (scrollRatio * m_textContainer->ViewportWidth); + double offset = m_textContainer->HorizontalOffset + (SCROLL_RATIO * m_textContainer->ViewportWidth); m_textContainer->ChangeView(offset, nullptr, nullptr); - m_textContainer->UpdateLayout(); - UpdateScrollButtons(); } } -void CalculationResult::OnKeyDown(KeyRoutedEventArgs^ e) +void CalculationResult::OnKeyDown(KeyRoutedEventArgs ^ e) { - if (m_scrollLeft != nullptr && m_scrollRight != nullptr) - { - auto key = e->Key; - if (key == Windows::System::VirtualKey::Left) - { - this->ScrollLeft(); - } - else if (key == Windows::System::VirtualKey::Right) - { - this->ScrollRight(); - } - } -} - -void CalculationResult::OnScrollClick(Object^ sender, RoutedEventArgs^ /*e*/) -{ - auto clicked = dynamic_cast(sender); - if (clicked == m_scrollLeft) + switch (e->Key) { + case Windows::System::VirtualKey::Left: this->ScrollLeft(); - } - else - { + break; + case Windows::System::VirtualKey::Right: this->ScrollRight(); + break; } } -void CalculationResult::OnPointerEntered(Platform::Object^ sender, PointerRoutedEventArgs^ e) +void CalculationResult::OnScrollLeftClick(Object ^ sender, RoutedEventArgs ^ /*e*/) { - if (e->Pointer->PointerDeviceType == PointerDeviceType::Mouse && m_textBlock->ActualWidth >= m_textContainer->ActualWidth) - { - UpdateScrollButtons(); - } + ScrollLeft(); } -void CalculationResult::ShowHideScrollButtons(::Visibility vLeft, ::Visibility vRight) +void CalculationResult::OnScrollRightClick(Object ^ sender, RoutedEventArgs ^ /*e*/) { - m_scrollLeft->Visibility = vLeft; - m_scrollRight->Visibility = vRight; + ScrollRight(); } void CalculationResult::UpdateScrollButtons() { - // When the width is smaller than the container, don't show any - if (m_textBlock->ActualWidth < m_textContainer->ActualWidth) + if (m_textContainer == nullptr) { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Collapsed); + return; } - // We have more number on both side. Show both arrows - else if (m_textContainer->HorizontalOffset > 0 && m_textContainer->HorizontalOffset < (m_textContainer->ExtentWidth - m_textContainer->ViewportWidth)) + + bool shouldTryFocusScrollRight = false; + if (m_scrollLeft != nullptr) { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Visible); + auto scrollLeftVisibility = m_textContainer->HorizontalOffset > SCROLL_BUTTONS_APPROXIMATION_RANGE ? ::Visibility::Visible : ::Visibility::Collapsed; + + if (scrollLeftVisibility == ::Visibility::Collapsed) + { + shouldTryFocusScrollRight = m_scrollLeft->Equals(FocusManager::GetFocusedElement()); + } + + m_scrollLeft->Visibility = scrollLeftVisibility; } - // Width is larger than the container and left most part of the number is shown. Should be able to scroll left. - else if (m_textContainer->HorizontalOffset == 0) + + if (m_scrollRight != nullptr) { - ShowHideScrollButtons(::Visibility::Collapsed, ::Visibility::Visible); - } - else // Width is larger than the container and right most part of the number is shown. Should be able to scroll left. - { - ShowHideScrollButtons(::Visibility::Visible, ::Visibility::Collapsed); + auto scrollRightVisibility = + m_textContainer->HorizontalOffset + m_textContainer->ViewportWidth + SCROLL_BUTTONS_APPROXIMATION_RANGE < m_textContainer->ExtentWidth + ? ::Visibility::Visible + : ::Visibility::Collapsed; + + if (scrollRightVisibility == ::Visibility::Collapsed && m_scrollLeft != nullptr && m_scrollLeft->Visibility == ::Visibility::Visible + && m_scrollRight->Equals(FocusManager::GetFocusedElement())) + { + // ScrollRight had the focus and will be collapsed, ScrollLeft should get the focus + m_scrollLeft->Focus(::FocusState::Programmatic); + } + m_scrollRight->Visibility = scrollRightVisibility; + + if (shouldTryFocusScrollRight && scrollRightVisibility == ::Visibility::Visible) + { + m_scrollRight->Focus(::FocusState::Programmatic); + } } } -void CalculationResult::OnPointerExited(Platform::Object^ sender, PointerRoutedEventArgs^ e) -{ - if (e->Pointer->PointerDeviceType == PointerDeviceType::Mouse) - { - UpdateScrollButtons(); - } -} - -void CalculationResult::ModifyFontAndMargin(TextBlock^ textBox, double fontChange) +void CalculationResult::ModifyFontAndMargin(TextBlock ^ textBox, double fontChange) { double cur = textBox->FontSize; double newFontSize = 0.0; - Thickness t = textBox->Margin; double scaleFactor = SCALEFACTOR; if (m_textContainer->ActualHeight <= HEIGHTCUTOFF) { scaleFactor = SMALLHEIGHTSCALEFACTOR; } - if (fontChange < 0) - { - newFontSize = max(cur + fontChange, MinFontSize); - t.Bottom += scaleFactor * abs(cur - newFontSize); - } - else - { - newFontSize = min(cur + fontChange, m_startingFontSize); - t.Bottom -= scaleFactor * abs(cur - newFontSize); - } + + newFontSize = min(max(cur + fontChange, MinFontSize), MaxFontSize); + m_textContainer->Padding = Thickness(0, 0, 0, scaleFactor * abs(cur - newFontSize)); textBox->FontSize = newFontSize; - textBox->Margin = t; } -void CalculationResult::UpdateAllState() -{ - UpdateVisualState(); - UpdateTextState(); -} - -void CalculationResult::OnTapped(TappedRoutedEventArgs^ e) +void CalculationResult::OnTapped(TappedRoutedEventArgs ^ e) { this->Focus(::FocusState::Programmatic); RaiseSelectedEvent(); } -void CalculationResult::OnRightTapped(RightTappedRoutedEventArgs^ e) +void CalculationResult::OnRightTapped(RightTappedRoutedEventArgs ^ e) { - this->Focus(::FocusState::Programmatic); -} + auto requestedElement = e->OriginalSource; -void CalculationResult::OnGotFocus(RoutedEventArgs^ e) -{ - if (this->FocusState == ::FocusState::Keyboard) + if (requestedElement->Equals(dynamic_cast(m_textBlock))) { - VisualStateManager::GoToState(this, s_FocusedState, true); + m_textBlock->Focus(::FocusState::Programmatic); + } + else + { + this->Focus(::FocusState::Programmatic); } } -void CalculationResult::OnLostFocus(RoutedEventArgs^ e) -{ - VisualStateManager::GoToState(this, s_UnfocusedState, true); -} - -AutomationPeer^ CalculationResult::OnCreateAutomationPeer() +AutomationPeer ^ CalculationResult::OnCreateAutomationPeer() { return ref new CalculationResultAutomationPeer(this); } @@ -425,3 +407,13 @@ void CalculationResult::RaiseSelectedEvent() { Selected(this); } + +void CalculationResult::OnTextContainerOnViewChanged(Object ^ /*sender*/, ScrollViewerViewChangedEventArgs ^ e) +{ + UpdateScrollButtons(); +} + +void CalculationResult::OnTextBlockSizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ /*e*/) +{ + UpdateScrollButtons(); +} diff --git a/src/Calculator/Controls/CalculationResult.h b/src/Calculator/Controls/CalculationResult.h index 3e059a2e..2db9fdbe 100644 --- a/src/Calculator/Controls/CalculationResult.h +++ b/src/Calculator/Controls/CalculationResult.h @@ -9,76 +9,73 @@ namespace CalculatorApp { namespace Controls { - public delegate void SelectedEventHandler(Platform::Object^ sender); + public + delegate void SelectedEventHandler(Platform::Object ^ sender); - public ref class CalculationResult sealed: public Windows::UI::Xaml::Controls::Control + public + ref class CalculationResult sealed : public Windows::UI::Xaml::Controls::Control { public: CalculationResult(); DEPENDENCY_PROPERTY_OWNER(CalculationResult); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::Visibility, ExpressionVisibility); - DEPENDENCY_PROPERTY(double, MinFontSize); + DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(double, MinFontSize, 0.0); + DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(double, MaxFontSize, 30.0); DEPENDENCY_PROPERTY(Windows::UI::Xaml::Thickness, DisplayMargin); - DEPENDENCY_PROPERTY(int, MaxExpressionHistoryCharacters); DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, IsActive); - DEPENDENCY_PROPERTY_WITH_CALLBACK(Windows::UI::Xaml::Media::Brush^, AccentColor); - DEPENDENCY_PROPERTY_WITH_CALLBACK(Platform::String^, DisplayValue); - DEPENDENCY_PROPERTY(Platform::String^, DisplayStringExpression); + DEPENDENCY_PROPERTY_WITH_CALLBACK(Platform::String ^, DisplayValue); DEPENDENCY_PROPERTY_WITH_CALLBACK(bool, IsInError); DEPENDENCY_PROPERTY_WITH_DEFAULT(bool, IsOperatorCommand, false); - event SelectedEventHandler^ Selected; + event SelectedEventHandler ^ Selected; void ProgrammaticSelect(); - internal: - void UpdateTextState(); - Platform::String^ GetRawDisplayValue(); + internal : void UpdateTextState(); + Platform::String ^ GetRawDisplayValue(); protected: - virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) override; + virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) override; virtual void OnApplyTemplate() override; - virtual void OnTapped(Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e) override; - virtual void OnPointerPressed(Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e) override; - virtual void OnRightTapped(Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e) override; - virtual void OnGotFocus(Windows::UI::Xaml::RoutedEventArgs^ e) override; - virtual void OnLostFocus(Windows::UI::Xaml::RoutedEventArgs^ e) override; - - virtual Windows::UI::Xaml::Automation::Peers::AutomationPeer^ OnCreateAutomationPeer() override; + virtual void OnTapped(Windows::UI::Xaml::Input::TappedRoutedEventArgs ^ e) override; + virtual void OnRightTapped(Windows::UI::Xaml::Input::RightTappedRoutedEventArgs ^ e) override; + + virtual Windows::UI::Xaml::Automation::Peers::AutomationPeer ^ OnCreateAutomationPeer() override; private: void OnIsActivePropertyChanged(bool oldValue, bool newValue); - void OnAccentColorPropertyChanged(Windows::UI::Xaml::Media::Brush^ oldValue, Windows::UI::Xaml::Media::Brush^ newValue); - void OnDisplayValuePropertyChanged(Platform::String^ oldValue, Platform::String^ newValue); + void OnDisplayValuePropertyChanged(Platform::String ^ oldValue, Platform::String ^ newValue); void OnIsInErrorPropertyChanged(bool oldValue, bool newValue); - void TextContainerSizeChanged(Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); - void OnTextContainerLayoutUpdated(Object^ sender, Object^ e); + void OnMinFontSizePropertyChanged(double oldValue, double newValue); + void OnMaxFontSizePropertyChanged(double oldValue, double newValue); + void OnTextContainerSizeChanged(Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); + void OnTextBlockSizeChanged(Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); + void OnTextContainerLayoutUpdated(Object ^ sender, Object ^ e); + void OnTextContainerOnViewChanged(Object ^ sender, Windows::UI::Xaml::Controls::ScrollViewerViewChangedEventArgs ^ e); void UpdateVisualState(); void UpdateAllState(); - void OnScrollClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnPointerEntered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e); - void OnPointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e); - void ModifyFontAndMargin(Windows::UI::Xaml::Controls::TextBlock^ textBlock, double fontChange); - void ShowHideScrollButtons(Windows::UI::Xaml::Visibility vLeft, Windows::UI::Xaml::Visibility vRight); + void OnScrollLeftClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); + void OnScrollRightClick(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); + void OnPointerEntered(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e); + void OnPointerExited(Platform::Object ^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs ^ e); + void ModifyFontAndMargin(Windows::UI::Xaml::Controls::TextBlock ^ textBlock, double fontChange); void UpdateScrollButtons(); void ScrollLeft(); void ScrollRight(); void RaiseSelectedEvent(); - // Visual states for focused - static Platform::StringReference s_FocusedState; - static Platform::StringReference s_UnfocusedState; - - Windows::UI::Xaml::Controls::ScrollViewer^ m_textContainer; - Windows::UI::Xaml::Controls::TextBlock^ m_textBlock; - Windows::UI::Xaml::Controls::HyperlinkButton^ m_scrollLeft; - Windows::UI::Xaml::Controls::HyperlinkButton^ m_scrollRight; - double m_startingFontSize; - double scrollRatio = 0.7; + Windows::UI::Xaml::Controls::ScrollViewer ^ m_textContainer; + Windows::UI::Xaml::Controls::TextBlock ^ m_textBlock; + Windows::UI::Xaml::Controls::HyperlinkButton ^ m_scrollLeft; + Windows::UI::Xaml::Controls::HyperlinkButton ^ m_scrollRight; bool m_isScalingText; bool m_haveCalculatedMax; Windows::Foundation::EventRegistrationToken m_textContainerLayoutChangedToken; + Windows::Foundation::EventRegistrationToken m_textContainerViewChangedToken; + Windows::Foundation::EventRegistrationToken m_textContainerSizeChangedToken; + Windows::Foundation::EventRegistrationToken m_scrollRightClickToken; + Windows::Foundation::EventRegistrationToken m_scrollLeftClickToken; + Windows::Foundation::EventRegistrationToken m_textBlockSizeChangedToken; }; } } diff --git a/src/Calculator/Controls/CalculationResultAutomationPeer.cpp b/src/Calculator/Controls/CalculationResultAutomationPeer.cpp index 2b77a172..6f381e28 100644 --- a/src/Calculator/Controls/CalculationResultAutomationPeer.cpp +++ b/src/Calculator/Controls/CalculationResultAutomationPeer.cpp @@ -9,16 +9,17 @@ using namespace Windows::UI::Xaml::Automation::Peers; namespace CalculatorApp::Controls { - CalculationResultAutomationPeer::CalculationResultAutomationPeer(FrameworkElement^ owner) : - FrameworkElementAutomationPeer(owner) - { } + CalculationResultAutomationPeer::CalculationResultAutomationPeer(FrameworkElement ^ owner) + : FrameworkElementAutomationPeer(owner) + { + } AutomationControlType CalculationResultAutomationPeer::GetAutomationControlTypeCore() { return AutomationControlType::Text; } - Platform::Object^ CalculationResultAutomationPeer::GetPatternCore(PatternInterface pattern) + Platform::Object ^ CalculationResultAutomationPeer::GetPatternCore(PatternInterface pattern) { if (pattern == PatternInterface::Invoke) { @@ -30,7 +31,7 @@ namespace CalculatorApp::Controls void CalculationResultAutomationPeer::Invoke() { - auto owner = static_cast(this->Owner); + auto owner = static_cast(this->Owner); owner->ProgrammaticSelect(); } } diff --git a/src/Calculator/Controls/CalculationResultAutomationPeer.h b/src/Calculator/Controls/CalculationResultAutomationPeer.h index afdaa8ba..ec75320c 100644 --- a/src/Calculator/Controls/CalculationResultAutomationPeer.h +++ b/src/Calculator/Controls/CalculationResultAutomationPeer.h @@ -9,14 +9,15 @@ namespace CalculatorApp { namespace Controls { - public ref class CalculationResultAutomationPeer sealed : Windows::UI::Xaml::Automation::Peers::FrameworkElementAutomationPeer, - Windows::UI::Xaml::Automation::Provider::IInvokeProvider + public + ref class CalculationResultAutomationPeer sealed : Windows::UI::Xaml::Automation::Peers::FrameworkElementAutomationPeer, + Windows::UI::Xaml::Automation::Provider::IInvokeProvider { public: - CalculationResultAutomationPeer(Windows::UI::Xaml::FrameworkElement^ owner); + CalculationResultAutomationPeer(Windows::UI::Xaml::FrameworkElement ^ owner); virtual Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() override; - virtual Platform::Object^ GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface pattern) override; + virtual Platform::Object ^ GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface pattern) override; virtual void Invoke(); }; } diff --git a/src/Calculator/Controls/CalculatorButton.cpp b/src/Calculator/Controls/CalculatorButton.cpp index abd00b92..2377a9a7 100644 --- a/src/Calculator/Controls/CalculatorButton.cpp +++ b/src/Calculator/Controls/CalculatorButton.cpp @@ -26,12 +26,12 @@ CalculatorButton::CalculatorButton() { // Set the default bindings for this button, these can be overwritten by Xaml if needed // These are a replacement for binding in styles - Binding^ commandBinding = ref new Binding(); + Binding ^ commandBinding = ref new Binding(); commandBinding->Path = ref new PropertyPath("ButtonPressed"); this->SetBinding(Button::CommandProperty, commandBinding); } -void CalculatorButton::OnKeyDown(KeyRoutedEventArgs^ e) +void CalculatorButton::OnKeyDown(KeyRoutedEventArgs ^ e) { // Ignore the Enter key if (e->Key == VirtualKey::Enter) @@ -42,7 +42,7 @@ void CalculatorButton::OnKeyDown(KeyRoutedEventArgs^ e) Button::OnKeyDown(e); } -void CalculatorButton::OnKeyUp(KeyRoutedEventArgs^ e) +void CalculatorButton::OnKeyUp(KeyRoutedEventArgs ^ e) { // Ignore the Enter key if (e->Key == VirtualKey::Enter) @@ -53,16 +53,12 @@ void CalculatorButton::OnKeyUp(KeyRoutedEventArgs^ e) Button::OnKeyUp(e); } -void CalculatorButton::OnButtonIdPropertyChanged( - NumbersAndOperatorsEnum /*oldValue*/, - NumbersAndOperatorsEnum newValue) +void CalculatorButton::OnButtonIdPropertyChanged(NumbersAndOperatorsEnum /*oldValue*/, NumbersAndOperatorsEnum newValue) { this->CommandParameter = ref new CalculatorButtonPressedEventArgs(AuditoryFeedback, newValue); } -void CalculatorButton::OnAuditoryFeedbackPropertyChanged( - String^ /*oldValue*/, - String^ newValue) +void CalculatorButton::OnAuditoryFeedbackPropertyChanged(String ^ /*oldValue*/, String ^ newValue) { this->CommandParameter = ref new CalculatorButtonPressedEventArgs(newValue, ButtonId); } diff --git a/src/Calculator/Controls/CalculatorButton.h b/src/Calculator/Controls/CalculatorButton.h index daad7382..8a780700 100644 --- a/src/Calculator/Controls/CalculatorButton.h +++ b/src/Calculator/Controls/CalculatorButton.h @@ -10,29 +10,27 @@ namespace CalculatorApp { namespace Controls { - public ref class CalculatorButton sealed: Windows::UI::Xaml::Controls::Button + public + ref class CalculatorButton sealed : Windows::UI::Xaml::Controls::Button { public: - CalculatorButton(); DEPENDENCY_PROPERTY_OWNER(CalculatorButton); DEPENDENCY_PROPERTY_WITH_CALLBACK(NumbersAndOperatorsEnum, ButtonId); - DEPENDENCY_PROPERTY_WITH_CALLBACK(Platform::String^, AuditoryFeedback); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush^, HoverBackground); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush^, HoverForeground); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush^, PressBackground); - DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush^, PressForeground); + DEPENDENCY_PROPERTY_WITH_CALLBACK(Platform::String ^, AuditoryFeedback); + DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush ^, HoverBackground); + DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush ^, HoverForeground); + DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush ^, PressBackground); + DEPENDENCY_PROPERTY(Windows::UI::Xaml::Media::Brush ^, PressForeground); protected: - - virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) override; - virtual void OnKeyUp(Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) override; + virtual void OnKeyDown(Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) override; + virtual void OnKeyUp(Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) override; private: - void OnButtonIdPropertyChanged(NumbersAndOperatorsEnum oldValue, NumbersAndOperatorsEnum newValue); - void OnAuditoryFeedbackPropertyChanged(Platform::String^ oldValue, Platform::String^ newValue); + void OnAuditoryFeedbackPropertyChanged(Platform::String ^ oldValue, Platform::String ^ newValue); }; } } diff --git a/src/Calculator/Controls/EquationTextBox.cpp b/src/Calculator/Controls/EquationTextBox.cpp new file mode 100644 index 00000000..a0d65298 --- /dev/null +++ b/src/Calculator/Controls/EquationTextBox.cpp @@ -0,0 +1,507 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "CalcViewModel/Common/AppResourceProvider.h" +#include "CalcViewModel/Common/LocalizationStringUtil.h" +#include "EquationTextBox.h" + +using namespace std; +using namespace Platform; +using namespace CalculatorApp; +using namespace CalculatorApp::Common; +using namespace CalculatorApp::Controls; +using namespace Windows::System; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::UI::Text; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Automation; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Media; + +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationColor); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonForegroundColor); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ColorChooserFlyout); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonContentIndex); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, HasError); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsAddEquationMode); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, MathEquation); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ErrorText); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsEquationLineDisabled); + +EquationTextBox::EquationTextBox() +{ +} + +void EquationTextBox::OnApplyTemplate() +{ + m_equationButton = dynamic_cast(GetTemplateChild("EquationButton")); + m_richEditBox = dynamic_cast(GetTemplateChild("MathRichEditBox")); + m_deleteButton = dynamic_cast