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
void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precision)
{
if (m_spTokens != nullptr)
if (m_spTokens == nullptr)
{
return;
}
unsigned int size;
IFT(m_spTokens->GetSize(&size));
@ -444,7 +447,6 @@ void CHistoryCollector::UpdateHistoryExpression(uint32_t radix, int32_t precisio
}
SetExpressionDisplay();
}
}
void CHistoryCollector::SetDecimalSymbol(wchar_t decimalSymbol)
{

View file

@ -490,8 +490,12 @@ namespace CalculationManager
void CalculatorManager::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);
auto memoryObjectPtr = m_currentCalculatorEngine->PersistedMemObject();
@ -506,7 +510,6 @@ namespace CalculationManager
}
this->SetMemorizedNumbersString();
}
}
/// <summary>
/// Recall the memorized number.
@ -516,12 +519,15 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{
return;
}
this->MemorizedNumberSelect(indexOfMemory);
m_currentCalculatorEngine->ProcessCommand(IDC_RECALL);
}
}
/// <summary>
/// Do the addition to the selected memory
@ -532,8 +538,12 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory)
{
SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory);
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{
return;
}
if (m_memorizedNumbers.empty())
{
this->MemorizeNumber();
@ -550,7 +560,6 @@ namespace CalculationManager
m_displayCallback->MemoryItemChanged(indexOfMemory);
}
}
void CalculatorManager::MemorizedNumberClear(_In_ unsigned int indexOfMemory)
{
@ -570,8 +579,12 @@ namespace CalculationManager
void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int 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
if (m_memorizedNumbers.empty())
{
@ -591,7 +604,6 @@ namespace CalculationManager
m_displayCallback->MemoryItemChanged(indexOfMemory);
}
}
/// <summary>
/// Clear all the memorized values
@ -613,12 +625,14 @@ namespace CalculationManager
/// <param name="indexOfMemory">Index of the target memory</param>
void CalculatorManager::MemorizedNumberSelect(_In_ unsigned int indexOfMemory)
{
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{
return;
}
auto memoryObject = m_memorizedNumbers.at(indexOfMemory);
m_currentCalculatorEngine->PersistedMemObject(memoryObject);
}
}
/// <summary>
/// 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>
void CalculatorManager::MemorizedNumberChanged(_In_ unsigned int indexOfMemory)
{
if (!(m_currentCalculatorEngine->FInErrorState()))
if (m_currentCalculatorEngine->FInErrorState())
{
return;
}
auto memoryObject = m_currentCalculatorEngine->PersistedMemObject();
if (memoryObject != nullptr)
{
m_memorizedNumbers.at(indexOfMemory) = *memoryObject;
}
}
}
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;
}
// Drop through in the 'e'-as-a-digit case
[[fallthrough]];
default:
state = machine[state][NZ];
break;
@ -646,7 +647,7 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis
break;
case LD:
pnumret->exp++;
// Fall through
[[fallthrough]];
case DD:
{
curChar = NormalizeCharDigit(curChar, radix);

View file

@ -109,22 +109,8 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i
vector<Unit>& unitVector = m_categoryToUnits[m_currentCategory];
for (unsigned int i = 0; i < unitVector.size(); i++)
{
if (unitVector[i].id == m_fromType.id)
{
unitVector[i].isConversionSource = true;
}
else
{
unitVector[i].isConversionSource = false;
}
if (unitVector[i].id == m_toType.id)
{
unitVector[i].isConversionTarget = true;
}
else
{
unitVector[i].isConversionTarget = false;
}
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'-')
@ -156,8 +142,11 @@ Category UnitConverter::GetCurrentCategory()
/// <param name="toType">Unit struct we are converting to</param>
void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType)
{
if (CheckLoad())
if (!CheckLoad())
{
return;
}
m_fromType = fromType;
m_toType = toType;
Calculate();
@ -165,7 +154,6 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType
UpdateCurrencySymbols();
UpdateViewModel();
}
}
/// <summary>
/// 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>
void UnitConverter::SwitchActive(const wstring& newValue)
{
if (CheckLoad())
if (!CheckLoad())
{
return;
}
swap(m_fromType, m_toType);
swap(m_currentHasDecimal, m_returnHasDecimal);
m_returnDisplay = m_currentDisplay;
@ -198,7 +189,6 @@ void UnitConverter::SwitchActive(const wstring& newValue)
m_vmCurrencyCallback->CurrencyRatiosCallback(currencyRatios.first, currencyRatios.second);
}
}
}
wstring UnitConverter::CategoryToString(const Category& c, const wchar_t * delimiter)
{
@ -291,8 +281,11 @@ wstring UnitConverter::ConversionDataToString(ConversionData d, const wchar_t *
/// </summary>
wstring UnitConverter::Serialize()
{
if (CheckLoad())
if (!CheckLoad())
{
return wstring();
}
wstringstream out(wstringstream::out);
const wchar_t * delimiter = L";";
@ -336,11 +329,6 @@ wstring UnitConverter::Serialize()
wstring test = out.str();
return test;
}
else
{
return wstring();
}
}
/// <summary>
/// De-Serializes the data in the converter from a string
@ -349,8 +337,12 @@ wstring UnitConverter::Serialize()
void UnitConverter::DeSerialize(const wstring& serializedData)
{
Reset();
if (!serializedData.empty())
if (serializedData.empty())
{
return;
}
vector<wstring> outerTokens = StringToVector(serializedData, L"|");
assert(outerTokens.size() == EXPECTEDSERIALIZEDTOKENCOUNT);
m_fromType = StringToUnit(outerTokens[0]);
@ -398,7 +390,6 @@ void UnitConverter::DeSerialize(const wstring& serializedData)
}
UpdateViewModel();
}
}
/// <summary>
/// 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>
void UnitConverter::RestoreUserPreferences(const wstring& userPreferences)
{
if (!userPreferences.empty())
if (userPreferences.empty())
{
return;
}
vector<wstring> outerTokens = StringToVector(userPreferences, L"|");
if (outerTokens.size() == 3)
{
@ -416,7 +410,6 @@ void UnitConverter::RestoreUserPreferences(const wstring& userPreferences)
m_currentCategory = StringToCategory(outerTokens[2]);
}
}
}
/// <summary>
/// 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>
void UnitConverter::SendCommand(Command command)
{
if (CheckLoad())
if (!CheckLoad())
{
return;
}
// TODO: Localization of characters
bool clearFront = false;
if (m_currentDisplay == L"0")
@ -641,7 +637,6 @@ void UnitConverter::SendCommand(Command command)
UpdateViewModel();
}
}
/// <summary>
/// Sets the callback interface to send display update calls to
@ -844,8 +839,11 @@ void UnitConverter::Reset()
ClearValues();
m_switchedActive = false;
if (!m_categories.empty())
if (m_categories.empty())
{
return;
}
m_currentCategory = m_categories[0];
m_categoryToUnits.clear();
@ -885,7 +883,6 @@ void UnitConverter::Reset()
InitializeSelectedUnits();
Calculate();
}
}
/// <summary>
/// Sets the active data loader based on the input category.
@ -1029,8 +1026,11 @@ void UnitConverter::Calculate()
/// <param name="input">wstring to trim</param>
void UnitConverter::TrimString(wstring& returnString)
{
if (returnString.find(L'.') != m_returnDisplay.npos)
if (returnString.find(L'.') == m_returnDisplay.npos)
{
return;
}
wstring::iterator iter;
for (iter = returnString.end() - 1; ;iter--)
{
@ -1045,7 +1045,6 @@ void UnitConverter::TrimString(wstring& returnString)
returnString.erase(returnString.end()-1, returnString.end());
}
}
}
/// <summary>
/// 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)
{
m_calendar = ref new Calendar();
m_calendar->ChangeTimeZone("UTC");
m_calendar->ChangeCalendarSystem(calendarIdentifier);
}

