Merge remote-tracking branch 'upstream/master' into Fix245

This commit is contained in:
Rudy Huyn 2019-03-18 16:55:59 -07:00
commit 55c860361a
11 changed files with 504 additions and 441 deletions

View file

@ -416,8 +416,11 @@ int CHistoryCollector::AddCommand(_In_ const std::shared_ptr<IExpressionCommand>
// To Update the operands in the Expression according to the current Radix // To Update the operands in the Expression according to the current Radix
void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precision) void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precision)
{ {
if (m_spTokens != nullptr) if (m_spTokens == nullptr)
{ {
return;
}
unsigned int size; unsigned int size;
IFT(m_spTokens->GetSize(&size)); IFT(m_spTokens->GetSize(&size));
@ -444,7 +447,6 @@ void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precisio
} }
SetExpressionDisplay(); SetExpressionDisplay();
} }
}
void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol) void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol)
{ {

View file

@ -490,8 +490,12 @@ namespace CalculationManager
void CalculatorManager::MemorizeNumber() void CalculatorManager::MemorizeNumber()
{ {
m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber)); m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber));
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
m_currentCalculatorEngine->ProcessCommand(IDC_STORE); m_currentCalculatorEngine->ProcessCommand(IDC_STORE);
auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject(); auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject();
@ -506,7 +510,6 @@ namespace CalculationManager
} }
this->SetMemorizedNumbersString(); this->SetMemorizedNumbersString();
} }
}
/// <summary> /// <summary>
/// Recall the memorized number. /// Recall the memorized number.
@ -516,12 +519,15 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory)
{ {
SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory); SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
this->MemorizedNumberSelect(indexOfMemory); this->MemorizedNumberSelect(indexOfMemory);
m_currentCalculatorEngine->ProcessCommand(IDC_RECALL); m_currentCalculatorEngine->ProcessCommand(IDC_RECALL);
} }
}
/// <summary> /// <summary>
/// Do the addition to the selected memory /// Do the addition to the selected memory
@ -532,8 +538,12 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory)
{ {
SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory); SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
if (m_memorizedNumbers.empty()) if (m_memorizedNumbers.empty())
{ {
this->MemorizeNumber(); this->MemorizeNumber();
@ -550,7 +560,6 @@ namespace CalculationManager
m_displayCallback->MemoryItemChanged(indexOfMemory); m_displayCallback->MemoryItemChanged(indexOfMemory);
} }
}
void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory)
{ {
@ -570,8 +579,12 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory)
{ {
SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory); SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
// To add negative of the number on display to the memory -x = x - 2x // To add negative of the number on display to the memory -x = x - 2x
if (m_memorizedNumbers.empty()) if (m_memorizedNumbers.empty())
{ {
@ -591,7 +604,6 @@ namespace CalculationManager
m_displayCallback->MemoryItemChanged(indexOfMemory); m_displayCallback->MemoryItemChanged(indexOfMemory);
} }
}
/// <summary> /// <summary>
/// Clear all the memorized values /// Clear all the memorized values
@ -613,12 +625,14 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param> /// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory)
{ {
if (!(m_currentCalculatorEngine->FInErrorState())) if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
auto memoryObject = m_memorizedNumbers.at(indexOfMemory); auto memoryObject = m_memorizedNumbers.at(indexOfMemory);
m_currentCalculatorEngine->PersistedMemObject(memoryObject); m_currentCalculatorEngine->PersistedMemObject(memoryObject);
} }
}
/// <summary> /// <summary>
/// Helper function that needs to be executed when memory is modified /// Helper function that needs to be executed when memory is modified
@ -627,15 +641,17 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param> /// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory) void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory)
{ {
if (!(m_currentCalculatorEngine->FInErrorState())) if (m_currentCalculatorEngine->FInErrorState())
{ {
return;
}
auto memoryObject = m_currentCalculatorEngine->PersistedMemObject(); auto memoryObject = m_currentCalculatorEngine->PersistedMemObject();
if (memoryObject != nullptr) if (memoryObject != nullptr)
{ {
m_memorizedNumbers.at(indexOfMemory) = *memoryObject; m_memorizedNumbers.at(indexOfMemory) = *memoryObject;
} }
} }
}
void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory) void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory)
{ {

View file

@ -612,6 +612,7 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis
break; break;
} }
// Drop through in the 'e'-as-a-digit case // Drop through in the 'e'-as-a-digit case
[[fallthrough]];
default: default:
state = machine[state][NZ]; state = machine[state][NZ];
break; break;
@ -646,7 +647,7 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis
break; break;
case LD: case LD:
pnumret->exp++; pnumret->exp++;
// Fall through [[fallthrough]];
case DD: case DD:
{ {
curChar = NormalizeCharDigit(curChar, radix); curChar = NormalizeCharDigit(curChar, radix);

View file

@ -109,22 +109,8 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i
vector<Unit>& unitVector = m_categoryToUnits[m_currentCategory]; vector<Unit>& unitVector = m_categoryToUnits[m_currentCategory];
for (unsigned int i = 0; i < unitVector.size(); i++) for (unsigned int i = 0; i < unitVector.size(); i++)
{ {
if (unitVector[i].id == m_fromType.id) unitVector[i].isConversionSource = (unitVector[i].id == m_fromType.id);
{ unitVector[i].isConversionTarget = (unitVector[i].id == m_toType.id);
unitVector[i].isConversionSource = true;
}
else
{
unitVector[i].isConversionSource = false;
}
if (unitVector[i].id == m_toType.id)
{
unitVector[i].isConversionTarget = true;
}
else
{
unitVector[i].isConversionTarget = false;
}
} }
m_currentCategory = input; m_currentCategory = input;
if (!m_currentCategory.supportsNegative && m_currentDisplay.front() == L'-') if (!m_currentCategory.supportsNegative && m_currentDisplay.front() == L'-')
@ -156,8 +142,11 @@ Category UnitConverter::GetCurrentCategory()
/// <param name="toType">Unit struct we are converting to</param> /// <param name="toType">Unit struct we are converting to</param>
void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType)
{ {
if (CheckLoad()) if (!CheckLoad())
{ {
return;
}
m_fromType = fromType; m_fromType = fromType;
m_toType = toType; m_toType = toType;
Calculate(); Calculate();
@ -165,7 +154,6 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType
UpdateCurrencySymbols(); UpdateCurrencySymbols();
UpdateViewModel(); UpdateViewModel();
} }
}
/// <summary> /// <summary>
/// Switches the active field, indicating that we are now entering data into /// Switches the active field, indicating that we are now entering data into
@ -181,8 +169,11 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType
/// </param> /// </param>
void UnitConverter::SwitchActive(const wstring& newValue) void UnitConverter::SwitchActive(const wstring& newValue)
{ {
if (CheckLoad()) if (!CheckLoad())
{ {
return;
}
swap(m_fromType, m_toType); swap(m_fromType, m_toType);
swap(m_currentHasDecimal, m_returnHasDecimal); swap(m_currentHasDecimal, m_returnHasDecimal);
m_returnDisplay = m_currentDisplay; m_returnDisplay = m_currentDisplay;
@ -198,7 +189,6 @@ void UnitConverter::SwitchActive(const wstring& newValue)
m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second); m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second);
} }
} }
}
wstring UnitConverter::CategoryToString(const Category& c, const wchar_t * delimiter) wstring UnitConverter::CategoryToString(const Category& c, const wchar_t * delimiter)
{ {
@ -291,8 +281,11 @@ wstring UnitConverter::ConversionDataToString(ConversionData d, const wchar_t *
/// </summary> /// </summary>
wstring UnitConverter::Serialize() wstring UnitConverter::Serialize()
{ {
if (CheckLoad()) if (!CheckLoad())
{ {
return wstring();
}
wstringstream out(wstringstream::out); wstringstream out(wstringstream::out);
const wchar_t * delimiter = L";"; const wchar_t * delimiter = L";";
@ -336,11 +329,6 @@ wstring UnitConverter::Serialize()
wstring test = out.str(); wstring test = out.str();
return test; return test;
} }
else
{
return wstring();
}
}
/// <summary> /// <summary>
/// De-Serializes the data in the converter from a string /// De-Serializes the data in the converter from a string
@ -349,8 +337,12 @@ wstring UnitConverter::Serialize()
void UnitConverter::DeSerialize(const wstring& serializedData) void UnitConverter::DeSerialize(const wstring& serializedData)
{ {
Reset(); Reset();
if (!serializedData.empty())
if (serializedData.empty())
{ {
return;
}
vector<wstring> outerTokens = StringToVector(serializedData, L"|"); vector<wstring> outerTokens = StringToVector(serializedData, L"|");
assert(outerTokens.size() == EXPECTEDSERIALIZEDTOKENCOUNT); assert(outerTokens.size() == EXPECTEDSERIALIZEDTOKENCOUNT);
m_fromType = StringToUnit(outerTokens[0]); m_fromType = StringToUnit(outerTokens[0]);
@ -398,7 +390,6 @@ void UnitConverter::DeSerialize(const wstring& serializedData)
} }
UpdateViewModel(); UpdateViewModel();
} }
}
/// <summary> /// <summary>
/// De-Serializes the data in the converter from a string /// De-Serializes the data in the converter from a string
@ -406,8 +397,11 @@ void UnitConverter::DeSerialize(const wstring& serializedData)
/// <param name="userPreferences">wstring holding the serialized data. If it does not have expected number of parameters, we will ignore it</param> /// <param name="userPreferences">wstring holding the serialized data. If it does not have expected number of parameters, we will ignore it</param>
void UnitConverter::RestoreUserPreferences(const wstring& userPreferences) void UnitConverter::RestoreUserPreferences(const wstring& userPreferences)
{ {
if (!userPreferences.empty()) if (userPreferences.empty())
{ {
return;
}
vector<wstring> outerTokens = StringToVector(userPreferences, L"|"); vector<wstring> outerTokens = StringToVector(userPreferences, L"|");
if (outerTokens.size() == 3) if (outerTokens.size() == 3)
{ {
@ -416,7 +410,6 @@ void UnitConverter::RestoreUserPreferences(const wstring& userPreferences)
m_currentCategory = StringToCategory(outerTokens[2]); m_currentCategory = StringToCategory(outerTokens[2]);
} }
} }
}
/// <summary> /// <summary>
/// Serializes the Category and Associated Units in the converter and returns it as a string /// Serializes the Category and Associated Units in the converter and returns it as a string
@ -503,8 +496,11 @@ wstring UnitConverter::Unquote(const wstring& s)
/// <param name="command">Command enum representing the command that was entered</param> /// <param name="command">Command enum representing the command that was entered</param>
void UnitConverter::SendCommand(Command command) void UnitConverter::SendCommand(Command command)
{ {
if (CheckLoad()) if (!CheckLoad())
{ {
return;
}
// TODO: Localization of characters // TODO: Localization of characters
bool clearFront = false; bool clearFront = false;
if (m_currentDisplay == L"0") if (m_currentDisplay == L"0")
@ -641,7 +637,6 @@ void UnitConverter::SendCommand(Command command)
UpdateViewModel(); UpdateViewModel();
} }
}
/// <summary> /// <summary>
/// Sets the callback interface to send display update calls to /// Sets the callback interface to send display update calls to
@ -844,8 +839,11 @@ void UnitConverter::Reset()
ClearValues(); ClearValues();
m_switchedActive = false; m_switchedActive = false;
if (!m_categories.empty()) if (m_categories.empty())
{ {
return;
}
m_currentCategory = m_categories[0]; m_currentCategory = m_categories[0];
m_categoryToUnits.clear(); m_categoryToUnits.clear();
@ -885,7 +883,6 @@ void UnitConverter::Reset()
InitializeSelectedUnits(); InitializeSelectedUnits();
Calculate(); Calculate();
} }
}
/// <summary> /// <summary>
/// Sets the active data loader based on the input category. /// Sets the active data loader based on the input category.
@ -1029,8 +1026,11 @@ void UnitConverter::Calculate()
/// <param name="input">wstring to trim</param> /// <param name="input">wstring to trim</param>
void UnitConverter::TrimString(wstring& returnString) void UnitConverter::TrimString(wstring& returnString)
{ {
if (returnString.find(L'.') != m_returnDisplay.npos) if (returnString.find(L'.') == m_returnDisplay.npos)
{ {
return;
}
wstring::iterator iter; wstring::iterator iter;
for (iter = returnString.end() - 1; ;iter--) for (iter = returnString.end() - 1; ;iter--)
{ {
@ -1045,7 +1045,6 @@ void UnitConverter::TrimString(wstring& returnString)
returnString.erase(returnString.end()-1, returnString.end()); returnString.erase(returnString.end()-1, returnString.end());
} }
} }
}
/// <summary> /// <summary>
/// Rounds the given double to the given number of significant digits /// Rounds the given double to the given number of significant digits

