// DDHorzScrollBar.cpp : Implementation of CDDHorzScrollBar
//
// Author : David Shepherd
// Copyright (c) 2003, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include ".\DDHorzScrollBar.h"
// no matter what the minimum value and maximum value properties are set to
// the scrollbar will always have a fixed range from zero to SCROLL_STEPS
#define SCROLL_STEPS 10000
// minimum allowed value
#define MIN_ALLOWED_VALUE (-MAX_ALLOWED_VALUE)
// maximum allowed value
#define MAX_ALLOWED_VALUE (1000000)
/////////////////////////////////////////////////////////////////////////////
// CDDHorzScrollBar
#pragma warning(push)
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
CDDHorzScrollBar::CDDHorzScrollBar() : m_ctlScrollBar(_T("ScrollBar"), this, 1)
#pragma warning(pop)
{
// initialise everything
m_bWindowOnly=TRUE;
m_Reserved0=0;
m_Reserved1=0;
m_Enabled=VARIANT_TRUE;
m_MinValue=0;
m_MaxValue=100;
m_SmallStep=1;
m_LargeStep=10;
m_Value=0;
}
HRESULT CDDHorzScrollBar::FinalConstruct()
{
return S_OK;
}
void CDDHorzScrollBar::FinalRelease()
{
}
const TCHAR* CDDHorzScrollBar::GetObjectFriendlyName()
{
// return the object friendly name
return _T("DDHorzScrollBar");
}
HRESULT CDDHorzScrollBar::OnSmallStepChanging(LONG newVal)
{
IMP_BEGIN
// check the new value is valid
if(newVal < 0 or newVal > MAX_ALLOWED_VALUE-MIN_ALLOWED_VALUE)
{
throw CHResult(E_INVALIDARG);
}
IMP_END
return RetVal;
}
HRESULT CDDHorzScrollBar::OnLargeStepChanging(LONG newVal)
{
IMP_BEGIN
// check the new value is valid
if(newVal < 0 or newVal > MAX_ALLOWED_VALUE-MIN_ALLOWED_VALUE)
{
throw CHResult(E_INVALIDARG);
}
IMP_END
return RetVal;
}
HRESULT CDDHorzScrollBar::OnValueChanging(LONG newVal)
{
IMP_BEGIN
// check the new value is valid
if(newVal < NormalisedMinValue() or newVal > NormalisedMaxValue())
{
throw CHResult(E_INVALIDARG);
}
IMP_END
return RetVal;
}
void CDDHorzScrollBar::OnEnabledChanged()
{
TRY
// update the scrollbar
// this is done indirectly by enabling the parent window
if(IsWindow())
{
(void)EnableWindow(VB2B(m_Enabled));
}
CATCH_ALL
}
void CDDHorzScrollBar::OnValueChanged()
{
TRY
// update the scrollbar
if(m_ctlScrollBar.IsWindow())
{
(void)m_ctlScrollBar.SetScrollPos(SB_CTL,ScrollPosFromValue(m_Value));
}
CATCH_ALL
}
LONG CDDHorzScrollBar::ValueFromScrollPos(LONG Pos)
{
// return the value for the passed scroll position
// formula : min+((pos/steps)*(max-min))
float Value=(float)Pos/SCROLL_STEPS;
Value*=NormalisedMaxValue()-NormalisedMinValue();
Value+=NormalisedMinValue();
return (LONG)Value;
}
LONG CDDHorzScrollBar::ScrollPosFromValue(LONG Value)
{
// return the scroll position for the passed value
// formula : steps*((val-min)/(max-min))
if(NormalisedMinValue()==NormalisedMaxValue())
{
return 0; // special case to prevent divide by zero
}
float Pos=(float)Value-NormalisedMinValue();
Pos/=NormalisedMaxValue()-NormalisedMinValue();
Pos*=SCROLL_STEPS;
return (LONG)Pos;
}
LONG CDDHorzScrollBar::NormalisedMinValue()
{
// return the normalised minimum value
return min(m_MinValue,m_MaxValue);
}
LONG CDDHorzScrollBar::NormalisedMaxValue()
{
// return the normalised maximum value
return max(m_MinValue,m_MaxValue);
}
BOOL CDDHorzScrollBar::PreTranslateAccelerator(LPMSG pMsg, HRESULT& hRet)
{
// this will be set TRUE if the key press was handled
BOOL Handled=FALSE;
TRY
// if the scrollbar has focus
if(GetFocus()==m_ctlScrollBar and
// and this is a key down message
pMsg->message==WM_KEYDOWN)
{
// assume the key press will be handled
Handled=TRUE;
hRet=S_OK;
// get the new value
LONG Value=m_Value;
switch(pMsg->wParam)
{
case VK_HOME:
Value=NormalisedMinValue();
break;
case VK_END:
Value=NormalisedMaxValue();
break;
case VK_LEFT:
case VK_UP:
Value-=m_SmallStep;
break;
case VK_RIGHT:
case VK_DOWN:
Value+=m_SmallStep;
break;
case VK_PRIOR:
Value-=m_LargeStep;
break;
case VK_NEXT:
Value+=m_LargeStep;
break;
default:
// the key press was not handled
Handled=FALSE;
hRet=S_FALSE;
break;
}
// ensure the new value is in range
Value=min(Value,NormalisedMaxValue());
Value=max(Value,NormalisedMinValue());
// if the value is changing
if(Value!=m_Value)
{
// update the value and scroll position
if(SUCCEEDED(put_Value(Value)))
{
// fire the change event
__raise Change();
}
}
}
CATCH_ALL
return Handled;
}
LRESULT CDDHorzScrollBar::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// create the scrollbar
if(m_ctlScrollBar.Create(
*this,CRect(0,0,0,0),_T(""),WS_CHILD|WS_VISIBLE|SBS_HORZ)==NULL)
{
throw std::exception();
}
// set the scroll range
if(m_ctlScrollBar.SetScrollRange(SB_CTL,0,SCROLL_STEPS)==FALSE)
{
throw std::exception();
}
// set the scroll position
(void)m_ctlScrollBar.SetScrollPos(SB_CTL,0);
// update the scrollbar
if(!SUCCEEDED(put_Enabled(m_Enabled)))
{
throw std::exception();
}
if(!SUCCEEDED(put_Value(m_Value)))
{
throw std::exception();
}
CATCH_ALL
return Caught ? -1 : 0;
}
LRESULT CDDHorzScrollBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// generated by the ATL control wizard
LRESULT lRes = CComControl<CDDHorzScrollBar>::OnSetFocus(uMsg, wParam, lParam, bHandled);
if (m_bInPlaceActive)
{
if(!IsChild(::GetFocus()))
m_ctlScrollBar.SetFocus();
}
return lRes;
}
LRESULT CDDHorzScrollBar::OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
// get the new value
LONG Value=m_Value;
switch(LOWORD(wParam))
{
case SB_ENDSCROLL:
break;
case SB_LEFT:
Value=NormalisedMinValue();
break;
case SB_RIGHT:
Value=NormalisedMaxValue();
break;
case SB_LINELEFT:
Value-=m_SmallStep;
break;
case SB_LINERIGHT:
Value+=m_SmallStep;
break;
case SB_PAGELEFT:
Value-=m_LargeStep;
break;
case SB_PAGERIGHT:
Value+=m_LargeStep;
break;
case SB_THUMBPOSITION:
break;
case SB_THUMBTRACK:
Value=ValueFromScrollPos(HIWORD(wParam));
break;
default:
ATLASSERT(FALSE);
break;
}
// ensure the new value is in range
Value=min(Value,NormalisedMaxValue());
Value=max(Value,NormalisedMinValue());
// if the value is changing
if(Value!=m_Value)
{
// update the value
// to prevent flicker this is done inline rather than delegating
// to IDDHorzScrollBar since the scroll position should not be
// updated if a thumb track is in progress
REQUEST_EDIT(DISPID_VALUE_NOTDEFAULT)
m_Value=Value;
PROPERTY_CHANGED(DISPID_VALUE_NOTDEFAULT)
// update the scroll position
if(LOWORD(wParam)!=SB_THUMBTRACK)
{
(void)m_ctlScrollBar.SetScrollPos(SB_CTL,ScrollPosFromValue(Value));
}
// fire the scroll event
if(LOWORD(wParam)==SB_THUMBTRACK)
{
__raise Scroll();
}
// fire the change event
else
{
__raise Change();
}
}
// if a thumb track has ended
if(LOWORD(wParam)==SB_THUMBPOSITION)
{
// update the scroll position
(void)m_ctlScrollBar.SetScrollPos(SB_CTL,ScrollPosFromValue(Value));
// fire the change event
__raise Change();
}
CATCH_ALL
return 0;
}
STDMETHODIMP CDDHorzScrollBar::SetObjectRects(LPCRECT prcPos,LPCRECT prcClip)
{
// generated by the ATL control wizard
IOleInPlaceObjectWindowlessImpl<CDDHorzScrollBar>::SetObjectRects(prcPos, prcClip);
int cx, cy;
cx = prcPos->right - prcPos->left;
cy = prcPos->bottom - prcPos->top;
::SetWindowPos(m_ctlScrollBar.m_hWnd, NULL, 0,
0, cx, cy, SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}
STDMETHODIMP CDDHorzScrollBar::get_Min(LONG *pVal)
{
IMP_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// get the minimum value
*pVal=m_MinValue;
IMP_END
return RetVal;
}
STDMETHODIMP CDDHorzScrollBar::put_Min(LONG newVal)
{
IMP_BEGIN
REQUEST_EDIT(DISPID_MINVALUE)
// check parameters
if(newVal < MIN_ALLOWED_VALUE or newVal > MAX_ALLOWED_VALUE)
{
throw CHResult(E_INVALIDARG);
}
// save the current minimum value
LONG OldMinValue=m_MinValue;
// set the new minimum value
m_MinValue=newVal;
// ensure the current value is valid
LONG Value=m_Value;
if(Value < NormalisedMinValue() or Value > NormalisedMaxValue())
{
// choose the closest limit
if(abs(NormalisedMinValue()-Value) < abs(NormalisedMaxValue()-Value))
{
Value=NormalisedMinValue();
}
else
{
Value=NormalisedMaxValue();
}
}
// update the value and scroll position
if(!SUCCEEDED(put_Value(Value)))
{
// restore the old minimum value
m_MinValue=OldMinValue;
throw CHResult(E_FAIL);
}
PROPERTY_CHANGED(DISPID_MINVALUE)
IMP_END
return RetVal;
}
STDMETHODIMP CDDHorzScrollBar::get_Max(LONG *pVal)
{
IMP_BEGIN
// check parameters
if(pVal==NULL)
{
throw CHResult(E_POINTER);
}
// get the maximum value
*pVal=m_MaxValue;
IMP_END
return RetVal;
}
STDMETHODIMP CDDHorzScrollBar::put_Max(LONG newVal)
{
IMP_BEGIN
REQUEST_EDIT(DISPID_MAXVALUE)
// check parameters
if(newVal < MIN_ALLOWED_VALUE or newVal > MAX_ALLOWED_VALUE)
{
throw CHResult(E_INVALIDARG);
}
// save the current maximum value
LONG OldMaxValue=m_MaxValue;
// set the new maximum value
m_MaxValue=newVal;
// ensure the current value is valid
LONG Value=m_Value;
if(Value < NormalisedMinValue() or Value > NormalisedMaxValue())
{
// choose the closest limit
if(abs(NormalisedMinValue()-Value) < abs(NormalisedMaxValue()-Value))
{
Value=NormalisedMinValue();
}
else
{
Value=NormalisedMaxValue();
}
}
// update the value and scroll position
if(!SUCCEEDED(put_Value(Value)))
{
// restore the old maximum value
m_MaxValue=OldMaxValue;
throw CHResult(E_FAIL);
}
PROPERTY_CHANGED(DISPID_MAXVALUE)
IMP_END
return RetVal;
}
STDMETHODIMP CDDHorzScrollBar::get__Default(LONG* pVal)
{
// delegate to the required default property
return get_Value(pVal);
}
STDMETHODIMP CDDHorzScrollBar::put__Default(LONG newVal)
{
// delegate to the required default property
return put_Value(newVal);
}