Click here to Skip to main content
15,886,077 members
Articles / Web Development / HTML

A Comprehensive CE Class Library to Replace ATL and MFC

Rate me:
Please Sign up or sign in to vote.
4.48/5 (14 votes)
4 Oct 2000CPOL 278.8K   998   70  
A collection of classes for CE that do not use ATL or MFC, plus an FTP client, database viewer, and sample application that solves beam deflection equations.
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////


#include "StdAfx.h"
#include "CeDebug.h"
#include "CeWnd.h"
#include "CeMisc.h"

#include "CeArray.h"
#include "CeMgr.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000


CeChildMgr::CeChildInfo::CeChildInfo()
{
	m_hWndChild = NULL;
	m_nFlags = 0;
	::SetRectEmpty(&m_rcOriginal);
	m_sizeMin.cy = m_sizeMin.cy = 0;
	::SetRectEmpty(&m_rcMove);
	m_sizeAssocLabelDelta = CeSize(0,0);
	m_hWndLabel = NULL;
	::SetRectEmpty(&m_rcLabel);
	m_hWndSpinner = NULL;
	::SetRectEmpty(&m_rcSpinner);
};


CeChildMgr::CeChildMgr()
{
	m_hWnd = NULL;
	::SetRectEmpty(&m_rcOriginal);
	::SetRectEmpty(&m_rcLastParent);
}


CeChildMgr::~CeChildMgr()
{
}


BOOL CeChildMgr::Manage(HWND hChild, UINT nFlags)
{
	ASSERT( hChild );
	ASSERT( ::GetParent(hChild) );
	
	CeChildInfo cInfo;
	
	if (m_hWnd == NULL)
	{
		// Gather information about controlling window
		m_hWnd = ::GetParent(hChild);
		::GetClientRect(m_hWnd, &m_rcOriginal);
	}
	
	CeWnd wnd;
	wnd.Attach(m_hWnd);
	
	ASSERT( ::GetParent(hChild) == m_hWnd);
	
	// We store an hWnd rather than a CeWnd so that
	// you can call Register with a CeWnd object that may
	// only be temporary (and yet be attached to a persistant
	// hWnd).
	cInfo.m_hWndChild = hChild;
	
	// Store resize mode
	cInfo.m_nFlags = nFlags;
	
	// Store original size of rectangle for some resize operations.
	::GetWindowRect(cInfo.m_hWndChild, &cInfo.m_rcOriginal);
	wnd.ScreenToClient(cInfo.m_rcOriginal);
	
	// Hack, set to value from user or from non-client size
	CeRect rectClient;
	::GetClientRect(cInfo.m_hWndChild, &rectClient);
	cInfo.m_sizeMin.cx = cInfo.m_rcOriginal.Width() - rectClient.Width();
	cInfo.m_sizeMin.cy = cInfo.m_rcOriginal.Height() - rectClient.Height();
	
	// If AL_FIX_ASSOC_LABEL flag is on, then get previous sibling item
	// and store in cInfo the distance from right edge of associated label 
	// to left edge of pWnd.
	if(nFlags & (AL_FIX_LABEL|AL_FIX_LABEL_ABOVE))
	{
		HWND hWndLabel = ::GetWindow(cInfo.m_hWndChild, GW_HWNDPREV);
		
		// If this assertion fails, the window does not have a previous sibling.
		// Perhaps AL_FIX_LABEL shouldn't be used.
		ASSERT(hWndLabel);
		
		CeRect rectPrev;
		::GetWindowRect(hWndLabel, &rectPrev);
		wnd.ScreenToClient(rectPrev);

		cInfo.m_sizeAssocLabelDelta.cx = cInfo.m_rcOriginal.left - rectPrev.right;
		cInfo.m_sizeAssocLabelDelta.cy = rectPrev.top - cInfo.m_rcOriginal.top;
		
		cInfo.m_hWndLabel = hWndLabel;
	}

	if (nFlags & AL_FIX_SPINNER)
	{
		HWND hWndSpin = ::GetWindow(cInfo.m_hWndChild, GW_HWNDNEXT);

		ASSERT(hWndSpin);
	
		CeRect rectNext;
		::GetWindowRect(hWndSpin, &rectNext);
		wnd.ScreenToClient(rectNext);
		
		cInfo.m_sizeAssocSpinDelta.cx = rectNext.left - cInfo.m_rcOriginal.right;
		cInfo.m_sizeAssocSpinDelta.cy = rectNext.top - cInfo.m_rcOriginal.top;
		
		cInfo.m_hWndSpinner = hWndSpin;
	}

	// Add the CLayoutInfo object to our collection.
	m_arrayInfo.Add(&cInfo);
	
	wnd.Detach();

	// Return "handle" (really just index into array) as return
	// value.  Might use as parameter for future "modifier" functions.
	return m_arrayInfo.GetUpperBound();
}

//
// TODO:
// 1. don't move if it didn't move
// 2. try hiding all, moving all, showing all
//

void CeChildMgr::OnSize(UINT nType, int /*cx*/, int /*cy*/)
{
	int nMax = m_arrayInfo.GetSize();
	
	if (SIZE_MINIMIZED == nType || nMax <= 0)
		return;
	
	ASSERT(m_hWnd);
	CeWnd wnd;
	wnd.Attach(m_hWnd);
	
	CeRect rectParent;
	::GetClientRect(m_hWnd, &rectParent);

	// Walk all controls in collection.
	for (int ii = 0; ii < nMax; ii++)
	{
		CeChildInfo *pInfo = &(m_arrayInfo[ii]);
		
		// validate hWnd
		if (! ::IsWindow(pInfo->m_hWndChild))
		{
			ASSERT(0);
			// mark for removal instead of error condition?
			continue;
		}
		
		// get current location
		CeRect rectWnd;
		::GetWindowRect(pInfo->m_hWndChild, rectWnd);
		wnd.ScreenToClient(rectWnd);
		
		// get the new size
		CeSize sizeNew = CalcSize(pInfo, rectParent);
		
		//
		// Handle X positioning
		//
		
		if (pInfo->m_nFlags & (AL_ANCHOR_RIGHT | AL_PROP_ADJUST_HORZPOS))
		{
			// Original distance from right edge of parent to control.
			int nDistToRight = m_rcOriginal.right - pInfo->m_rcOriginal.right;
			
			if (pInfo->m_nFlags & AL_ANCHOR_RIGHT)
			{
				// Preserve distance to right edge of parent.
				rectWnd.right = rectParent.right - nDistToRight;
			}
			else // if(pInfo->dwLayoutMode & AL_PROP_ADJUST_HORZPOS)
			{
				// To find the proportionally scaled position, we calculate where the original
				// top left point would lie in scaled coordinate frame.
				// (We use top left instead of center so that controls of differing sizes
				// still move together...)
				
				CePoint ptOrigTopLeft = pInfo->m_rcOriginal.TopLeft();            
				

				CePoint ptNewTopLeft(
					(ptOrigTopLeft.x * rectParent.Width()) / m_rcOriginal.Width(),
					(ptOrigTopLeft.y * rectParent.Height()) / m_rcOriginal.Height()
					);
//					::MulDiv(ptOrigTopLeft.x, rectParent.Width(), m_rcOriginal.Width()),
//					::MulDiv(ptOrigTopLeft.y, RectHeight(rectParent), RectHeight(m_rcOriginal)) 
				
				// New right edge is the new left edge plus new width.
				rectWnd.right = ptNewTopLeft.x + sizeNew.cx;
			}
			
			// Establish left edge.
			rectWnd.left = rectWnd.right - sizeNew.cx;
		}
		else // if(pInfo->dwLayoutMode & AL_ANCHOR_LEFT)
		{
			// Simple - new right is old left plus new width.
			rectWnd.right = rectWnd.left + sizeNew.cx;
		}
		
		//
		// Handle Y positioning
		//
		
		if (pInfo->m_nFlags & (AL_ANCHOR_BOTTOM | AL_PROP_ADJUST_VERTPOS))
		{
			// Original distance from bottom of parent to bottom of control.
			int nDistToBottom = m_rcOriginal.bottom - pInfo->m_rcOriginal.bottom;
			
			if(pInfo->m_nFlags & AL_ANCHOR_BOTTOM)
			{
				// Preserve distance to bottom edge of parent.              
				rectWnd.bottom = rectParent.bottom - nDistToBottom;
			}
			else  // if(pInfo->m_nFlags & AL_PROP_ADJUST_VERTPOS)
			{
				// To find the proportionally scaled position, we calculate where the original
				// top left point would lie in scaled coordinate frame.
				// (We use top left instead of center so that controls of differing sizes
				// still move together...)
				
				CePoint ptOrigTopLeft = pInfo->m_rcOriginal.TopLeft();            
				
				CePoint ptNewTopLeft(
					(ptOrigTopLeft.x * rectParent.Width()) / m_rcOriginal.Width(),
					(ptOrigTopLeft.y * rectParent.Height()) / m_rcOriginal.Height()
					);
				
				// New bottom edge is the new top edge plus new width.
				rectWnd.bottom = ptNewTopLeft.y + sizeNew.cy;
			}
			
			// Establish top edge.
			rectWnd.top = rectWnd.bottom - sizeNew.cy;
			if (rectWnd.top > rectWnd.bottom)
				rectWnd.top = rectWnd.bottom;
		}
		else // if(pInfo->m_nFlags & AL_ANCHOR_TOP)
		{
			// Simple = new bottom is old top plus new height.
			rectWnd.bottom = rectWnd.top + sizeNew.cy;
			if (rectWnd.bottom < rectWnd.top)
				rectWnd.bottom = rectWnd.top;
		}
		
		// If AL_FIX_LABEL flag set, then adjust previous sibling (e.g. a text label)
		// to have same distance from this window as it did in the beginning...
		// (i.e. label's right edge to this window's left edge).
		if (pInfo->m_nFlags & (AL_FIX_LABEL|AL_FIX_LABEL_ABOVE))
		{
			CeRect rectAssoc;
			::GetWindowRect(pInfo->m_hWndLabel, rectAssoc);
			
			int nAssocWidth = rectAssoc.Width();
			rectAssoc.right  = rectWnd.left - pInfo->m_sizeAssocLabelDelta.cx;
			rectAssoc.left   = rectAssoc.right - nAssocWidth;
			
			int nAssocHeight = RectHeight(rectAssoc);
			rectAssoc.top    = rectWnd.top + pInfo->m_sizeAssocLabelDelta.cy;
			rectAssoc.bottom = rectAssoc.top + nAssocHeight;
			
			pInfo->m_rcLabel = rectAssoc;
		}

		// If AL_FIX_SPINNER flag set, then adjust spinner sibling
		// to have same distance from this window as it did in the beginning...
		// (i.e. spinner's left edge to this window's right edge).
		if (pInfo->m_nFlags & AL_FIX_SPINNER)
		{
			CeRect rectAssoc;
			::GetWindowRect(pInfo->m_hWndSpinner, rectAssoc);
			
			int nAssocWidth  = rectAssoc.Width();
			rectAssoc.left   = rectWnd.right + pInfo->m_sizeAssocSpinDelta.cx;
			rectAssoc.right  = rectAssoc.left + nAssocWidth;

			int nAssocHeight = RectHeight(rectAssoc);
			rectAssoc.top    = rectWnd.top + pInfo->m_sizeAssocSpinDelta.cy;
			rectAssoc.bottom = rectAssoc.top + nAssocHeight;
			
			pInfo->m_rcSpinner = rectAssoc;
		}

		
		if (RectHeight(rectWnd) < pInfo->m_sizeMin.cy &&
			rectWnd.Width()  < pInfo->m_sizeMin.cx)
		{
			// move nothing, we just can't right now
			return;
		}
		
		pInfo->m_rcMove = rectWnd;
	}

	// Preserve for next time through.
	m_rcLastParent = rectParent;

	if (! DetectCollision())
	{

#if !defined(_WIN32_WCE)
		// Preallocate the number of windows
		// it will increase if the number includes labels
		HDWP hDWP = ::BeginDeferWindowPos(nMax);

		for (ii = 0; ii < nMax; ii++)
		{
			CeChildInfo* pInfo= &(m_arrayInfo[ii]);
			
			hDWP = ::DeferWindowPos(hDWP,
				pInfo->m_hWndChild, NULL,
				pInfo->m_rcMove.left, pInfo->m_rcMove.top,
				pInfo->m_rcMove.Width(), RectHeight(pInfo->m_rcMove),
				SWP_NOZORDER);

			if (pInfo->m_nFlags & (AL_FIX_LABEL|AL_FIX_LABEL_ABOVE))
				hDWP = ::DeferWindowPos(hDWP,
					pInfo->m_hWndLabel, NULL,
					pInfo->m_rcLabel.left, pInfo->m_rcLabel.top,
					pInfo->m_rcLabel.Width(), RectHeight(pInfo->m_rcLabel),
					SWP_NOZORDER);

			if (pInfo->m_nFlags & AL_FIX_SPINNER)
				hDWP = ::DeferWindowPos(hDWP,
					pInfo->m_hWndSpinner, NULL,
					pInfo->m_rcSpinner.left, pInfo->m_rcSpinner.top,
					pInfo->m_rcSpinner.Width(), RectHeight(pInfo->m_rcSpinner),
					SWP_NOZORDER);
		}

		::EndDeferWindowPos(hDWP);

#else  // _WIN32_WCE

		// No violated contraints on positioning
		// move all the windows to their new places
		
		for (ii = 0; ii < nMax; ii++)
		{
			CeChildInfo* pInfo= &(m_arrayInfo[ii]);
			
			::MoveWindow(pInfo->m_hWndChild,
				pInfo->m_rcMove.left, pInfo->m_rcMove.top,
				pInfo->m_rcMove.Width(), RectHeight(pInfo->m_rcMove),
				TRUE);
			
			if (pInfo->m_nFlags & (AL_FIX_LABEL|AL_FIX_LABEL_ABOVE))
				::MoveWindow(pInfo->m_hWndLabel,
				pInfo->m_rcLabel.left, pInfo->m_rcLabel.top,
				pInfo->m_rcLabel.Width(), RectHeight(pInfo->m_rcLabel),
				TRUE);

			if (pInfo->m_nFlags & AL_FIX_SPINNER)
				::MoveWindow(pInfo->m_hWndSpinner,
				pInfo->m_rcSpinner.left, pInfo->m_rcSpinner.top,
				pInfo->m_rcSpinner.Width(), RectHeight(pInfo->m_rcSpinner),
				TRUE);
		}

		// Another pass for those controls that don't handle
		// being on top of one another for any amount of time
		for (ii = 0; ii < nMax; ii++)
		{
			CeChildInfo* pInfo= &(m_arrayInfo[ii]);

			::InvalidateRect(pInfo->m_hWndChild, NULL, TRUE);
			if (pInfo->m_nFlags & AL_FIX_LABEL)
				::InvalidateRect(pInfo->m_hWndLabel, NULL, TRUE);
			if (pInfo->m_nFlags & AL_FIX_SPINNER)
				::InvalidateRect(pInfo->m_hWndSpinner, NULL, TRUE);
		}
#endif
	}

	wnd.Detach();
}