View file

@ -12,6 +12,7 @@ using namespace CalculatorApp::Common::DateCalculation;
DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier) DateCalculationEngine::DateCalculationEngine(_In_ String^ calendarIdentifier)
{ {
m_calendar = ref new Calendar(); m_calendar = ref new Calendar();
m_calendar->ChangeTimeZone("UTC");
m_calendar->ChangeCalendarSystem(calendarIdentifier); m_calendar->ChangeCalendarSystem(calendarIdentifier);
} }

View file

@ -43,11 +43,7 @@ DateCalculatorViewModel::DateCalculatorViewModel() :
m_StrDateDiffResultAutomationName(L""), m_StrDateDiffResultAutomationName(L""),
m_StrDateDiffResultInDays(L""), m_StrDateDiffResultInDays(L""),
m_StrDateResult(L""), m_StrDateResult(L""),
m_StrDateResultAutomationName(L""), m_StrDateResultAutomationName(L"")
m_fromDate({ 0 }),
m_toDate({ 0 }),
m_startDate({ 0 }),
m_dateResult({ 0 })
{ {
const auto& localizationSettings = LocalizationSettings::GetInstance(); const auto& localizationSettings = LocalizationSettings::GetInstance();
@ -56,19 +52,19 @@ DateCalculatorViewModel::DateCalculatorViewModel() :
// Initialize Date Calc engine // Initialize Date Calc engine
m_dateCalcEngine = make_shared<DateCalculationEngine>(localizationSettings.GetCalendarIdentifier()); m_dateCalcEngine = make_shared<DateCalculationEngine>(localizationSettings.GetCalendarIdentifier());
// Initialize dates of DatePicker controls to today's date // Initialize dates of DatePicker controls to today's date
auto calendar = ref new Calendar(); auto calendar = ref new Calendar();
// We force the timezone to UTC, in order to avoid being affected by Daylight Saving Time
// when we calculate the difference between 2 dates.
calendar->ChangeTimeZone("UTC");
auto today = calendar->GetDateTime(); auto today = calendar->GetDateTime();
// FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC) // FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC)
m_fromDate = today; m_fromDate = ClipTime(today);
m_toDate = today; m_toDate = ClipTime(today);
FromDate = ClipTime(today);
ToDate = ClipTime(today);
// StartDate should not be clipped // StartDate should not be clipped
StartDate = today; m_startDate = today;
m_dateResult = today; m_dateResult = today;
// Initialize the list separator delimiter appended with a space at the end, e.g. ", " // Initialize the list separator delimiter appended with a space at the end, e.g. ", "
@ -86,15 +82,6 @@ DateCalculatorViewModel::DateCalculatorViewModel() :
m_offsetValues->Append(ref new String(numberStr.c_str())); m_offsetValues->Append(ref new String(numberStr.c_str()));
} }
/* In the ClipTime function, we used to change timezone to UTC before clipping the time.
The comment from the previous developers said this was done to eliminate the effects of
Daylight Savings Time. We can't think of a good reason why this change in timezone is
necessary and did find bugs related to the change, therefore, we have removed the
change. Just in case, we will see if the clipped time is ever a different day from the
original day, which would hopefully indicate the change in timezone was actually
necessary. We will collect telemetry if we find this case. If we don't see any
telemetry events after the application has been used for some time, we will feel safe
and can remove this function. */
DayOfWeek trueDayOfWeek = calendar->DayOfWeek; DayOfWeek trueDayOfWeek = calendar->DayOfWeek;
DateTime clippedTime = ClipTime(today); DateTime clippedTime = ClipTime(today);
@ -383,13 +370,14 @@ String^ DateCalculatorViewModel::GetLocalizedNumberString(int value) const
return ref new String(numberStr.c_str()); return ref new String(numberStr.c_str());
} }
// Adjusts the given DateTime to 12AM of the same day // Adjusts the given DateTime to 12AM (UTC) of the same day
DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime) DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime)
{ {
auto calendar = ref new Calendar(); auto calendar = ref new Calendar();
calendar->ChangeTimeZone("UTC");
calendar->SetDateTime(dateTime); calendar->SetDateTime(dateTime);
calendar->Period = 1; calendar->Period = calendar->FirstPeriodInThisDay;
calendar->Hour = 12; calendar->Hour = calendar->FirstHourInThisPeriod;
calendar->Minute = 0; calendar->Minute = 0;
calendar->Second = 0; calendar->Second = 0;
calendar->Nanosecond = 0; calendar->Nanosecond = 0;

View file

@ -23,7 +23,7 @@ namespace CalculatorApp
// Input Properties // Input Properties
OBSERVABLE_PROPERTY_RW(bool, IsDateDiffMode); OBSERVABLE_PROPERTY_RW(bool, IsDateDiffMode);
OBSERVABLE_PROPERTY_RW(bool, IsAddMode); OBSERVABLE_PROPERTY_RW(bool, IsAddMode);
OBSERVABLE_PROPERTY_RW(bool, IsDiffInDays); // If diff is only in days or the dates are the same, OBSERVABLE_PROPERTY_R(bool, IsDiffInDays); // If diff is only in days or the dates are the same,
// then show only one result and avoid redundancy // then show only one result and avoid redundancy
OBSERVABLE_PROPERTY_RW(int, DaysOffset); OBSERVABLE_PROPERTY_RW(int, DaysOffset);
@ -82,11 +82,11 @@ namespace CalculatorApp
} }
// Output Properties // Output Properties
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResult); OBSERVABLE_PROPERTY_R(Platform::String^, StrDateDiffResult);
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultAutomationName); OBSERVABLE_PROPERTY_R(Platform::String^, StrDateDiffResultAutomationName);
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateDiffResultInDays); OBSERVABLE_PROPERTY_R(Platform::String^, StrDateDiffResultInDays);
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResult); OBSERVABLE_PROPERTY_R(Platform::String^, StrDateResult);
OBSERVABLE_PROPERTY_RW(Platform::String^, StrDateResultAutomationName); OBSERVABLE_PROPERTY_R(Platform::String^, StrDateResultAutomationName);
COMMAND_FOR_METHOD(CopyCommand, DateCalculatorViewModel::OnCopyCommand); COMMAND_FOR_METHOD(CopyCommand, DateCalculatorViewModel::OnCopyCommand);
@ -104,8 +104,6 @@ namespace CalculatorApp
Platform::String^ GetLocalizedNumberString(int value) const; Platform::String^ GetLocalizedNumberString(int value) const;
static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime); static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime);
static void CheckClipTimeSameDay(Windows::Globalization::Calendar^ reference);
property bool IsOutOfBound property bool IsOutOfBound
{ {
bool get() { return m_isOutOfBound; } bool get() { return m_isOutOfBound; }

View file

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. // Licensed under the MIT License.
#include "pch.h" #include "pch.h"
@ -53,6 +53,8 @@ constexpr size_t SELECTED_TARGET_UNIT = 2;
// x millisecond delay before we consider conversion to be final // x millisecond delay before we consider conversion to be final
constexpr unsigned int CONVERSION_FINALIZED_DELAY_IN_MS = 1000; constexpr unsigned int CONVERSION_FINALIZED_DELAY_IN_MS = 1000;
const wregex regexTrimSpacesStart = wregex(L"^\\s+");
const wregex regexTrimSpacesEnd = wregex(L"\\s+$");
namespace CalculatorApp::ViewModel namespace CalculatorApp::ViewModel
{ {
@ -202,13 +204,11 @@ void UnitConverterViewModel::BuildUnitList(const vector<UCM::Unit>& modelUnitLis
m_Units->Clear(); m_Units->Clear();
for (const UCM::Unit& modelUnit : modelUnitList) for (const UCM::Unit& modelUnit : modelUnitList)
{ {
if (modelUnit.isWhimsical) if (!modelUnit.isWhimsical)
{ {
continue;
}
m_Units->Append(ref new Unit(modelUnit)); m_Units->Append(ref new Unit(modelUnit));
} }
}
if (m_Units->Size == 0) if (m_Units->Size == 0)
{ {
@ -348,10 +348,15 @@ String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& str
if (pos != wstring::npos) if (pos != wstring::npos)
{ {
currencyResult.erase(pos, currencyCode.length()); currencyResult.erase(pos, currencyCode.length());
pos = currencyResult.find(L'\u00a0'); // non-breaking space std::wsmatch sm;
if (pos != wstring::npos) if (regex_search(currencyResult, sm, regexTrimSpacesStart))
{ {
currencyResult.erase(pos, 1); currencyResult.erase(sm.prefix().length(), sm.length());
}
if (regex_search(currencyResult, sm, regexTrimSpacesEnd))
{
currencyResult.erase(sm.prefix().length(), sm.length());
} }
} }
@ -643,11 +648,9 @@ String^ UnitConverterViewModel::Serialize()
String^ serializedData = ref new String(wstring(out.str()).c_str()); String^ serializedData = ref new String(wstring(out.str()).c_str());
return serializedData; return serializedData;
} }
else
{
return nullptr; return nullptr;
} }
}
void UnitConverterViewModel::Deserialize(Platform::String^ state) void UnitConverterViewModel::Deserialize(Platform::String^ state)
{ {
@ -879,14 +882,11 @@ void UnitConverterViewModel::UpdateInputBlocked(_In_ const wstring& currencyInpu
{ {
// currencyInput is in en-US and has the default decimal separator, so this is safe to do. // currencyInput is in en-US and has the default decimal separator, so this is safe to do.
auto posOfDecimal = currencyInput.find(L'.'); auto posOfDecimal = currencyInput.find(L'.');
m_isInputBlocked = false;
if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory) if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory)
{ {
m_isInputBlocked = (posOfDecimal + static_cast<size_t>(m_currencyMaxFractionDigits) + 1 == currencyInput.length()); m_isInputBlocked = (posOfDecimal + static_cast<size_t>(m_currencyMaxFractionDigits) + 1 == currencyInput.length());
} }
else
{
m_isInputBlocked = false;
}
} }
NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId( NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId(
@ -988,7 +988,7 @@ void UnitConverterViewModel::OnPaste(String^ stringToPaste, ViewMode mode)
} }
// Negate is only allowed if it's the first legal character, which is handled above. // Negate is only allowed if it's the first legal character, which is handled above.
if (NumbersAndOperatorsEnum::None != op && NumbersAndOperatorsEnum::Negate != op) if (NumbersAndOperatorsEnum::Negate != op)
{ {
UCM::Command cmd = CommandFromButtonId(op); UCM::Command cmd = CommandFromButtonId(op);
m_model->SendCommand(cmd); m_model->SendCommand(cmd);

View file

@ -515,6 +515,7 @@
<Setter Target="M5.MaxWidth" Value="80"/> <Setter Target="M5.MaxWidth" Value="80"/>
<Setter Target="memButton.(Grid.Column)" Value="5"/> <Setter Target="memButton.(Grid.Column)" Value="5"/>
<Setter Target="MemoryButton.(Grid.Column)" Value="6"/> <Setter Target="MemoryButton.(Grid.Column)" Value="6"/>
<Setter Target="HistoryButton.Visibility" Value="Collapsed"/>
</VisualState.Setters> </VisualState.Setters>
<Storyboard Completed="OnStoryboardCompleted"/> <Storyboard Completed="OnStoryboardCompleted"/>
</VisualState> </VisualState>

View file

@ -415,7 +415,7 @@ void Calculator::UpdateHistoryState()
SetChildAsHistory(); SetChildAsHistory();
HistoryButton->Visibility = ::Visibility::Collapsed; HistoryButton->Visibility = ::Visibility::Collapsed;
if (m_IsLastFlyoutHistory) if (!IsProgrammer && m_IsLastFlyoutHistory)
{ {
DockPivot->SelectedIndex = 0; DockPivot->SelectedIndex = 0;
} }
@ -522,7 +522,7 @@ void Calculator::HistoryFlyout_Closed(_In_ Object ^sender, _In_ Object ^args)
AutomationProperties::SetName(HistoryButton, m_openHistoryFlyoutAutomationName); AutomationProperties::SetName(HistoryButton, m_openHistoryFlyoutAutomationName);
m_fIsHistoryFlyoutOpen = false; m_fIsHistoryFlyoutOpen = false;
EnableControls(true); EnableControls(true);
if (HistoryButton->IsEnabled) if (HistoryButton->IsEnabled && HistoryButton->Visibility == ::Visibility::Visible)
{ {
HistoryButton->Focus(::FocusState::Programmatic); HistoryButton->Focus(::FocusState::Programmatic);
} }

View file

@ -387,6 +387,63 @@ namespace DateCalculationUnitTests
VERIFY_IS_TRUE(StringReference(L"") != viewModel->StrDateResult); VERIFY_IS_TRUE(StringReference(L"") != viewModel->StrDateResult);
} }
TEST_METHOD(DateCalcViewModelDateDiffDaylightSavingTimeTest)
{
auto viewModel = ref new DateCalculatorViewModel();
viewModel->IsDateDiffMode = true;
VERIFY_IS_TRUE(viewModel->IsDateDiffMode);
// 29.02.2008
viewModel->FromDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].startDate);
// 31.03.2008
viewModel->ToDate = DateUtils::SystemTimeToDateTime(datetimeDifftest[5].endDate);
//// Assert for the result
VERIFY_IS_FALSE(viewModel->IsDiffInDays);
VERIFY_ARE_EQUAL(StringReference(L"31 days"), viewModel->StrDateDiffResultInDays);
VERIFY_ARE_EQUAL(StringReference(L"1 month, 2 days"), viewModel->StrDateDiffResult);
// Daylight Saving Time - Clock Forward
// 10.03.2019
SYSTEMTIME startDate;
startDate.wYear = 2019;
startDate.wMonth = 03;
startDate.wDay = 10;
startDate.wDayOfWeek = 0;
startDate.wHour = startDate.wMinute = 0;
startDate.wSecond = startDate.wMilliseconds = 0;
viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate);
// 11.03.2019
SYSTEMTIME endDate;
endDate.wYear = 2019;
endDate.wMonth = 03;
endDate.wDay = 11;
endDate.wDayOfWeek = 0;
endDate.wHour = endDate.wMinute = 0;
endDate.wSecond = endDate.wMilliseconds = 0;
viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate);
VERIFY_IS_TRUE(viewModel->IsDiffInDays);
VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult);
endDate.wDay += 6;
viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate);
VERIFY_IS_FALSE(viewModel->IsDiffInDays);
VERIFY_ARE_EQUAL(StringReference(L"1 week"), viewModel->StrDateDiffResult);
// Daylight Saving Time - Clock Backward
// 03.11.2019
startDate.wMonth = 11;
startDate.wDay = 03;
viewModel->FromDate = DateUtils::SystemTimeToDateTime(startDate);
// 04.11.2019
endDate.wMonth = 11;
endDate.wDay = 04;
viewModel->ToDate = DateUtils::SystemTimeToDateTime(endDate);
VERIFY_IS_TRUE(viewModel->IsDiffInDays);
VERIFY_ARE_EQUAL(StringReference(L"1 day"), viewModel->StrDateDiffResult);
}
TEST_METHOD(DateCalcViewModelAddTest) TEST_METHOD(DateCalcViewModelAddTest)
{ {
// TODO - MSFT 10331900, fix this test // TODO - MSFT 10331900, fix this test