Click here to Skip to main content
15,881,812 members
Articles / Desktop Programming / WTL

Form Designer

26 Jul 2021CPOL24 min read 351K   82.5K   230  
Component for adding scriptable forms capabilities to an application.
// DDVertScrollBar.cpp : Implementation of CDDVertScrollBar
//
// Author : David Shepherd
//			Copyright (c) 2003, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include ".\DDVertScrollBar.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)

/////////////////////////////////////////////////////////////////////////////
// CDDVertScrollBar

#pragma warning(push)
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
CDDVertScrollBar::CDDVertScrollBar() : 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 CDDVertScrollBar::FinalConstruct()
{
	return S_OK;
}

void CDDVertScrollBar::FinalRelease()
{
}

const TCHAR* CDDVertScrollBar::GetObjectFriendlyName()
{
	// return the object friendly name
	return _T("DDVertScrollBar");
}

HRESULT CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::OnEnabledChanged()
{
TRY
	// update the scrollbar
	// this is done indirectly by enabling the parent window
	if(IsWindow())
	{
		(void)EnableWindow(VB2B(m_Enabled));
	}
CATCH_ALL
}

void CDDVertScrollBar::OnValueChanged()
{
TRY
	// update the scrollbar
	if(m_ctlScrollBar.IsWindow())
	{
		(void)m_ctlScrollBar.SetScrollPos(SB_CTL,ScrollPosFromValue(m_Value));
	}
CATCH_ALL
}

LONG CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::NormalisedMinValue()
{
	// return the normalised minimum value
	return min(m_MinValue,m_MaxValue);
}

LONG CDDVertScrollBar::NormalisedMaxValue()
{
	// return the normalised maximum value
	return max(m_MinValue,m_MaxValue);
}

BOOL CDDVertScrollBar::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 CDDVertScrollBar::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_VERT)==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 CDDVertScrollBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	// generated by the ATL control wizard
	LRESULT lRes = CComControl<CDDVertScrollBar>::OnSetFocus(uMsg, wParam, lParam, bHandled);
	if (m_bInPlaceActive)
	{
		if(!IsChild(::GetFocus()))
			m_ctlScrollBar.SetFocus();
	}
	return lRes;
}

LRESULT CDDVertScrollBar::OnVScroll(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_TOP:
		Value=NormalisedMinValue();
		break;
	case SB_BOTTOM:
		Value=NormalisedMaxValue();
		break;
	case SB_LINEUP:
		Value-=m_SmallStep;
		break;
	case SB_LINEDOWN:
		Value+=m_SmallStep;
		break;
	case SB_PAGEUP:
		Value-=m_LargeStep;
		break;
	case SB_PAGEDOWN:
		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 IDDVertScrollBar 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 CDDVertScrollBar::SetObjectRects(LPCRECT prcPos,LPCRECT prcClip)
{
	// generated by the ATL control wizard
	IOleInPlaceObjectWindowlessImpl<CDDVertScrollBar>::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 CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::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 CDDVertScrollBar::get__Default(LONG* pVal)
{
	// delegate to the required default property
	return get_Value(pVal);
}

STDMETHODIMP CDDVertScrollBar::put__Default(LONG newVal)
{
	// delegate to the required default property
	return put_Value(newVal);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions