//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1992, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of TRangeValidator, integer numeric range input validator
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/validate.h>
#include <owl/validate.rh>
#include <owl/applicat.h>
#include <owl/appdict.h>
#include <owl/framewin.h>
#include <algorithm>
namespace owl {
OWL_DIAGINFO;
//
/// Constructs a range validator object by first calling the constructor inherited
/// from TFilterValidator, passing a set of characters containing the digits
/// '0'..'9' and the characters '+' and '-'. Sets Range establishing the range of
/// acceptable long integer values.
//
TRangeValidator::TRangeValidator(const TRange& range)
:
TFilterValidator(_T("0-9+-"))
{
SetRange(range);
}
//
/// Overrides TValidator's virtual function and displays a message box indicating
/// that the entered value does not fall within the specified range.
//
void
TRangeValidator::Error(TWindow* owner)
{
PRECONDITION(owner);
owner->FormatMessageBox(owner->LoadString(IDS_VALNOTINRANGE), owner->LoadString(IDS_VALCAPTION), MB_ICONERROR | MB_OK, Range.Min, Range.Max);
}
//
// Hide the internal helper functions so they are excluded from public view by
// using an unnamed namespace.
//
namespace
{
//
// Parses the given stream, expecting it to contain only a single valid integer value.
// On success, returns the converted integer value. On failure, sets the `failbit` state of the
// given stream and returns 0, unless exceptions are enabled for the stream, in which case an
// exception will be thrown (see std::ios_base::exceptions).
//
auto ParseInteger_(tistream& is) -> TRangeValidator::TExtent
{
auto value = TRangeValidator::TExtent{};
is >> value;
// Allow trailing whitespace, but no other trailing content.
//
if (!is.fail() && !is.eof())
{
is >> std::ws;
if (!is.eof())
{
value = 0;
is.setstate(is.failbit);
}
}
return value;
}
}
namespace
{
//
// Ensures that proper magnitude signs are allowed/disallowed based on the range.
//
void ValidateMagnitudeFilter_(const TRangeValidator::TRange& range, TCharSet& validChars)
{
auto minus = _T('-');
auto plus = _T('+');
auto hasMinus = validChars.Has(minus);
auto hasPlus = validChars.Has(plus);
// Add or remove minus sign as needed based on range.Min value.
//
if ((range.Min < 0) != hasMinus)
{
if (hasMinus)
validChars -= minus;
else
validChars += minus;
}
// Add or remove plus sign as needed based on range.Max value.
//
if ((range.Max > 0) != hasPlus)
{
if (hasPlus)
validChars -= plus;
else
validChars += plus;
}
}
//
// Ensures that `range.Min` is less than `range.Max`; if not, swaps the values.
// Calls ValidateMagnitudeFilter_ to ensure necessary magnitude signs are
// available characters.
//
void ValidateRange_(TRangeValidator::TRange& range, TCharSet& validChars)
{
WARN(range.Min > range.Max, _T("TRangeValidator has a minimum value of ") << range.Min
<< _T(" that is greater than the maximum value of ") << range.Max
<< _T("; swapped values will be used."));
std::sort(&range.Min, &range.Max + 1);
ValidateMagnitudeFilter_(range, validChars);
}
} // namespace
//
/// Sets the minimum number the validator can accept.
//
/// We can't validate the range here, because of the asynchronous manner in
/// which SetMin and SetMax could be called might have Range.Min greater
/// than Range.Max.
//
void TRangeValidator::SetMin(TExtent minValue)
{
Range.Min = minValue;
ValidateMagnitudeFilter_(Range, ValidChars);
}
//
/// Sets the maximum number the validator can accept.
//
/// We can't validate the range here, because of the asynchronous manner in
/// which SetMin and SetMax could be called might have Range.Min greater
/// than Range.Max.
//
void TRangeValidator::SetMax(TExtent maxValue)
{
Range.Max = maxValue;
ValidateMagnitudeFilter_(Range, ValidChars);
}
//
/// Sets the minimum and maximum number the validator can accept.
//
void TRangeValidator::SetRange(const TRange& range)
{
Range = range;
ValidateRange_(Range, ValidChars);
}
//
/// We first validate the range, because of the asynchronous manner in which SetMin
/// and SetMax could be called might have Min greater than Max.
//
/// If the given string is empty or blank (all white-space), returns true.
/// Otherwise, converts the string str into an integer number and returns true if the result
/// meets all three of these conditions:
/// - It is a valid integer number.
/// - Its value is greater than or equal to Min.
/// - Its value is less than or equal to Max.
/// If any of those tests fails, IsValid returns false.
//
bool
TRangeValidator::IsValid(LPCTSTR s)
{
PRECONDITION(s);
if (!TFilterValidator::IsValid(s))
return false;
ValidateRange_(Range, ValidChars);
auto is = tistringstream{s};
is >> std::ws;
if (is.eof())
return true; // Empty/blank input is allowed.
const auto value = ParseInteger_(is);
return !is.fail() && value >= Range.Min && value <= Range.Max;
}
//
/// Incorporates the three types, tdSizeData, tdGetData, and tdSetData, that a range
/// validator can handle for its associated edit control. The parameter str is the
/// edit control's string value, and buffer is the data passed to the edit control.
/// Depending on the value of direction, Transfer either sets str from the number in
/// buffer or sets the number at buffer to the value of the string str. If direction
/// is tdSetData, Transfer sets str from buffer. If direction is tdGetData, Transfer
/// sets buffer from str. If direction is tdSizeData, Transfer neither sets nor
/// reads data.
/// Transfer always returns the size of the data transferred.
//
uint
TRangeValidator::Transfer(tchar * s, void* buffer, TTransferDirection direction)
{
if (Options & voTransfer) {
if (!buffer && direction != tdSizeData) return 0;
if (direction == tdGetData) {
auto is = tistringstream{s};
const auto value = ParseInteger_(is);
WARN(is.fail(), _T("ParseInteger_ failed"));
*(TExtent*)buffer = value;
}
else if (direction == tdSetData) {
wsprintf(s, _T("%ld"), *(TExtent*)buffer); // need wsprintf for char *
}
return sizeof(TExtent);
}
else
return 0;
}
//
/// We first validate the range, because of the asynchronous manner in which SetMin
/// and SetMax could be called might have Min greater than Max.
//
/// Adjusts the 'value' of the text, given a cursor position and an amount. Returns
/// the actual amount adjusted.
//
int
TRangeValidator::Adjust(tstring& text, int& /*begPos*/, int& /*endPos*/, int amount)
{
auto clamp = [](TExtent e, const TRange& r)
{ return e < r.Min ? r.Min : e > r.Max ? r.Max : e; };
ValidateRange_(Range, ValidChars);
auto is = tistringstream{text};
const auto value = ParseInteger_(is);
WARN(is.fail(), _T("ParseInteger_ failed"));
const auto newValue = clamp(value + amount, Range);
text = to_tstring(newValue);
return newValue - value;
}
IMPLEMENT_STREAMABLE1(TRangeValidator, TFilterValidator);
#if OWL_PERSISTENT_STREAMS
//
/// Reads an instance of TRangeValidator from the given ipstream.
//
void*
TRangeValidator::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
ReadBaseObject((TFilterValidator*)GetObject(), is);
is >> GetObject()->Range.Min >> GetObject()->Range.Max;
return GetObject();
}
//
/// Writes the TRangeValidator to the given opstream.
//
void
TRangeValidator::Streamer::Write(opstream& os) const
{
WriteBaseObject((TFilterValidator*)GetObject(), os);
os << GetObject()->Range.Min << GetObject()->Range.Max;
}
#endif
} // OWL namespace
↑ V1004 The 'owner' pointer was used unsafely after it was verified against nullptr. Check lines: 41, 42.
↑ V522 There might be dereferencing of a potential null pointer '(TExtent *) buffer'.