SIZE CeChildMgr::CalcSize(CeChildInfo *pInfo, CeRect &rcParent)
{
	// Default to no change
	SIZE sizeRet;
	
	sizeRet.cx = pInfo->m_rcOriginal.Width();
	sizeRet.cy = pInfo->m_rcOriginal.Height();
	
	if (pInfo->m_nFlags & AL_ADJUST_WIDTH)
	{
		// Adjust width by same as parent has changed
		sizeRet.cx += rcParent.Width() - m_rcOriginal.Width();
	}
	else if (pInfo->m_nFlags & AL_PROP_ADJUST_WIDTH)
	{
		// Adjust width proportionally to parent change.
		// (orig wnd width/orig parent width)*(new parent width) = new wnd width
		// new left = new right - new wnd width.
		sizeRet.cx = (pInfo->m_rcOriginal.Width() * rcParent.Width()) / m_rcOriginal.Width();
	}
	
	if (pInfo->m_nFlags & AL_ADJUST_HEIGHT)
	{
		// Adjust height by same as parent change.
		sizeRet.cy += rcParent.Height() - m_rcOriginal.Height();
	}
	else if (pInfo->m_nFlags & AL_PROP_ADJUST_HEIGHT)
	{
		// Adjust height proportionally to parent change.
		// (orig wnd height/orig parent height)*(new parent height) = new wnd height
		// new top = new bottom - new wnd height.
		sizeRet.cy = (pInfo->m_rcOriginal.Height() * rcParent.Height()) / m_rcOriginal.Height();
	}
	
	return sizeRet;
}

