diff --git a/src/CalcManager/CCalcManager.cpp b/src/CalcManager/CCalcManager.cpp index 5080bbc2..6db0f533 100644 --- a/src/CalcManager/CCalcManager.cpp +++ b/src/CalcManager/CCalcManager.cpp @@ -1,8 +1,14 @@ +#define __STDC_WANT_LIB_EXT1__ 1 #include "pch.h" #include "CCalcManager.h" #include "CalculatorManager.h" #include "CalculatorResource.h" #include +#include +#include +#include +#include +#include using namespace CalculationManager; @@ -71,7 +77,12 @@ public: { auto str = convert.to_bytes(memorizedNumbers[i]); auto pData = new char[str.size() + 1]; - strncpy_s(pData, str.size(), str.data(), str.size()); + +#if !defined(__EMSCRIPTEN__) + strcpy_s(pData, str.size(), str.data()); +#else + strcpy(pData, str.data()); +#endif numbers[i] = pData; } @@ -114,10 +125,15 @@ public: void* CalculatorManager_Create(CalculatorManager_CreateParams* pParams) { + printf("-> NativeCalcManager:CalculatorManager_Create(%p)\n", pParams); + auto calcDisplay = new CalcDisplay(*pParams); auto resProvider = new ResourceProvider(*pParams); + printf("NativeCalcManager:CalculatorManager_Create: Got providers\n"); + auto cm = new CalculatorManager(calcDisplay, resProvider); + printf("<- NativeCalcManager:CalculatorManager_Create(%p)\n", pParams); return cm; } diff --git a/src/CalcManager/CCalcManager.h b/src/CalcManager/CCalcManager.h index 02db9d34..37e10406 100644 --- a/src/CalcManager/CCalcManager.h +++ b/src/CalcManager/CCalcManager.h @@ -51,8 +51,14 @@ struct CalculatorManager_CreateParams { GetCEngineStringFunc GetCEngineString; }; +#if defined(__EMSCRIPTEN__) +#define DLL_EXPORT +#else +#define DLL_EXPORT __declspec(dllexport) +#endif + extern "C" { - __declspec(dllexport) void* CalculatorManager_Create(CalculatorManager_CreateParams* params); - __declspec(dllexport) void CalculatorManager_SendCommand(void* manager, int command); + DLL_EXPORT void* CalculatorManager_Create(CalculatorManager_CreateParams* params); + DLL_EXPORT void CalculatorManager_SendCommand(void* manager, int command); } diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index c22904ad..d5bf99b1 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -314,7 +314,6 @@ - @@ -355,7 +354,6 @@ Create Create - diff --git a/src/CalcManager/CalcManager.vcxproj.filters b/src/CalcManager/CalcManager.vcxproj.filters index 40d45f51..d32a02a8 100644 --- a/src/CalcManager/CalcManager.vcxproj.filters +++ b/src/CalcManager/CalcManager.vcxproj.filters @@ -76,7 +76,6 @@ - CEngine @@ -106,7 +105,6 @@ RatPack - diff --git a/src/CalcManager/CalcManager.wasm b/src/CalcManager/CalcManager.wasm new file mode 100644 index 00000000..ec28806f Binary files /dev/null and b/src/CalcManager/CalcManager.wasm differ diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index 2b224020..b60e5ecf 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -14,7 +14,11 @@ 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 +#if defined(__EMSCRIPTEN__) +#define MEMORY_COMMAND_TO_UNSIGNED_CHAR(c) static_cast(c) +#else #define MEMORY_COMMAND_TO_UNSIGNED_CHAR(c) __pragma(warning(push)) __pragma(warning(disable : 4309)) static_cast(c) __pragma(warning(pop)) +#endif namespace CalculationManager { diff --git a/src/CalcManager/CalculatorVector.h b/src/CalcManager/CalculatorVector.h index 3795ecf0..8595b0b5 100644 --- a/src/CalcManager/CalculatorVector.h +++ b/src/CalcManager/CalculatorVector.h @@ -10,7 +10,7 @@ #include "Ratpack/CalcErr.h" #include // for std::out_of_range -#if !defined(__WEBASSEMBLY__) +#if !defined(__EMSCRIPTEN__) #include #include // for SAL #endif diff --git a/src/CalcManager/Ratpack/basex.cpp b/src/CalcManager/Ratpack/basex.cpp index f52befa9..053c3a42 100644 --- a/src/CalcManager/Ratpack/basex.cpp +++ b/src/CalcManager/Ratpack/basex.cpp @@ -34,7 +34,7 @@ void _mulnumx(PNUMBER* pa, PNUMBER b); // //---------------------------------------------------------------------------- -void __inline mulnumx(PNUMBER* pa, PNUMBER b) +void /*__inline*/ mulnumx(PNUMBER* pa, PNUMBER b) { if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) @@ -215,7 +215,7 @@ void _divnumx(PNUMBER* pa, PNUMBER b, int32_t precision); // //---------------------------------------------------------------------------- -void __inline divnumx(PNUMBER* pa, PNUMBER b, int32_t precision) +void /*__inline*/ divnumx(PNUMBER* pa, PNUMBER b, int32_t precision) { if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) diff --git a/src/CalcManager/Ratpack/conv.cpp b/src/CalcManager/Ratpack/conv.cpp index a9adcb74..cd7af0e2 100644 --- a/src/CalcManager/Ratpack/conv.cpp +++ b/src/CalcManager/Ratpack/conv.cpp @@ -131,7 +131,8 @@ void* zmalloc(size_t a) 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) - sizeof(MANTTYPE*))); + memcpy(dest->mant, src->mant, (int)(src->cdigit) * sizeof(MANTTYPE)); } //----------------------------------------------------------------------------- @@ -151,6 +152,7 @@ void _destroynum(_In_ PNUMBER pnum) { if (pnum != nullptr) { + free(pnum->mant); free(pnum); } } @@ -198,14 +200,18 @@ PNUMBER _createnum(_In_ uint32_t size) uint32_t cbAlloc; // sizeof( MANTTYPE ) is the size of a 'digit' - if (SUCCEEDED(Calc_ULongAdd(size, 1, &cbAlloc)) && SUCCEEDED(Calc_ULongMult(cbAlloc, sizeof(MANTTYPE), &cbAlloc)) - && SUCCEEDED(Calc_ULongAdd(cbAlloc, sizeof(NUMBER), &cbAlloc))) + if (SUCCEEDED(Calc_ULongMult(size, sizeof(MANTTYPE), &cbAlloc))) { - pnumret = (PNUMBER)zmalloc(cbAlloc); + pnumret = (PNUMBER)zmalloc(sizeof(NUMBER)); if (pnumret == nullptr) { throw(CALC_E_OUTOFMEMORY); } + pnumret->mant = (MANTTYPE*)zmalloc(cbAlloc); + if (pnumret->mant == nullptr) + { + throw(CALC_E_OUTOFMEMORY); + } } else { diff --git a/src/CalcManager/Ratpack/num.cpp b/src/CalcManager/Ratpack/num.cpp index 07b59d91..45fda82a 100644 --- a/src/CalcManager/Ratpack/num.cpp +++ b/src/CalcManager/Ratpack/num.cpp @@ -43,7 +43,7 @@ using namespace std; void _addnum(PNUMBER* pa, PNUMBER b, uint32_t radix); -void __inline addnum(PNUMBER* pa, PNUMBER b, uint32_t radix) +void /*__inline*/ addnum(PNUMBER* pa, PNUMBER b, uint32_t radix) { if (b->cdigit > 1 || b->mant[0] != 0) @@ -186,7 +186,7 @@ void _addnum(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 /*__inline*/ mulnum(PNUMBER* pa, PNUMBER b, uint32_t radix) { if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) @@ -365,7 +365,7 @@ void remnum(PNUMBER* pa, PNUMBER b, uint32_t radix) 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 /*__inline*/ divnum(PNUMBER* pa, PNUMBER b, uint32_t radix, int32_t precision) { if (b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0) diff --git a/src/CalcManager/Ratpack/ratconst.h b/src/CalcManager/Ratpack/ratconst.h index 00f10ad7..7ddf78b3 100644 --- a/src/CalcManager/Ratpack/ratconst.h +++ b/src/CalcManager/Ratpack/ratconst.h @@ -4,591 +4,345 @@ #pragma once // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_one = { 1, - 1, - 0, - { - 1, - } }; +MANTTYPE p_init_num_one[] = { + 1, +}; +inline const NUMBER init_num_one = { 1, 1, 0, p_init_num_one }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_two = { 1, - 1, - 0, - { - 2, - } }; +MANTTYPE p_init_num_two[] = { + 2, +}; +inline const NUMBER init_num_two = { 1, 1, 0, p_init_num_two }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_five = { 1, - 1, - 0, - { - 5, - } }; +MANTTYPE p_init_num_five[] = { + 5, +}; +inline const NUMBER init_num_five = { 1, 1, 0, p_init_num_five }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_six = { 1, - 1, - 0, - { - 6, - } }; +MANTTYPE p_init_num_six[] = { + 6, +}; +inline const NUMBER init_num_six = { 1, 1, 0, p_init_num_six }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_num_ten = { 1, - 1, - 0, - { - 10, - } }; +MANTTYPE p_init_num_ten[] = { + 10, +}; +inline const NUMBER init_num_ten = { 1, 1, 0, p_init_num_ten }; // 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, - } }; +MANTTYPE p_init_p_rat_smallest[] = { + 1, +}; +inline const NUMBER init_p_rat_smallest = { 1, 1, 0, p_init_p_rat_smallest }; +MANTTYPE p_init_q_rat_smallest[] = { + 0, + 190439170, + 901055854, + 10097, +}; +inline const NUMBER init_q_rat_smallest = { 1, 4, 0, p_init_q_rat_smallest }; // 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, - } }; +MANTTYPE p_init_p_rat_negsmallest[] = { + 1, +}; +inline const NUMBER init_p_rat_negsmallest = { -1, 1, 0, p_init_p_rat_negsmallest }; +MANTTYPE p_init_q_rat_negsmallest[] = { + 0, + 190439170, + 901055854, + 10097, +}; +inline const NUMBER init_q_rat_negsmallest = { 1, 4, 0, p_init_q_rat_negsmallest }; // 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, - } }; +MANTTYPE p_init_p_pt_eight_five[] = { + 85, +}; +inline const NUMBER init_p_pt_eight_five = { 1, 1, 0, p_init_p_pt_eight_five }; +MANTTYPE p_init_q_pt_eight_five[] = { + 100, +}; +inline const NUMBER init_q_pt_eight_five = { 1, 1, 0, p_init_q_pt_eight_five }; // 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, - } }; +MANTTYPE p_init_p_rat_six[] = { + 6, +}; +inline const NUMBER init_p_rat_six = { 1, 1, 0, p_init_p_rat_six }; +MANTTYPE p_init_q_rat_six[] = { + 1, +}; +inline const NUMBER init_q_rat_six = { 1, 1, 0, p_init_q_rat_six }; // 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, - } }; +MANTTYPE p_init_p_rat_two[] = { + 2, +}; +inline const NUMBER init_p_rat_two = { 1, 1, 0, p_init_p_rat_two }; +MANTTYPE p_init_q_rat_two[] = { + 1, +}; +inline const NUMBER init_q_rat_two = { 1, 1, 0, p_init_q_rat_two }; // 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, - } }; +MANTTYPE p_init_p_rat_zero[] = { + 0, +}; +inline const NUMBER init_p_rat_zero = { 1, 1, 0, p_init_p_rat_zero }; +MANTTYPE p_init_q_rat_zero[] = { + 1, +}; +inline const NUMBER init_q_rat_zero = { 1, 1, 0, p_init_q_rat_zero }; // 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, - } }; +MANTTYPE p_init_p_rat_one[] = { + 1, +}; +inline const NUMBER init_p_rat_one = { 1, 1, 0, p_init_p_rat_one }; +MANTTYPE p_init_q_rat_one[] = { + 1, +}; +inline const NUMBER init_q_rat_one = { 1, 1, 0, p_init_q_rat_one }; // 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, - } }; +MANTTYPE p_init_p_rat_neg_one[] = { + 1, +}; +inline const NUMBER init_p_rat_neg_one = { -1, 1, 0, p_init_p_rat_neg_one }; +MANTTYPE p_init_q_rat_neg_one[] = { + 1, +}; +inline const NUMBER init_q_rat_neg_one = { 1, 1, 0, p_init_q_rat_neg_one }; // 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, - } }; +MANTTYPE p_init_p_rat_half[] = { + 1, +}; +inline const NUMBER init_p_rat_half = { 1, 1, 0, p_init_p_rat_half }; +MANTTYPE p_init_q_rat_half[] = { + 2, +}; +inline const NUMBER init_q_rat_half = { 1, 1, 0, p_init_q_rat_half }; // 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, - } }; +MANTTYPE p_init_p_rat_ten[] = { + 10, +}; +inline const NUMBER init_p_rat_ten = { 1, 1, 0, p_init_p_rat_ten }; +MANTTYPE p_init_q_rat_ten[] = { + 1, +}; +inline const NUMBER init_q_rat_ten = { 1, 1, 0, p_init_q_rat_ten }; // 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, - } }; +MANTTYPE p_init_p_pi[] = { + 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8, +}; +inline const NUMBER init_p_pi = { 1, 6, 0, p_init_p_pi }; +MANTTYPE p_init_q_pi[] = { + 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2, +}; +inline const NUMBER init_q_pi = { 1, 6, 0, p_init_q_pi }; // 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, - } }; +MANTTYPE p_init_p_two_pi[] = { + 251055792, 567796700, 1773504224, 1198217877, 428852897, 17, +}; +inline const NUMBER init_p_two_pi = { 1, 6, 0, p_init_p_two_pi }; +MANTTYPE p_init_q_two_pi[] = { + 1288380402, 1120116153, 1860424692, 1944118326, 1583591604, 2, +}; +inline const NUMBER init_q_two_pi = { 1, 6, 0, p_init_q_two_pi }; // 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, - } }; +MANTTYPE p_init_p_pi_over_two[] = { + 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8, +}; +inline const NUMBER init_p_pi_over_two = { 1, 6, 0, p_init_p_pi_over_two }; +MANTTYPE p_init_q_pi_over_two[] = { + 429277156, 92748659, 1573365737, 1740753005, 1019699561, 5, +}; +inline const NUMBER init_q_pi_over_two = { 1, 6, 0, p_init_q_pi_over_two }; // 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, - } }; +MANTTYPE p_init_p_one_pt_five_pi[] = { + 1241201312, 270061909, 1051574664, 1924965045, 1340320627, 70, +}; +inline const NUMBER init_p_one_pt_five_pi = { 1, 6, 0, p_init_p_one_pt_five_pi }; +MANTTYPE p_init_q_one_pt_five_pi[] = { + 1579671539, 1837970263, 1067644340, 523549916, 2119366659, 14, +}; +inline const NUMBER init_q_one_pt_five_pi = { 1, 6, 0, p_init_q_one_pt_five_pi }; // 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, - } }; +MANTTYPE p_init_p_e_to_one_half[] = { + 256945612, 216219427, 223516738, 477442596, 581063757, 23, +}; +inline const NUMBER init_p_e_to_one_half = { 1, 6, 0, p_init_p_e_to_one_half }; +MANTTYPE p_init_q_e_to_one_half[] = { + 1536828363, 698484484, 1127331835, 224219346, 245499408, 14, +}; +inline const NUMBER init_q_e_to_one_half = { 1, 6, 0, p_init_q_e_to_one_half }; // 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, - } }; +MANTTYPE p_init_p_rat_exp[] = { + 943665199, 1606559160, 1094967530, 1759391384, 1671799163, 1123581, +}; +inline const NUMBER init_p_rat_exp = { 1, 6, 0, p_init_p_rat_exp }; +MANTTYPE p_init_q_rat_exp[] = { + 879242208, 2022880100, 617392930, 1374929092, 1367479163, 413342, +}; +inline const NUMBER init_q_rat_exp = { 1, 6, 0, p_init_q_rat_exp }; // 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, - } }; +MANTTYPE p_init_p_ln_ten[] = { + 2086268922, 165794492, 1416063951, 1851428830, 1893239400, 65366841, +}; +inline const NUMBER init_p_ln_ten = { 1, 6, 0, p_init_p_ln_ten }; +MANTTYPE p_init_q_ln_ten[] = { + 26790652, 564532679, 783998273, 216030448, 1564709968, 28388458, +}; +inline const NUMBER init_q_ln_ten = { 1, 6, 0, p_init_q_ln_ten }; // 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, - } }; +MANTTYPE p_init_p_ln_two[] = { + 1789230241, 1057927868, 715399197, 908801241, 1411265331, 3, +}; +inline const NUMBER init_p_ln_two = { 1, 6, 0, p_init_p_ln_two }; +MANTTYPE p_init_q_ln_two[] = { + 1559869847, 1930657510, 1228561531, 219003871, 593099283, 5, +}; +inline const NUMBER init_q_ln_two = { 1, 6, 0, p_init_q_ln_two }; // 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, - } }; +MANTTYPE p_init_p_rad_to_deg[] = { + 2127722024, 1904928383, 2016479213, 2048947859, 1578647346, 492, +}; +inline const NUMBER init_p_rad_to_deg = { 1, 6, 0, p_init_p_rad_to_deg }; +MANTTYPE p_init_q_rad_to_deg[] = { + 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8, +}; +inline const NUMBER init_q_rad_to_deg = { 1, 6, 0, p_init_q_rad_to_deg }; // 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, - } }; +MANTTYPE p_init_p_rad_to_grad[] = { + 2125526288, 684931327, 570267400, 129125085, 1038224725, 547, +}; +inline const NUMBER init_p_rad_to_grad = { 1, 6, 0, p_init_p_rad_to_grad }; +MANTTYPE p_init_q_rad_to_grad[] = { + 125527896, 283898350, 1960493936, 1672850762, 1288168272, 8, +}; +inline const NUMBER init_q_rad_to_grad = { 1, 6, 0, p_init_q_rad_to_grad }; // 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, - } }; +MANTTYPE p_init_p_rat_qword[] = { + 2147483647, + 2147483647, + 3, +}; +inline const NUMBER init_p_rat_qword = { 1, 3, 0, p_init_p_rat_qword }; +MANTTYPE p_init_q_rat_qword[] = { + 1, +}; +inline const NUMBER init_q_rat_qword = { 1, 1, 0, p_init_q_rat_qword }; // 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, - } }; +MANTTYPE p_init_p_rat_dword[] = { + 2147483647, + 1, +}; +inline const NUMBER init_p_rat_dword = { 1, 2, 0, p_init_p_rat_dword }; +MANTTYPE p_init_q_rat_dword[] = { + 1, +}; +inline const NUMBER init_q_rat_dword = { 1, 1, 0, p_init_q_rat_dword }; // Autogenerated by _dumprawrat in support.cpp -inline const NUMBER init_p_rat_max_i32 = { 1, - 1, - 0, - { - 2147483647, - } }; -inline const NUMBER init_q_rat_max_i32 = { 1, - 1, - 0, - { - 1, - } }; +MANTTYPE p_init_p_rat_max_i32[] = { + 2147483647, +}; +inline const NUMBER init_p_rat_max_i32 = { 1, 1, 0, p_init_p_rat_max_i32 }; +MANTTYPE p_init_q_rat_max_i32[] = { + 1, +}; +inline const NUMBER init_q_rat_max_i32 = { 1, 1, 0, p_init_q_rat_max_i32 }; // Autogenerated by _dumprawrat in support.cpp -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, - } }; +MANTTYPE p_init_p_rat_min_i32[] = { + 0, + 1, +}; +inline const NUMBER init_p_rat_min_i32 = { -1, 2, 0, p_init_p_rat_min_i32 }; +MANTTYPE p_init_q_rat_min_i32[] = { + 1, +}; +inline const NUMBER init_q_rat_min_i32 = { 1, 1, 0, p_init_q_rat_min_i32 }; // 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, - } }; +MANTTYPE p_init_p_rat_word[] = { + 65535, +}; +inline const NUMBER init_p_rat_word = { 1, 1, 0, p_init_p_rat_word }; +MANTTYPE p_init_q_rat_word[] = { + 1, +}; +inline const NUMBER init_q_rat_word = { 1, 1, 0, p_init_q_rat_word }; // 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, - } }; +MANTTYPE p_init_p_rat_byte[] = { + 255, +}; +inline const NUMBER init_p_rat_byte = { 1, 1, 0, p_init_p_rat_byte }; +MANTTYPE p_init_q_rat_byte[] = { + 1, +}; +inline const NUMBER init_q_rat_byte = { 1, 1, 0, p_init_q_rat_byte }; // 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, - } }; +MANTTYPE p_init_p_rat_400[] = { + 400, +}; +inline const NUMBER init_p_rat_400 = { 1, 1, 0, p_init_p_rat_400 }; +MANTTYPE p_init_q_rat_400[] = { + 1, +}; +inline const NUMBER init_q_rat_400 = { 1, 1, 0, p_init_q_rat_400 }; // 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, - } }; +MANTTYPE p_init_p_rat_360[] = { + 360, +}; +inline const NUMBER init_p_rat_360 = { 1, 1, 0, p_init_p_rat_360 }; +MANTTYPE p_init_q_rat_360[] = { + 1, +}; +inline const NUMBER init_q_rat_360 = { 1, 1, 0, p_init_q_rat_360 }; // 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, - } }; +MANTTYPE p_init_p_rat_200[] = { + 200, +}; +inline const NUMBER init_p_rat_200 = { 1, 1, 0, p_init_p_rat_200 }; +MANTTYPE p_init_q_rat_200[] = { + 1, +}; +inline const NUMBER init_q_rat_200 = { 1, 1, 0, p_init_q_rat_200 }; // 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, - } }; +MANTTYPE p_init_p_rat_180[] = { + 180, +}; +inline const NUMBER init_p_rat_180 = { 1, 1, 0, p_init_p_rat_180 }; +MANTTYPE p_init_q_rat_180[] = { + 1, +}; +inline const NUMBER init_q_rat_180 = { 1, 1, 0, p_init_q_rat_180 }; // 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, - } }; +MANTTYPE p_init_p_rat_max_exp[] = { + 100000, +}; +inline const NUMBER init_p_rat_max_exp = { 1, 1, 0, p_init_p_rat_max_exp }; +MANTTYPE p_init_q_rat_max_exp[] = { + 1, +}; +inline const NUMBER init_q_rat_max_exp = { 1, 1, 0, p_init_q_rat_max_exp }; // 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, - } }; +MANTTYPE p_init_p_rat_min_exp[] = { + 100000, +}; +inline const NUMBER init_p_rat_min_exp = { -1, 1, 0, p_init_p_rat_min_exp }; +MANTTYPE p_init_q_rat_min_exp[] = { + 1, +}; +inline const NUMBER init_q_rat_min_exp = { 1, 1, 0, p_init_q_rat_min_exp }; // 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, - } }; +MANTTYPE p_init_p_rat_max_fact[] = { + 3249, +}; +inline const NUMBER init_p_rat_max_fact = { 1, 1, 0, p_init_p_rat_max_fact }; +MANTTYPE p_init_q_rat_max_fact[] = { + 1, +}; +inline const NUMBER init_q_rat_max_fact = { 1, 1, 0, p_init_q_rat_max_fact }; // 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, - } }; +MANTTYPE p_init_p_rat_min_fact[] = { + 1000, +}; +inline const NUMBER init_p_rat_min_fact = { -1, 1, 0, p_init_p_rat_min_fact }; +MANTTYPE p_init_q_rat_min_fact[] = { + 1, +}; +inline const NUMBER init_q_rat_min_fact = { 1, 1, 0, p_init_q_rat_min_fact }; diff --git a/src/CalcManager/Ratpack/ratpak.h b/src/CalcManager/Ratpack/ratpak.h index c18a70fd..88812e5d 100644 --- a/src/CalcManager/Ratpack/ratpak.h +++ b/src/CalcManager/Ratpack/ratpak.h @@ -66,7 +66,7 @@ typedef struct _number // radix being used. int32_t exp; // The offset of digits from the radix point // (decimal point in radix 10) - MANTTYPE mant[]; + MANTTYPE* mant; // This is actually allocated as a continuation of the // NUMBER structure. } NUMBER, *PNUMBER, **PPNUMBER; diff --git a/src/CalcManager/Ratpack/trans.cpp b/src/CalcManager/Ratpack/trans.cpp index 30104abb..7ae319ec 100644 --- a/src/CalcManager/Ratpack/trans.cpp +++ b/src/CalcManager/Ratpack/trans.cpp @@ -120,6 +120,8 @@ void sinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_200, precision); mulrat(pa, pi, precision); break; + case ANGLE_RAD: + break; } _sinrat(pa, precision); } @@ -223,6 +225,8 @@ void cosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_200, precision); mulrat(pa, pi, precision); break; + case ANGLE_RAD: + break; } _cosrat(pa, radix, precision); } @@ -285,6 +289,9 @@ void tananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_200, precision); mulrat(pa, pi, precision); break; + + case ANGLE_RAD: + break; } _tanrat(pa, radix, precision); } diff --git a/src/CalcManager/UnitConverter.cpp b/src/CalcManager/UnitConverter.cpp deleted file mode 100644 index 3b5490a0..00000000 --- a/src/CalcManager/UnitConverter.cpp +++ /dev/null @@ -1,1105 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include // for std::sort -#include "Command.h" -#include "UnitConverter.h" - -using namespace concurrency; -using namespace std; -using namespace UnitConversionManager; - -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 int32_t MAXIMUMDIGITSALLOWED = 15; -static constexpr int32_t OPTIMALDIGITSALLOWED = 7; - -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)); - -unordered_map quoteConversions; -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) -{ -} - -/// -/// Constructor, sets up all the variables and requires two configLoaders -/// -/// An instance of the IConverterDataLoader interface which we use to read in category/unit names and conversion data -/// An instance of the IConverterDataLoader interface, specialized for loading currency data from an internet service -UnitConverter::UnitConverter(_In_ const shared_ptr& dataLoader, _In_ const shared_ptr& currencyDataLoader) -{ - m_dataLoader = dataLoader; - m_currencyDataLoader = currencyDataLoader; - // declaring the delimiter character conversion map - quoteConversions[L'|'] = L"{p}"; - quoteConversions[L'['] = L"{lc}"; - quoteConversions[L']'] = L"{rc}"; - quoteConversions[L':'] = L"{co}"; - quoteConversions[L','] = L"{cm}"; - quoteConversions[L';'] = L"{sc}"; - quoteConversions[LEFTESCAPECHAR] = L"{lb}"; - quoteConversions[RIGHTESCAPECHAR] = L"{rb}"; - unquoteConversions[L"{p}"] = L'|'; - unquoteConversions[L"{lc}"] = L'['; - unquoteConversions[L"{rc}"] = L']'; - unquoteConversions[L"{co}"] = L':'; - unquoteConversions[L"{cm}"] = L','; - unquoteConversions[L"{sc}"] = L';'; - unquoteConversions[L"{lb}"] = LEFTESCAPECHAR; - unquoteConversions[L"{rb}"] = RIGHTESCAPECHAR; - ClearValues(); - ResetCategoriesAndRatios(); -} - -void UnitConverter::Initialize() -{ - m_dataLoader->LoadData(); -} - -bool UnitConverter::CheckLoad() -{ - if (m_categories.empty()) - { - ResetCategoriesAndRatios(); - } - return !m_categories.empty(); -} - -/// -/// Returns a list of the categories in use by this converter -/// -vector UnitConverter::GetCategories() -{ - CheckLoad(); - return m_categories; -} - -/// -/// Sets the current category in use by this converter, -/// and returns a list of unit types that exist under the given category. -/// -/// Category struct which we are setting -CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& input) -{ - if (m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(input)) - { - m_currencyDataLoader->LoadData(); - } - - vector newUnitList{}; - if (CheckLoad()) - { - if (m_currentCategory.id != input.id) - { - vector& unitVector = m_categoryToUnits[m_currentCategory]; - for (unsigned int i = 0; i < unitVector.size(); i++) - { - unitVector[i].isConversionSource = (unitVector[i].id == m_fromType.id); - unitVector[i].isConversionTarget = (unitVector[i].id == m_toType.id); - } - m_currentCategory = input; - if (!m_currentCategory.supportsNegative && m_currentDisplay.front() == L'-') - { - m_currentDisplay.erase(0, 1); - } - } - - newUnitList = m_categoryToUnits[input]; - } - - InitializeSelectedUnits(); - return make_tuple(newUnitList, m_fromType, m_toType); -} - -/// -/// Gets the category currently being used -/// -Category UnitConverter::GetCurrentCategory() -{ - return m_currentCategory; -} - -/// -/// Sets the current unit types to be used, indicates a likely change in the -/// display values, so we re-calculate and callback the updated values -/// -/// Unit struct which the user is modifying -/// Unit struct we are converting to -void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) -{ - if (!CheckLoad()) - { - return; - } - - m_fromType = fromType; - m_toType = toType; - Calculate(); - - UpdateCurrencySymbols(); -} - -/// -/// Switches the active field, indicating that we are now entering data into -/// what was originally the return field, and storing results into what was -/// originally the current field. We swap appropriate values, -/// but do not callback, as values have not changed. -/// -/// -/// wstring representing the value user had in the field they've just activated. -/// We use this to handle cases where the front-end may choose to trim more digits -/// than we have been storing internally, in which case appending will not function -/// as expected without the use of this parameter. -/// -void UnitConverter::SwitchActive(const wstring& newValue) -{ - if (!CheckLoad()) - { - return; - } - - 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; - - 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) -{ - 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(); -} - -vector UnitConverter::StringToVector(const wstring& w, const wchar_t* delimiter, bool addRemainder) -{ - size_t delimiterIndex = w.find(delimiter); - size_t startIndex = 0; - vector serializedTokens = vector(); - while (delimiterIndex != w.npos) - { - serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex)); - startIndex = delimiterIndex + (int)wcslen(delimiter); - delimiterIndex = w.find(delimiter, startIndex); - } - if (addRemainder) - { - delimiterIndex = w.size(); - serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex)); - } - return serializedTokens; -} - -Category UnitConverter::StringToCategory(const wstring& w) -{ - 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; -} - -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) -{ - vector tokenList = StringToVector(w, L";"); - assert(tokenList.size() == EXPECTEDSERIALIZEDUNITTOKENCOUNT); - Unit serializedUnit; - serializedUnit.id = _wtoi(Unquote(tokenList[0]).c_str()); - 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); - return serializedUnit; -} - -ConversionData UnitConverter::StringToConversionData(const wstring& 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()) - { - return wstring(); - } - - 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; -} - -/// -/// 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) -{ - ClearValues(); - ResetCategoriesAndRatios(); - - if (serializedData.empty()) - { - return; - } - - 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(); -} - -/// -/// 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()) - { - return; - } - - 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); - if (itr != m_categoryToUnits.end()) - { - const auto& curUnits = itr->second; - if (find(curUnits.begin(), curUnits.end(), fromType) != curUnits.end()) - { - m_fromType = fromType; - } - if (find(curUnits.begin(), curUnits.end(), toType) != curUnits.end()) - { - m_toType = toType; - } - } -} - -/// -/// Serializes the Category and Associated Units in the converter and returns it as a string -/// -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; -} - -/// -/// 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) -{ - wstringstream quotedString(wstringstream::out); - - // Iterate over the delimiter characters we need to quote - wstring::const_iterator cursor = s.begin(); - while (cursor != s.end()) - { - if (quoteConversions.find(*cursor) != quoteConversions.end()) - { - quotedString << quoteConversions[*cursor]; - } - else - { - quotedString << *cursor; - } - ++cursor; - } - return quotedString.str(); -} - -/// -/// 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) -{ - wstringstream quotedSubString(wstringstream::out); - wstringstream unquotedString(wstringstream::out); - wstring::const_iterator cursor = s.begin(); - while (cursor != s.end()) - { - if (*cursor == LEFTESCAPECHAR) - { - quotedSubString.str(L""); - while (cursor != s.end() && *cursor != RIGHTESCAPECHAR) - { - quotedSubString << *cursor; - ++cursor; - } - if (cursor == s.end()) - { - // Badly formatted - break; - } - else - { - quotedSubString << *cursor; - unquotedString << unquoteConversions[quotedSubString.str()]; - } - } - else - { - unquotedString << *cursor; - } - ++cursor; - } - return unquotedString.str(); -} - -/// -/// Handles inputs to the converter from the view-model, corresponding to a given button or keyboard press -/// -/// Command enum representing the command that was entered -void UnitConverter::SendCommand(Command command) -{ - if (!CheckLoad()) - { - return; - } - - // TODO: Localization of characters - bool clearFront = false; - if (m_currentDisplay == L"0") - { - clearFront = true; - } - bool clearBack = false; - if ((m_currentHasDecimal && m_currentDisplay.size() - 1 >= MAXIMUMDIGITSALLOWED) - || (!m_currentHasDecimal && m_currentDisplay.size() >= MAXIMUMDIGITSALLOWED)) - { - 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) - { - m_currentDisplay += L"."; - m_currentHasDecimal = true; - } - break; - - case Command::Backspace: - clearFront = false; - clearBack = false; - if ((m_currentDisplay.front() != '-' && m_currentDisplay.size() > 1) || m_currentDisplay.size() > 2) - { - if (m_currentDisplay.back() == '.') - { - m_currentHasDecimal = false; - } - m_currentDisplay.pop_back(); - } - else - { - m_currentDisplay = L"0"; - m_currentHasDecimal = false; - } - 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(); - ResetCategoriesAndRatios(); - break; - - default: - break; - } - - if (clearFront) - { - m_currentDisplay.erase(0, 1); - } - if (clearBack) - { - m_currentDisplay.erase(m_currentDisplay.size() - 1, 1); - m_vmCallback->MaxDigitsReached(); - } - - Calculate(); -} - -/// -/// Sets the callback interface to send display update calls to -/// -/// instance of IDisplayCallback interface that receives our update calls -void UnitConverter::SetViewModelCallback(_In_ const shared_ptr& newCallback) -{ - m_vmCallback = newCallback; - if (CheckLoad()) - { - UpdateViewModel(); - } -} - -void UnitConverter::SetViewModelCurrencyCallback(_In_ const shared_ptr& newCallback) -{ - m_vmCurrencyCallback = newCallback; - - shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); - if (currencyDataLoader != nullptr) - { - currencyDataLoader->SetViewModelCallback(newCallback); - } -} - -task> UnitConverter::RefreshCurrencyRatios() -{ - shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); - return create_task([this, currencyDataLoader]() { - if (currencyDataLoader != nullptr) - { - return currencyDataLoader->TryLoadDataFromWebOverrideAsync(); - } - else - { - return task_from_result(false); - } - }) - .then( - [this, currencyDataLoader](bool didLoad) { - wstring timestamp = L""; - if (currencyDataLoader != nullptr) - { - timestamp = currencyDataLoader->GetCurrencyTimestamp(); - } - - return make_pair(didLoad, timestamp); - }, - task_continuation_context::use_default()); -} - -shared_ptr UnitConverter::GetCurrencyConverterDataLoader() -{ - return dynamic_pointer_cast(m_currencyDataLoader); -} - -/// -/// Converts a double value into another unit type, currently by multiplying by the given double ratio -/// -/// double input value to convert -/// double conversion ratio to use -double UnitConverter::Convert(double value, ConversionData conversionData) -{ - if (conversionData.offsetFirst) - { - return (value + conversionData.offset) * conversionData.ratio; - } - else - { - return (value * conversionData.ratio) + conversionData.offset; - } -} - -/// -/// Calculates the suggested values for the current display value and returns them as a vector -/// -vector> UnitConverter::CalculateSuggested() -{ - if (m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(m_currentCategory)) - { - return vector>(); - } - - vector> returnVector; - vector intermediateVector; - vector intermediateWhimsicalVector; - unordered_map ratios = m_ratioMap[m_fromType]; - // Calculate converted values for every other unit type in this category, along with their magnitude - for (const auto& cur : ratios) - { - if (cur.first != m_fromType && cur.first != m_toType) - { - double convertedValue = Convert(stod(m_currentDisplay), cur.second); - SuggestedValueIntermediate newEntry; - newEntry.magnitude = log10(convertedValue); - newEntry.value = convertedValue; - newEntry.type = cur.first; - if (newEntry.type.isWhimsical == false) - intermediateVector.push_back(newEntry); - else - intermediateWhimsicalVector.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) { - if (abs(first.magnitude) == abs(second.magnitude)) - { - return first.magnitude > second.magnitude; - } - else - { - return abs(first.magnitude) < abs(second.magnitude); - } - }); - - // Now that the list is sorted, iterate over it and populate the return vector with properly rounded and formatted return strings - for (const auto& entry : intermediateVector) - { - wstring roundedString; - if (abs(entry.value) < 100) - { - roundedString = RoundSignificant(entry.value, 2); - } - else if (abs(entry.value) < 1000) - { - roundedString = RoundSignificant(entry.value, 1); - } - else - { - roundedString = RoundSignificant(entry.value, 0); - } - if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative) - { - TrimString(roundedString); - returnVector.push_back(make_tuple(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) { - if (abs(first.magnitude) == abs(second.magnitude)) - { - return first.magnitude > second.magnitude; - } - else - { - return abs(first.magnitude) < abs(second.magnitude); - } - }); - - // Now that the list is sorted, iterate over it and populate the return vector with properly rounded and formatted return strings - vector> whimsicalReturnVector; - - for (const auto& entry : intermediateWhimsicalVector) - { - wstring roundedString; - if (abs(entry.value) < 100) - { - roundedString = RoundSignificant(entry.value, 2); - } - else if (abs(entry.value) < 1000) - { - roundedString = RoundSignificant(entry.value, 1); - } - else - { - roundedString = RoundSignificant(entry.value, 0); - } - - // 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)); - } - } - // Pickup the 'best' whimsical value - currently the first one - if (whimsicalReturnVector.size() != 0) - { - returnVector.push_back(whimsicalReturnVector.at(0)); - } - - return returnVector; -} - -/// -/// Resets categories and ratios -/// -void UnitConverter::ResetCategoriesAndRatios() -{ - m_categories = m_dataLoader->LoadOrderedCategories(); - - m_switchedActive = false; - - if (m_categories.empty()) - { - return; - } - - 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) - { - // 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; - } - } - } - - InitializeSelectedUnits(); -} - -/// -/// Sets the active data loader based on the input category. -/// -shared_ptr UnitConverter::GetDataLoaderForCategory(const Category& category) -{ - if (m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(category)) - { - return m_currencyDataLoader; - } - else - { - return m_dataLoader; - } -} - -/// -/// Sets the initial values for m_fromType and m_toType. -/// This is an internal helper method as opposed to SetCurrentUnits -/// which is for external use by clients. -/// If we fail to set units, we will fallback to the EMPTY_UNIT. -/// -void UnitConverter::InitializeSelectedUnits() -{ - if (m_categoryToUnits.empty()) - { - return; - } - - auto itr = m_categoryToUnits.find(m_currentCategory); - if (itr == m_categoryToUnits.end()) - { - return; - } - - vector curUnits = itr->second; - if (!curUnits.empty()) - { - bool conversionSourceSet = false; - bool conversionTargetSet = false; - for (const Unit& cur : curUnits) - { - if (!conversionSourceSet && cur.isConversionSource) - { - m_fromType = cur; - conversionSourceSet = true; - } - - if (!conversionTargetSet && cur.isConversionTarget) - { - m_toType = cur; - conversionTargetSet = true; - } - - if (conversionSourceSet && conversionTargetSet) - { - return; - } - } - } - - m_fromType = EMPTY_UNIT; - m_toType = EMPTY_UNIT; -} - -/// -/// Resets the value fields to 0 -/// -void UnitConverter::ClearValues() -{ - m_currentHasDecimal = false; - m_returnHasDecimal = false; - m_currentDisplay = L"0"; -} - -/// -/// Checks if either unit is EMPTY_UNIT. -/// -bool UnitConverter::AnyUnitIsEmpty() -{ - return m_fromType == EMPTY_UNIT || m_toType == EMPTY_UNIT; -} - -/// -/// Calculates a new return value based on the current display value -/// -void UnitConverter::Calculate() -{ - if (AnyUnitIsEmpty()) - { - m_returnDisplay = m_currentDisplay; - m_returnHasDecimal = m_currentHasDecimal; - TrimString(m_returnDisplay); - UpdateViewModel(); - return; - } - - unordered_map conversionTable = m_ratioMap[m_fromType]; - double returnValue = stod(m_currentDisplay); - if (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0) - { - m_returnDisplay = m_currentDisplay; - m_returnHasDecimal = m_currentHasDecimal; - TrimString(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--; - } - - if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) - { - wstringstream out(wstringstream::out); - out << scientific << returnValue; - m_returnDisplay = out.str(); - } - else - { - returnValue = stod(m_returnDisplay); - wstring returnString; - if (m_currentDisplay.size() <= OPTIMALDIGITSALLOWED && abs(returnValue) >= OPTIMALDECIMALALLOWED) - { - returnString = RoundSignificant(returnValue, OPTIMALDIGITSALLOWED - min(numPreDecimal, OPTIMALDIGITSALLOWED)); - } - else - { - returnString = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED - min(numPreDecimal, MAXIMUMDIGITSALLOWED)); - } - m_returnDisplay = returnString; - TrimString(m_returnDisplay); - } - m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos); - } - UpdateViewModel(); -} - -/// -/// 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) - { - return; - } - - 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(); -} - -void UnitConverter::UpdateCurrencySymbols() -{ - if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr) - { - shared_ptr currencyDataLoader = GetCurrencyConverterDataLoader(); - const pair currencySymbols = currencyDataLoader->GetCurrencySymbols(m_fromType, m_toType); - const pair currencyRatios = currencyDataLoader->GetCurrencyRatioEquality(m_fromType, m_toType); - - m_vmCurrencyCallback->CurrencySymbolsCallback(currencySymbols.first, currencySymbols.second); - m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second); - } -} - -void UnitConverter::UpdateViewModel() -{ - m_vmCallback->DisplayCallback(m_currentDisplay, m_returnDisplay); - m_vmCallback->SuggestedValueCallback(CalculateSuggested()); -} diff --git a/src/CalcManager/UnitConverter.h b/src/CalcManager/UnitConverter.h deleted file mode 100644 index b111ee79..00000000 --- a/src/CalcManager/UnitConverter.h +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include -#include // 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( - 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) - { - std::wstring nameValue1 = isRtlLanguage ? currencyName : countryName; - std::wstring nameValue2 = isRtlLanguage ? countryName : currencyName; - - name = nameValue1 + L" - " + nameValue2; - accessibleName = nameValue1 + L" " + nameValue2; - } - - virtual ~Unit() - { - } - - int id; - std::wstring name; - std::wstring accessibleName; - std::wstring abbreviation; - bool isConversionSource; - bool isConversionTarget; - bool isWhimsical; - - bool operator!=(const Unit& that) const - { - return that.id != id; - } - - bool operator==(const Unit& that) const - { - return that.id == id; - } - }; - - // The EMPTY_UNIT acts as a 'null-struct' so that - // Unit pointers can safely be dereferenced without - // null checks. - // - // unitId, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical - inline const Unit EMPTY_UNIT = Unit{ -1, L"", L"", true, true, false }; - - struct Category - { - Category() - { - } - - Category(int id, std::wstring name, bool supportsNegative) - : id(id) - , name(name) - , supportsNegative(supportsNegative) - { - } - - int id; - std::wstring name; - bool supportsNegative; - - bool operator!=(const Category& that) const - { - return that.id != id; - } - - bool operator==(const Category& that) const - { - return that.id == id; - } - }; - - class UnitHash - { - public: - size_t operator()(const Unit& x) const - { - return x.id; - } - }; - - class CategoryHash - { - public: - size_t operator()(const Category& x) const - { - return x.id; - } - }; - - struct SuggestedValueIntermediate - { - double magnitude; - double value; - Unit type; - }; - - struct ConversionData - { - ConversionData() - { - } - ConversionData(double ratio, double offset, bool offsetFirst) - : ratio(ratio) - , offset(offset) - , offsetFirst(offsetFirst) - { - } - - virtual ~ConversionData() - { - } - - double ratio; - double offset; - bool offsetFirst; - }; - - struct CurrencyStaticData - { - std::wstring countryCode; - std::wstring countryName; - std::wstring currencyCode; - std::wstring currencyName; - std::wstring currencySymbol; - }; - - struct CurrencyRatio - { - double ratio; - std::wstring sourceCurrencyCode; - std::wstring targetCurrencyCode; - }; - - typedef std::tuple, UnitConversionManager::Unit, UnitConversionManager::Unit> CategorySelectionInitializer; - typedef std::unordered_map< - UnitConversionManager::Unit, - std::unordered_map, - UnitConversionManager::UnitHash> - UnitToUnitToConversionDataMap; - typedef std::unordered_map, UnitConversionManager::CategoryHash> - CategoryToUnitVectorMap; - - class IViewModelCurrencyCallback - { - public: - 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; - virtual void CurrencyTimestampCallback(_In_ const std::wstring& timestamp, bool isWeekOldData) = 0; - virtual void NetworkBehaviorChanged(_In_ int newBehavior) = 0; - }; - - 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 std::unordered_map LoadOrderedRatios(const Unit& u) = 0; - virtual bool SupportsCategory(const Category& target) = 0; - }; - - class ICurrencyConverterDataLoader - { - 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::wstring GetCurrencyTimestamp() = 0; - - virtual concurrency::task TryLoadDataFromCacheAsync() = 0; - virtual concurrency::task TryLoadDataFromWebAsync() = 0; - virtual concurrency::task TryLoadDataFromWebOverrideAsync() = 0; - }; - - class IUnitConverterVMCallback - { - public: - 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; - }; - - class IUnitConverter - { - public: - 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 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 void Calculate() = 0; - virtual void ResetCategoriesAndRatios() = 0; - }; - - class UnitConverter : public IUnitConverter, public std::enable_shared_from_this - { - public: - UnitConverter(_In_ const std::shared_ptr& dataLoader); - UnitConverter(_In_ const std::shared_ptr& dataLoader, _In_ const std::shared_ptr& currencyDataLoader); - - // IUnitConverter - void Initialize() override; - std::vector GetCategories() override; - CategorySelectionInitializer SetCurrentCategory(const Category& input) override; - 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 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; - 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); - - private: - bool CheckLoad(); - double Convert(double value, ConversionData conversionData); - std::vector> CalculateSuggested(); - void ClearValues(); - 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); - void UpdateCurrencySymbols(); - void UpdateViewModel(); - bool AnyUnitIsEmpty(); - std::shared_ptr GetDataLoaderForCategory(const Category& category); - std::shared_ptr GetCurrencyConverterDataLoader(); - - private: - std::shared_ptr m_dataLoader; - std::shared_ptr m_currencyDataLoader; - std::shared_ptr m_vmCallback; - std::shared_ptr m_vmCurrencyCallback; - std::vector m_categories; - CategoryToUnitVectorMap m_categoryToUnits; - UnitToUnitToConversionDataMap m_ratioMap; - Category m_currentCategory; - Unit m_fromType; - Unit m_toType; - std::wstring m_currentDisplay; - std::wstring m_returnDisplay; - bool m_currentHasDecimal; - bool m_returnHasDecimal; - bool m_switchedActive; - }; -} diff --git a/src/CalcManager/build.sh b/src/CalcManager/build.sh new file mode 100644 index 00000000..8d76ba7a --- /dev/null +++ b/src/CalcManager/build.sh @@ -0,0 +1,11 @@ +emcc \ + -std=c++17 \ + -s WASM=1 \ + -s LEGALIZE_JS_FFI=0 \ + -s RESERVED_FUNCTION_POINTERS=64 \ + -s ALLOW_MEMORY_GROWTH=1 \ + -s BINARYEN=1 \ + -s SIDE_MODULE=1 \ + -o CalcManager.wasm \ + -s EXPORT_ALL=1 \ + CEngine/*.cpp RatPack/*.cpp *.cpp -I. diff --git a/src/CalcManager/compat.h b/src/CalcManager/compat.h index b7c9ba5f..becc507c 100644 --- a/src/CalcManager/compat.h +++ b/src/CalcManager/compat.h @@ -1,6 +1,6 @@ #pragma once -#if defined(__WEBASSEMBLY__) +#if defined(__EMSCRIPTEN__) #define HRESULT long #define _In_opt_ @@ -27,4 +27,4 @@ typedef unsigned long DWORD; #define HRESULT_CODE(hr) ((hr)&0xFFFF) #define SCODE_CODE(sc) ((sc)&0xFFFF) -#endif \ No newline at end of file +#endif diff --git a/src/CalcManager/output.txt b/src/CalcManager/output.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/CalcManager/pch.h b/src/CalcManager/pch.h index e66e1a38..f255b1f1 100644 --- a/src/CalcManager/pch.h +++ b/src/CalcManager/pch.h @@ -11,12 +11,15 @@ #include #include #include -#include #include -#include #include #include #include #include #include + +#if !defined(__EMSCRIPTEN__) +#include #include +#include +#endif diff --git a/src/Calculator.Shared/Assets/CalcMDL2.woff b/src/Calculator.Shared/Assets/CalcMDL2.woff new file mode 100644 index 00000000..e32f7a09 Binary files /dev/null and b/src/Calculator.Shared/Assets/CalcMDL2.woff differ diff --git a/src/Calculator.Shared/CalcManager/CalculatorManager.Interop.cs b/src/Calculator.Shared/CalcManager/CalculatorManager.Interop.cs index cfd5b643..88ebd595 100644 --- a/src/Calculator.Shared/CalcManager/CalculatorManager.Interop.cs +++ b/src/Calculator.Shared/CalcManager/CalculatorManager.Interop.cs @@ -10,137 +10,142 @@ using System.Text; namespace CalculationManager { + public static class NativeDispatch + { + [DllImport("CalcManager")] + public static extern int CalculatorManager_Create(ref CalculatorManager_CreateParams parms); + + [DllImport("CalcManager")] + public static extern void CalculatorManager_SendCommand(int instance, Command command); + + public delegate int GetCEngineStringFunc(int state, string id); + public delegate void BinaryOperatorReceivedFunc(int state); + public delegate void SetPrimaryDisplayCallbackFunc(int state, string displayStringValue, bool isError); + public delegate void SetIsInErrorCallbackFunc(int state, bool isError); + public delegate void SetParenthesisNumberCallbackFunc(int state, int parenthesisCount); + + public delegate void MaxDigitsReachedCallbackFunc(int state); + public delegate void MemoryItemChangedCallbackFunc(int state, int indexOfMemory); + public delegate void OnHistoryItemAddedCallbackFunc(int state, int addedItemIndex); + public delegate void OnNoRightParenAddedCallbackFunc(int state); + public delegate void SetExpressionDisplayCallbackFunc(int state); + public delegate void SetMemorizedNumbersCallbackFunc(int state, string[] newMemorizedNumbers); + + public static GetCEngineStringFunc _getCEngineStringCallback = GetCEngineStringCallback; + public static BinaryOperatorReceivedFunc _binaryOperatorReceivedCallback = BinaryOperatorReceivedCallback; + public static SetPrimaryDisplayCallbackFunc _setPrimaryDisplayCallback = SetPrimaryDisplayCallback; + public static SetIsInErrorCallbackFunc _setIsInErrorCallback = SetIsInErrorCallback; + public static SetParenthesisNumberCallbackFunc _setParenthesisNumberCallback = SetParenthesisNumberCallback; + + public static MaxDigitsReachedCallbackFunc _maxDigitsReachedCallback = MaxDigitsReachedCallback; + public static MemoryItemChangedCallbackFunc _memoryItemChangedCallback = MemoryItemChangedCallback; + public static OnHistoryItemAddedCallbackFunc _onHistoryItemAddedCallback = OnHistoryItemAddedCallback; + public static OnNoRightParenAddedCallbackFunc _onNoRightParenAddedCallback = OnNoRightParenAddedCallback; + public static SetExpressionDisplayCallbackFunc _setExpressionDisplayCallback = SetExpressionDisplayCallback; + public static SetMemorizedNumbersCallbackFunc _setMemorizedNumbersCallback = SetMemorizedNumbersCallback; + + public static void MaxDigitsReachedCallback(int state) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.MaxDigitsReached(); + + Debug.WriteLine($"CalculatorManager.MaxDigitsReachedCallback"); + } + + public static void MemoryItemChangedCallback(int state, int indexOfMemory) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.MemoryItemChanged(indexOfMemory); + + Debug.WriteLine($"CalculatorManager.MemoryItemChangedCallback({indexOfMemory})"); + } + + public static void OnHistoryItemAddedCallback(int state, int addedItemIndex) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.OnHistoryItemAdded(addedItemIndex); + + Debug.WriteLine($"CalculatorManager.OnHistoryItemAddedCallback({addedItemIndex})"); + } + + public static void OnNoRightParenAddedCallback(int state) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.OnNoRightParenAdded(); + + Debug.WriteLine($"CalculatorManager.OnNoRightParenAddedCallback"); + } + + public static void SetExpressionDisplayCallback(int state) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + // manager.SetExpressionDisplay(); + + Debug.WriteLine($"CalculatorManager.SetExpressionDisplayCallback"); + } + + public static void SetMemorizedNumbersCallback(int state, string[] newMemorizedNumbers) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.SetMemorizedNumbers(newMemorizedNumbers.ToList()); + + Debug.WriteLine($"CalculatorManager.SetMemorizedNumbersCallback({string.Join(";", newMemorizedNumbers)})"); + } + + public static void SetParenthesisNumberCallback(int state, int parenthesisCount) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.SetParenthesisNumber(parenthesisCount); + + Debug.WriteLine($"CalculatorManager.SetParenthesisNumberCallback({parenthesisCount})"); + } + + public static void BinaryOperatorReceivedCallback(int state) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.BinaryOperatorReceived(); + + Debug.WriteLine($"CalculatorManager.BinaryOperatorReceivedCallback"); + } + + public static void SetPrimaryDisplayCallback(int state, string displayStringValue, bool isError) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.SetPrimaryDisplay(displayStringValue, isError); + + Debug.WriteLine($"CalculatorManager.SetPrimaryDisplayCallback({displayStringValue}, {isError})"); + } + + public static void SetIsInErrorCallback(int state, bool isError) + { + var manager = GCHandle.FromIntPtr((IntPtr)state).Target as CalculatorDisplay; + manager.SetIsInError(isError); + + Debug.WriteLine($"CalculatorManager.SetIsInErrorCallback({isError})"); + } + + public static int GetCEngineStringCallback(int state, string resourceId) + { + var provider = GCHandle.FromIntPtr((IntPtr)state).Target as EngineResourceProvider; + var ret = provider.GetCEngineString(resourceId) ?? ""; + + var retBytes = Encoding.UTF8.GetBytes(ret); + var retPtr = Marshal.AllocHGlobal(retBytes.Length + 1); + Marshal.WriteByte(retPtr + retBytes.Length, 0); + Marshal.Copy(retBytes, 0, retPtr, retBytes.Length); + + Debug.WriteLine($"CalculatorManager.GetCEngineStringCallback({resourceId},{ret})"); + + return (int)retPtr; + } + } + public partial class CalculatorManager : ICalcDisplay { - [DllImport("CalcManager")] - public static extern IntPtr CalculatorManager_Create(ref CalculatorManager_CreateParams parms); - [DllImport("CalcManager")] - public static extern void CalculatorManager_SendCommand(IntPtr instance, Command command); + private GCHandle _displayCallbackHandle; + private GCHandle _resourceProviderHandle; + private readonly int _nativeManager; - private delegate IntPtr GetCEngineStringFunc(IntPtr state, string id); - private delegate void BinaryOperatorReceivedFunc(IntPtr state); - private delegate void SetPrimaryDisplayCallbackFunc(IntPtr state, string displayStringValue, bool isError); - private delegate void SetIsInErrorCallbackFunc(IntPtr state, bool isError); - private delegate void SetParenthesisNumberCallbackFunc(IntPtr state, int parenthesisCount); - - private delegate void MaxDigitsReachedCallbackFunc(IntPtr state); - private delegate void MemoryItemChangedCallbackFunc(IntPtr state, int indexOfMemory); - private delegate void OnHistoryItemAddedCallbackFunc(IntPtr state, int addedItemIndex); - private delegate void OnNoRightParenAddedCallbackFunc(IntPtr state); - private delegate void SetExpressionDisplayCallbackFunc(IntPtr state); - private delegate void SetMemorizedNumbersCallbackFunc(IntPtr state, string[] newMemorizedNumbers); - - private static GetCEngineStringFunc _getCEngineStringCallback = GetCEngineStringCallback; - private static BinaryOperatorReceivedFunc _binaryOperatorReceivedCallback = BinaryOperatorReceivedCallback; - private static SetPrimaryDisplayCallbackFunc _setPrimaryDisplayCallback = SetPrimaryDisplayCallback; - private static SetIsInErrorCallbackFunc _setIsInErrorCallback = SetIsInErrorCallback; - private static SetParenthesisNumberCallbackFunc _setParenthesisNumberCallback = SetParenthesisNumberCallback; - - private static MaxDigitsReachedCallbackFunc _maxDigitsReachedCallback = MaxDigitsReachedCallback; - private static MemoryItemChangedCallbackFunc _memoryItemChangedCallback = MemoryItemChangedCallback; - private static OnHistoryItemAddedCallbackFunc _onHistoryItemAddedCallback = OnHistoryItemAddedCallback; - private static OnNoRightParenAddedCallbackFunc _onNoRightParenAddedCallback = OnNoRightParenAddedCallback; - private static SetExpressionDisplayCallbackFunc _setExpressionDisplayCallback = SetExpressionDisplayCallback; - private static SetMemorizedNumbersCallbackFunc _setMemorizedNumbersCallback = SetMemorizedNumbersCallback; - - private GCHandle _displayCallbackHandle; - private GCHandle _resourceProviderHandle; - private readonly IntPtr _nativeManager; - - private static void MaxDigitsReachedCallback(IntPtr state) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.MaxDigitsReached(); - - Debug.WriteLine($"CalculatorManager.MaxDigitsReachedCallback"); - } - - private static void MemoryItemChangedCallback(IntPtr state, int indexOfMemory) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.MemoryItemChanged(indexOfMemory); - - Debug.WriteLine($"CalculatorManager.MemoryItemChangedCallback({indexOfMemory})"); - } - - private static void OnHistoryItemAddedCallback(IntPtr state, int addedItemIndex) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.OnHistoryItemAdded(addedItemIndex); - - Debug.WriteLine($"CalculatorManager.OnHistoryItemAddedCallback({addedItemIndex})"); - } - - private static void OnNoRightParenAddedCallback(IntPtr state) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.OnNoRightParenAdded(); - - Debug.WriteLine($"CalculatorManager.OnNoRightParenAddedCallback"); - } - - private static void SetExpressionDisplayCallback(IntPtr state) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - // manager.SetExpressionDisplay(); - - Debug.WriteLine($"CalculatorManager.SetExpressionDisplayCallback"); - } - - private static void SetMemorizedNumbersCallback(IntPtr state, string[] newMemorizedNumbers) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.SetMemorizedNumbers(newMemorizedNumbers.ToList()); - - Debug.WriteLine($"CalculatorManager.SetMemorizedNumbersCallback({string.Join(";", newMemorizedNumbers)})"); - } - - private static void SetParenthesisNumberCallback(IntPtr state, int parenthesisCount) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.SetParenthesisNumber(parenthesisCount); - - Debug.WriteLine($"CalculatorManager.SetParenthesisNumberCallback({parenthesisCount})"); - } - - private static void BinaryOperatorReceivedCallback(IntPtr state) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.BinaryOperatorReceived(); - - Debug.WriteLine($"CalculatorManager.BinaryOperatorReceivedCallback"); - } - - private static void SetPrimaryDisplayCallback(IntPtr state, string displayStringValue, bool isError) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.SetPrimaryDisplay(displayStringValue, isError); - - Debug.WriteLine($"CalculatorManager.SetPrimaryDisplayCallback({displayStringValue}, {isError})"); - } - - private static void SetIsInErrorCallback(IntPtr state, bool isError) - { - var manager = GCHandle.FromIntPtr(state).Target as CalculatorDisplay; - manager.SetIsInError(isError); - - Debug.WriteLine($"CalculatorManager.SetIsInErrorCallback({isError})"); - } - - private static IntPtr GetCEngineStringCallback(IntPtr state, string resourceId) - { - var provider = GCHandle.FromIntPtr(state).Target as EngineResourceProvider; - var ret = provider.GetCEngineString(resourceId) ?? ""; - - var retBytes = Encoding.UTF8.GetBytes(ret); - var retPtr = Marshal.AllocHGlobal(retBytes.Length+1); - Marshal.WriteByte(retPtr + retBytes.Length, 0); - Marshal.Copy(retBytes, 0, retPtr, retBytes.Length); - - Debug.WriteLine($"CalculatorManager.GetCEngineStringCallback({resourceId},{ret})"); - - return retPtr; - } - } + } } diff --git a/src/Calculator.Shared/CalcManager/CalculatorManager.cs b/src/Calculator.Shared/CalcManager/CalculatorManager.cs index 46a8241e..00049a96 100644 --- a/src/Calculator.Shared/CalcManager/CalculatorManager.cs +++ b/src/Calculator.Shared/CalcManager/CalculatorManager.cs @@ -100,33 +100,59 @@ namespace CalculationManager public CalculatorManager(ref CalculatorDisplay displayCallback, ref EngineResourceProvider resourceProvider) { - Debug.WriteLine($"new CalculatorManager"); + Debug.WriteLine($"new CalculatorManager"); displayCallback = new CalculatorDisplay(); resourceProvider = new EngineResourceProvider(); _displayCallbackHandle = GCHandle.Alloc(displayCallback); _resourceProviderHandle = GCHandle.Alloc(resourceProvider); +#if __WASM__ + var rawPtrs = Uno.Foundation.WebAssemblyRuntime.InvokeJS("CalcManager.registerCallbacks()"); + var ptrs = rawPtrs.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + + var p = new CalculatorManager_CreateParams + { + CalculatorState = GCHandle.ToIntPtr(_displayCallbackHandle), + ResourceState = GCHandle.ToIntPtr(_resourceProviderHandle), + + GetCEngineString = (IntPtr)int.Parse(ptrs[0]), + BinaryOperatorReceived = (IntPtr)int.Parse(ptrs[1]), + SetPrimaryDisplay = (IntPtr)int.Parse(ptrs[2]), + SetIsInError = (IntPtr)int.Parse(ptrs[3]), + SetParenthesisNumber = (IntPtr)int.Parse(ptrs[4]), + MaxDigitsReached = (IntPtr)int.Parse(ptrs[5]), + MemoryItemChanged = (IntPtr)int.Parse(ptrs[6]), + OnHistoryItemAdded = (IntPtr)int.Parse(ptrs[7]), + OnNoRightParenAdded = (IntPtr)int.Parse(ptrs[8]), + SetExpressionDisplay = (IntPtr)int.Parse(ptrs[9]), + SetMemorizedNumbers = (IntPtr)int.Parse(ptrs[10]), + }; + +#else var p = new CalculatorManager_CreateParams { CalculatorState = GCHandle.ToIntPtr(_displayCallbackHandle), - GetCEngineString = Marshal.GetFunctionPointerForDelegate(_getCEngineStringCallback), + GetCEngineString = Marshal.GetFunctionPointerForDelegate(NativeDispatch._getCEngineStringCallback), ResourceState = GCHandle.ToIntPtr(_resourceProviderHandle), - BinaryOperatorReceived = Marshal.GetFunctionPointerForDelegate(_binaryOperatorReceivedCallback), - SetPrimaryDisplay = Marshal.GetFunctionPointerForDelegate(_setPrimaryDisplayCallback), - SetIsInError = Marshal.GetFunctionPointerForDelegate(_setIsInErrorCallback), - SetParenthesisNumber = Marshal.GetFunctionPointerForDelegate(_setParenthesisNumberCallback), - MaxDigitsReached = Marshal.GetFunctionPointerForDelegate(_maxDigitsReachedCallback), - MemoryItemChanged = Marshal.GetFunctionPointerForDelegate(_memoryItemChangedCallback), - OnHistoryItemAdded = Marshal.GetFunctionPointerForDelegate(_onHistoryItemAddedCallback), - OnNoRightParenAdded = Marshal.GetFunctionPointerForDelegate(_onNoRightParenAddedCallback), - SetExpressionDisplay = Marshal.GetFunctionPointerForDelegate(_setExpressionDisplayCallback), - SetMemorizedNumbers = Marshal.GetFunctionPointerForDelegate(_setMemorizedNumbersCallback), + BinaryOperatorReceived = Marshal.GetFunctionPointerForDelegate(NativeDispatch._binaryOperatorReceivedCallback), + SetPrimaryDisplay = Marshal.GetFunctionPointerForDelegate(NativeDispatch._setPrimaryDisplayCallback), + SetIsInError = Marshal.GetFunctionPointerForDelegate(NativeDispatch._setIsInErrorCallback), + SetParenthesisNumber = Marshal.GetFunctionPointerForDelegate(NativeDispatch._setParenthesisNumberCallback), + MaxDigitsReached = Marshal.GetFunctionPointerForDelegate(NativeDispatch._maxDigitsReachedCallback), + MemoryItemChanged = Marshal.GetFunctionPointerForDelegate(NativeDispatch._memoryItemChangedCallback), + OnHistoryItemAdded = Marshal.GetFunctionPointerForDelegate(NativeDispatch._onHistoryItemAddedCallback), + OnNoRightParenAdded = Marshal.GetFunctionPointerForDelegate(NativeDispatch._onNoRightParenAddedCallback), + SetExpressionDisplay = Marshal.GetFunctionPointerForDelegate(NativeDispatch._setExpressionDisplayCallback), + SetMemorizedNumbers = Marshal.GetFunctionPointerForDelegate(NativeDispatch._setMemorizedNumbersCallback), }; - _nativeManager = CalculatorManager_Create(ref p); - } +#endif + Debug.WriteLine($"-> CalculatorManager_Create"); + _nativeManager = NativeDispatch.CalculatorManager_Create(ref p); + Debug.WriteLine($"<- CalculatorManager_Create"); + } public void Reset(bool clearMemory = true) => throw new NotImplementedException(); public void SetStandardMode() => throw new NotImplementedException(); @@ -136,7 +162,7 @@ namespace CalculationManager { Debug.WriteLine($"CalculatorManager.SendCommand({command})"); - CalculatorManager_SendCommand(_nativeManager, command); + NativeDispatch.CalculatorManager_SendCommand(_nativeManager, command); } public List SerializeCommands() => throw new NotImplementedException(); diff --git a/src/Calculator.Shared/Calculator.Shared.projitems b/src/Calculator.Shared/Calculator.Shared.projitems index 6dd46e12..24599449 100644 --- a/src/Calculator.Shared/Calculator.Shared.projitems +++ b/src/Calculator.Shared/Calculator.Shared.projitems @@ -225,6 +225,7 @@ + diff --git a/src/Calculator.Shared/Styles.xaml b/src/Calculator.Shared/Styles.xaml index 98abae65..15a910e4 100644 --- a/src/Calculator.Shared/Styles.xaml +++ b/src/Calculator.Shared/Styles.xaml @@ -1,10 +1,14 @@  + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="using:CalculatorApp" + mc:Ignorable="xamarin"> + + - - + + + + 500 320 - ms-appx:///Assets/CalcMDL2.ttf#Calculator MDL2 Assets + ms-appx:///Assets/CalcMDL2.ttf#Calculator MDL2 Assets + Calculator MDL2 Assets 256 0,1,0,0 @@ -331,9 +350,7 @@ To="KeyBoardEntry" /> - + + - --> + - + - + + mc:Ignorable="xamarin"> @@ -188,6 +189,7 @@ @@ -967,6 +969,7 @@ IsOperatorCommand="{x:Bind Model.IsOperatorCommand, Mode=OneWay}" TabIndex="1" /> { - // UNO TODO - //if (TraceLogger.GetInstance().UpdateWindowIdLog(ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread()))) - //{ - // TraceLogger.GetInstance().LogAppLaunchComplete(); - // AppLifecycleLogger.GetInstance().LaunchUIResponsive(); - // AppLifecycleLogger.GetInstance().LaunchVisibleComplete(); - //} - }); - } + Windows.UI.Xaml.Window.Current.SizeChanged += WindowSizeChanged; + UpdateViewState(); - void SetDefaultFocus() + SetHeaderAutomationName(); + SetDefaultFocus(); + + // Delay load things later when we get a chance. + this.Dispatcher.RunAsync( + CoreDispatcherPriority.Normal, () => + { + // UNO TODO + //if (TraceLogger.GetInstance().UpdateWindowIdLog(ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread()))) + //{ + // TraceLogger.GetInstance().LogAppLaunchComplete(); + // AppLifecycleLogger.GetInstance().LaunchUIResponsive(); + // AppLifecycleLogger.GetInstance().LaunchVisibleComplete(); + //} + }); + } + } + + void SetDefaultFocus() { if (m_calculator != null && m_calculator.Visibility == Visibility.Visible) { @@ -281,7 +303,7 @@ namespace CalculatorApp // delay load calculator. m_calculator = new Calculator(); m_calculator.Name = "Calculator"; - m_calculator.DataContext = m_model.CalculatorViewModel; + m_calculator.DataContext = m_model.CalculatorViewModel; Binding isStandardBinding = new Binding(); isStandardBinding.Path = new PropertyPath("IsStandard"); m_calculator.SetBinding(CalculatorApp.Calculator.IsStandardProperty, isStandardBinding); @@ -305,7 +327,7 @@ namespace CalculatorApp ShowHideControls(this.Model.Mode); } - if (m_dateCalculator != null) + if (m_dateCalculator != null) { // UNO TODO // m_dateCalculator.CloseCalendarFlyout(); diff --git a/src/Calculator.Wasm/Calculator.Wasm.csproj b/src/Calculator.Wasm/Calculator.Wasm.csproj index 1555afa4..3ec72b5f 100644 --- a/src/Calculator.Wasm/Calculator.Wasm.csproj +++ b/src/Calculator.Wasm/Calculator.Wasm.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,15 +6,20 @@ true $(DefineConstants);__WASM__ NU1701 - false + false - + true + release-dynamic + + + C:\Users\jerome.laban\Downloads\mono-wasm-4d023b6bf84.zip + - + @@ -27,9 +32,10 @@ You can safely remove this ItemGroup completely. --> + - + @@ -37,5 +43,8 @@ + + + diff --git a/src/Calculator.Wasm/Program.cs b/src/Calculator.Wasm/Program.cs index 46ada3e5..40b01b4c 100644 --- a/src/Calculator.Wasm/Program.cs +++ b/src/Calculator.Wasm/Program.cs @@ -11,8 +11,10 @@ namespace WindowsCalculator.Wasm private static App _app; static void Main(string[] args) - { - ConfigureFilters(LogExtensionPoint.AmbientLoggerFactory); + { + Console.WriteLine("Program.Main"); + + ConfigureFilters(LogExtensionPoint.AmbientLoggerFactory); Windows.UI.Xaml.Application.Start(_ => _app = new App()); } diff --git a/src/Calculator.Wasm/WasmCSS/Fonts.css b/src/Calculator.Wasm/WasmCSS/Fonts.css index 27e5a545..a49767b6 100644 --- a/src/Calculator.Wasm/WasmCSS/Fonts.css +++ b/src/Calculator.Wasm/WasmCSS/Fonts.css @@ -1,7 +1,12 @@ @font-face { - font-family: "Symbols"; - /* winjs-symbols.woff2: https://github.com/Microsoft/fonts/tree/master/Symbols */ - src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'); + font-family: "Calculator MDL2 Assets"; + src: url("Assets/CalcMDL2.woff") format('woff'); +} + +@font-face { + font-family: "Symbols"; + /* winjs-symbols.woff2: https://github.com/Microsoft/fonts/tree/master/Symbols */ + src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'); } @font-face { diff --git a/src/Calculator.Wasm/WasmScripts/CalcManager.js b/src/Calculator.Wasm/WasmScripts/CalcManager.js new file mode 100644 index 00000000..fffd2bfa --- /dev/null +++ b/src/Calculator.Wasm/WasmScripts/CalcManager.js @@ -0,0 +1,44 @@ +class CalcManager { + static registerCallbacks() { + + var _getCEngineStringCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:GetCEngineStringCallback"); + var _binaryOperatorReceivedCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:BinaryOperatorReceivedCallback"); + var _setPrimaryDisplayCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:SetPrimaryDisplayCallback"); + var _setIsInErrorCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:SetIsInErrorCallback"); + var _setParenthesisNumberCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:SetParenthesisNumberCallback"); + var _maxDigitsReachedCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:MaxDigitsReachedCallback"); + var _memoryItemChangedCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:MemoryItemChangedCallback"); + var _onHistoryItemAddedCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:OnHistoryItemAddedCallback"); + var _onNoRightParenAddedCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:OnNoRightParenAddedCallback"); + var _setExpressionDisplayCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:SetExpressionDisplayCallback"); + var _setMemorizedNumbersCallback = Module.mono_bind_static_method("[Calculator.Wasm] CalculationManager.NativeDispatch:SetMemorizedNumbersCallback"); + + var fGetCEngineStringCallback = Module.addFunction((state, id) => _getCEngineStringCallback(state, Module.UTF8ToString(id)), 'iii'); + + var fBinaryOperatorReceivedCallback = Module.addFunction((state) => _binaryOperatorReceivedCallback(state), 'vi'); + var fSetPrimaryDisplayCallback = Module.addFunction((state, displayStringValue, isError) => _setPrimaryDisplayCallback(state, Module.UTF8ToString(displayStringValue), isError), 'viii'); + var fSetIsInErrorCallback = Module.addFunction((state, isError) => _setIsInErrorCallback(state, isError), 'vii'); + var fSetParenthesisNumberCallback = Module.addFunction((state, parenthesisCount) => _setParenthesisNumberCallback(state, parenthesisCount), 'vii'); + var fMaxDigitsReachedCallback = Module.addFunction((state) => _maxDigitsReachedCallback(state), 'vii'); + var fMemoryItemChangedCallback = Module.addFunction((state, indexOfMemory) => _memoryItemChangedCallback(state, indexOfMemory), 'vii'); + var fOnHistoryItemAddedCallback = Module.addFunction((state, addedItemIndex) => _onHistoryItemAddedCallback(state, addedItemIndex), 'vii'); + var fOnNoRightParenAddedCallback = Module.addFunction((state) => _onNoRightParenAddedCallback (state), 'vi'); + var fSetExpressionDisplayCallback = Module.addFunction((state) => _setExpressionDisplayCallback (state), 'vi'); + var fSetMemorizedNumbersCallback = Module.addFunction((state, numbers) => _setMemorizedNumbersCallback(state, numbers), 'vii'); + + var ret = `${fGetCEngineStringCallback};` + + `${fBinaryOperatorReceivedCallback};` + + `${fSetPrimaryDisplayCallback};` + + `${fSetIsInErrorCallback};` + + `${fSetParenthesisNumberCallback};` + + `${fMaxDigitsReachedCallback};` + + `${fMemoryItemChangedCallback};` + + `${fOnHistoryItemAddedCallback};` + + `${fOnNoRightParenAddedCallback};` + + `${fSetExpressionDisplayCallback};` + + `${fSetMemorizedNumbersCallback};` + ; + + return ret; + } +}