View file

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

View file

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

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.
#include "pch.h"
@ -53,6 +53,8 @@ constexpr size_t SELECTED_TARGET_UNIT = 2;
// x millisecond delay before we consider conversion to be final
constexpr unsigned int CONVERSION_FINALIZED_DELAY_IN_MS = 1000;
const wregex regexTrimSpacesStart = wregex(L"^\\s+");
const wregex regexTrimSpacesEnd = wregex(L"\\s+$");
namespace CalculatorApp::ViewModel
{
@ -202,13 +204,11 @@ void UnitConverterViewModel::BuildUnitList(const vector<UCM::Unit>& modelUnitLis
m_Units->Clear();
for (const UCM::Unit& modelUnit : modelUnitList)
{
if (modelUnit.isWhimsical)
if (!modelUnit.isWhimsical)
{
continue;
}
m_Units->Append(ref new Unit(modelUnit));
}
}
if (m_Units->Size == 0)
{
@ -348,10 +348,15 @@ String^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& str
if (pos != wstring::npos)
{
currencyResult.erase(pos, currencyCode.length());
pos = currencyResult.find(L'\u00a0'); // non-breaking space
if (pos != wstring::npos)
std::wsmatch sm;
if (regex_search(currencyResult, sm, regexTrimSpacesStart))
{
currencyResult.erase(pos, 1);
currencyResult.erase(sm.prefix().length(), sm.length());
}
if (regex_search(currencyResult, sm, regexTrimSpacesEnd))
{
currencyResult.erase(sm.prefix().length(), sm.length());
}
}
@ -643,11 +648,9 @@ String^ UnitConverterViewModel::Serialize()
String^ serializedData = ref new String(wstring(out.str()).c_str());
return serializedData;
}
else
{
return nullptr;
}
}
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.
auto posOfDecimal = currencyInput.find(L'.');
m_isInputBlocked = false;
if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory)
{
m_isInputBlocked = (posOfDecimal + static_cast<size_t>(m_currencyMaxFractionDigits) + 1 == currencyInput.length());
}
else
{
m_isInputBlocked = false;
}
}
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.
if (NumbersAndOperatorsEnum::None != op && NumbersAndOperatorsEnum::Negate != op)
if (NumbersAndOperatorsEnum::Negate != op)
{
UCM::Command cmd = CommandFromButtonId(op);
m_model->SendCommand(cmd);

View file

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

View file

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

View file

@ -387,6 +387,63 @@ namespace DateCalculationUnitTests
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)
{
// TODO - MSFT 10331900, fix this test