/*
void CeChildMgr::MsgBoxCurrentSize(CeWnd *pWnd)
{
	CeRect rect;
	pWnd->GetWindowRect(rect);
	
	long baseunits = ::GetDialogBaseUnits();
	int baseunitX  = LOWORD(baseunits);
	int baseunitY  = HIWORD(baseunits);
	
	int dialogunitX = (rect.Width() * 4) / baseunitX;  
	int dialogunitY = (rect.Height() * 8) / baseunitY; 
	
	CString str;
	str.Format("Pixel Width: %d\n" 
		"Pixel Height: %d\n"
		"Dialog Unit Width: %d\n"
		"Dialog Unit Height: %d",
		rect.Width(), rect.Height(), dialogunitX, dialogunitY);
	
	pWnd->MessageBox(str, NULL, MB_ICONINFORMATION);
}
*/

// Call this when handling WM_GETMINMAXINFO, for example:
// lpMMI->ptMinTrackSize = CAutoLayout::SizeDialogToScreen(250,210);
POINT CeChildMgr::SizeDialogToScreen(int nDialogX, int nDialogY)
{
	POINT ptRet;
	
	long baseunits = ::GetDialogBaseUnits();
	int  baseunitX = LOWORD(baseunits);
	int  baseunitY = HIWORD(baseunits);
	
	ptRet.x = (nDialogX * baseunitX) / 4;  
	ptRet.y = (nDialogY * baseunitY) / 8; 
	
	return ptRet;
}


// This works...but there is not much that you can do with it.
BOOL CeChildMgr::DetectCollision()
{
	int nMax = m_arrayInfo.GetSize();
	BOOL bCollide = FALSE;
	
	for (int ii = 0; ii < nMax && ! bCollide; ii++)
	{
		// check to see if we would go too small
		CeSize size = m_arrayInfo[ii].m_rcMove.Size();
		if (size.cx < m_arrayInfo[ii].m_sizeMin.cx ||
			size.cy < m_arrayInfo[ii].m_sizeMin.cy)
			return TRUE;
		
		for (int jj = 0; jj < nMax; jj++)
		{
			if (jj == ii)
				continue;
			
			CeRect rectIntersect;
			
			// check for collision of controls, but only 
			// if one is not contained within another
			if (rectIntersect.IntersectRect(m_arrayInfo[ii].m_rcMove, m_arrayInfo[jj].m_rcMove) && 
				rectIntersect != m_arrayInfo[ii].m_rcMove && 
				rectIntersect != m_arrayInfo[jj].m_rcMove)
				return TRUE;
		}
	}
	
	return FALSE;
}


// Offset ALL child controls by size, this function
// works for all children, not just the managed ones
void CeChildMgr::OffsetChildren(HWND hWnd, CeSize size)
{
	CeRect rcChild;

	for (HWND hChild = ::GetWindow(hWnd, GW_CHILD); hChild; hChild = ::GetNextSibling(hChild))
	{
		::GetWindowRect(hChild, &rcChild);    // screen coordinates

		//::ScreenToClient(hWnd, &rcChild);    // to parent coordinates
		::ScreenToClient(hWnd, (LPPOINT)&rcChild);
		::ScreenToClient(hWnd, ((LPPOINT)&rcChild)+1);

		size = ::OffsetRect(rcChild, size.cx, size.cy);
		::MoveWindow(hChild, rcChild.left, rcChild.top, rcChild.Width(), rcChild.Height(), TRUE);
	}
}


void CeChildMgr::ValidateChildBkgnd(HDC hDC, HWND hWnd)
{
	HRGN rgn;
	CeRect rcClip;
	TCHAR szClass[64];

	// Because of bugs in the Windows GDI in ExcludeClipRect() specifically
	// that cause the clipping box to be truncated to the size of
	// the physical device coordinates, we need to build the update region
	// by hand, we do this by creating an update region from the clipping rectangle
	// set in the DC as passed to me (which we assume us accurate at this point, which it should be
	// if somebody didn't call ExcludeClipRect() on it.
	// Then I difference out the child controls from the update region, this appears to work properly
	// in all circumstances.

	GetClipBox(hDC, rcClip);
	rgn = CreateRectRgnIndirect(rcClip);
	//SelectClipRgn(hDC, &rgn, RGN_COPY);
	SelectClipRgn(hDC, rgn);

	// Iterate through all the children and exclude them from the
	// DCs clipping rectangle
	for (HWND hChild = ::GetWindow(hWnd, GW_CHILD); hChild; hChild = ::GetNextSibling(hChild))
	{
		// skip windows that aren't visible
		if (! IsWindowVisible(hChild))
			continue;

		// Doesn't mean anything, could be that the child is disabled from input
		// but contains meaningful data
		//ASSERT(pChild->IsWindowEnabled());

		::GetClassName(hChild, szClass, 64);
		
		// skip group boxes, they rely on being hollow
		// and on the parent painting it's background
		// also
		if (_tcsicmp(szClass, _T("BUTTON")) || (GetWindowStyle(hChild) & 0xF) != BS_GROUPBOX)
		{
			CeRect rcChild;
			::GetWindowRect(hChild, &rcChild);    // screen coordinates

			//::ScreenToClient(hWnd, &rcChild);    // to parent coordinates
			::ScreenToClient(hWnd, (LPPOINT)&rcChild);
			::ScreenToClient(hWnd, ((LPPOINT)&rcChild)+1);
			
			// the old way, before I was required to use regions
			//pDC->ExcludeClipRect(rectChild);

			// exclude it from painting
			SetRectRgn(rgn, rcChild.left, rcChild.top, rcChild.right, rcChild.bottom);
			//SelectClipRgn(&hDC, rgn, RGN_DIFF);
			SelectClipRgn(hDC, rgn);
		}
	}
}



// Adjusts the parent rectangle, useful for things like
// adding dialog bars to the parents client area
// and then adjust the controls with fixed distances
// to the parents borders
void CeChildMgr::AdjustArea(const SIZE& sizeDelta, BOOL bTopLeft)
{
	int nMax = m_arrayInfo.GetSize();
	if (nMax <= 0)
		// nothing managed
		return;
	
	// expand or shrink client area managed
	m_rcOriginal.right += sizeDelta.cx;
	m_rcOriginal.bottom += sizeDelta.cy;
	
	if (!bTopLeft)
		return;
	
	// Walk all controls in collection.
	for (int ii = 0; ii < nMax; ii++)
	{
		CeChildInfo *pInfo = &(m_arrayInfo[ii]);
		if (pInfo->m_hWndChild)
			pInfo->m_rcOriginal.OffsetRect(sizeDelta);
		if (pInfo->m_hWndLabel)
			pInfo->m_rcLabel.OffsetRect(sizeDelta);
		if (pInfo->m_hWndSpinner)
			pInfo->m_rcSpinner.OffsetRect(sizeDelta);
	}
}

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
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions