Click here to Skip to main content
15,886,258 members
Articles / Mobile Apps / Android

Gynoid: Framework for Mobile Development

Rate me:
Please Sign up or sign in to vote.
4.97/5 (13 votes)
29 May 2009LGPL37 min read 40.6K   537   41  
Gynoid is a wrapper around mobile phone APIs (WinCE, symbian, iPhone)
  • addrbook_bin.zip
    • addrbook.exe
  • addrbook_src.zip
    • addrbook_src
      • addrbook
        • .svn
          • all-wcprops
          • entries
          • prop-base
          • props
          • text-base
          • tmp
            • prop-base
            • props
            • text-base
        • build
          • .svn
            • all-wcprops
            • entries
            • prop-base
            • props
            • text-base
            • tmp
              • prop-base
              • props
              • text-base
          • vc200x-ce
            • .svn
              • all-wcprops
              • entries
              • prop-base
                • addrbook.sln.svn-base
                • addrbook.vcproj.svn-base
              • props
              • text-base
                • addrbook.sln.svn-base
                • addrbook.vcproj.svn-base
              • tmp
                • prop-base
                • props
                • text-base
            • addrbook.sln
            • addrbook.vcproj
        • res
          • .svn
            • all-wcprops
            • entries
            • prop-base
              • addrbook.ico.svn-base
              • addrbookppc.rc.svn-base
              • addrbookppc.rc2.svn-base
            • props
            • text-base
              • addrbook.ico.svn-base
              • addrbookppc.rc.svn-base
              • addrbookppc.rc2.svn-base
            • tmp
              • prop-base
              • props
              • text-base
          • addrbook.ico
          • addrbookppc.rc
          • addrbookppc.rc2
          • arrow.ico
          • RCa10548
          • RCb10548
        • src
          • .svn
            • all-wcprops
            • entries
            • prop-base
              • AboutDlg.h.svn-base
              • addrbook.cpp.svn-base
              • AddrbookFrame.h.svn-base
              • AddrbookView.h.svn-base
              • atltouch.h.svn-base
              • ContactView.h.svn-base
              • Gynoid.AddrBook.cpp.svn-base
              • Gynoid.AddrBook.h.svn-base
              • resourceppc.h.svn-base
              • stdafx.cpp.svn-base
              • stdafx.h.svn-base
            • props
            • text-base
              • AboutDlg.h.svn-base
              • addrbook.cpp.svn-base
              • AddrbookFrame.h.svn-base
              • AddrbookView.h.svn-base
              • atltouch.h.svn-base
              • ContactView.h.svn-base
              • Gynoid.AddrBook.cpp.svn-base
              • Gynoid.AddrBook.h.svn-base
              • resourceppc.h.svn-base
              • stdafx.cpp.svn-base
              • stdafx.h.svn-base
            • tmp
              • prop-base
              • props
              • text-base
          • AboutDlg.h
          • addrbook.cpp
          • AddrbookFrame.h
          • AddrbookView.h
          • atltouch.h
          • ContactView.h
          • resourceppc.h
          • stdafx.cpp
          • stdafx.h
#pragma once
#define USE_STL_CONTAINERS 0

#if USE_STL_CONTAINERS
 #include <vector>
 #include <algorithm>
#endif

#include <atlcrack.h>
#include <atlmisc.h>
#include <atlcoll.h>

#define SCROLL_TIMER	1023

#define SBHIT_NONE		0
#define SBHIT_TOP		1
#define SBHIT_LEFT		1
#define SBHIT_SLIDER	2
#define SBHIT_BOTTOM	3
#define SBHIT_RIGHT		3


#if USE_STL_CONTAINERS
 template<typename CT>
 class CTouchArray : public std::vector<CT>
 {
 public:
	 inline size_t Add(CT element)
	 {
		 insert(end(), element);
		 return (end() - begin());
	 }

	 inline void InsertAt(size_t iElement,CT element, size_t nCount = 1)
	 {
		 insert(begin()+iElement, element);
	 }
	 inline void RemoveAt(size_t iElement, size_t nCount = 1 )
	 {
		 erase(begin()+iElement, begin()+iElement+nCount);
	 }
	inline size_t GetCount( ) const throw( )
	{
		return size();
	}
	inline void RemoveAll( ) throw( )
	{
		clear();
	}

	 

};
#else
 //typedef CAtlArray CTouchArray;
#define CTouchArray CAtlArray
#endif

class CScrollBarData
{
private:
	int		m_nMinSize,
			m_nSize,
			m_nFill,
			m_nOffset,
			m_nHitArea;
	bool	m_bVisible;

	int RoundDiv(int a, int b)
	{
		ATLASSERT(b > 0);

		int d = a / b;
		if((a % b) > (b / 2))
			++d;
		return d;
	}

public:
	CScrollBarData(int nMinSize = 16)
	:	m_nMinSize	(nMinSize), 
		m_nSize		(0), 
		m_nFill		(0),
		m_nOffset	(0),
		m_nHitArea	(SBHIT_NONE),
		m_bVisible	(true)
	{ }

	bool	IsVisible	()				{ return m_bVisible && m_nSize != 0; }
	void	SetVisible	(bool bVisible)	{ m_bVisible = bVisible; }
	int		GetSize		()				{ return m_nSize; }
	int		GetOffset	()				{ return m_nOffset; }
	void	SetOffset	(int nOffset)	{ m_nOffset = nOffset; }
	void	SetHitArea	(int nHitArea)	{ m_nHitArea = nHitArea; }
	int		GetHitArea	()				{ return m_nHitArea; }

	void SetMinSize(int nMinSize) { m_nMinSize = nMinSize; }

	int SetHitPos(int z)
	{
		if(z < m_nOffset)
			m_nHitArea = SBHIT_TOP;
		else if(z < m_nOffset + m_nSize)
			m_nHitArea = SBHIT_SLIDER;
		else
			m_nHitArea = SBHIT_BOTTOM;
		return m_nHitArea;
	}

	void SetViewData(int nViewOffset, int nViewSize, int nRealSize)
	{
		if(nRealSize > 0 && nViewSize < nRealSize)
		{
			m_nSize = RoundDiv(nViewSize * nViewSize, nRealSize);

			if(m_nSize < m_nMinSize)
			{
				m_nFill	= m_nMinSize - m_nSize;
				m_nSize	= m_nMinSize;
			}
			else
				m_nFill = 0;

			m_nOffset = RoundDiv((nViewSize - m_nFill) * nViewOffset, nRealSize);
		}
		else
		{
			m_nSize		= 0;
			m_nOffset	= 0;
		}
	}

	int GetViewOffset(int nViewSize, int nRealSize)
	{
		int nOffset = RoundDiv(m_nOffset * nRealSize, nViewSize - m_nFill);
		return nOffset;
	}
};


//-------------------------------------------------------------------------
//
//	CTouchWindow
//
//-------------------------------------------------------------------------


// CTouchWindow
//
//		Minin template class for touch-based windows
//
template <class T>
class CTouchWindow
{
protected:
	CPoint			m_ptOrg,			// View origin
					m_ptLast;			// Last clicked point
	CSize			m_sizeExt,			// Document size
					m_sizeWnd,			// Window size
					m_sizeMove,			// Automatic scrolling movement size
					m_sizeSens,			// Sensitivity
					m_sizeAccel,		// Number of pixels to reduce m_sizeMove per 1/4 sec
					m_sizeScroll;		// Scroll bar widths (cx -> Vertical, cy -> Horizontal)
	CScrollBarData	m_sbVer,			// Vertical scroll bar data
					m_sbHor;			// Horizontal scroll bar data
	bool			m_bTimer,
					m_bScroll;
	DWORD			m_dwTickIni,		// Initial tick
					m_dwTickMove,
					m_dwTickDown;		// Tick on WM_LBUTTONDOWN
	CBitmap			m_bmpScrollVer,
					m_bmpScrollHor;

	void Decelerate(CSize &move)
	{
		DWORD	dwNumTicks	= GetTickCount() - m_dwTickIni;
		int		nMult		= dwNumTicks / 250;

		if(move.cx && m_sizeAccel.cx > 0)
		{
			if(move.cx > 0)
			{
				move.cx -= nMult * m_sizeAccel.cx;
				if(move.cx < 0)
					move.cx = 0;
			}
			else
			{
				move.cx += nMult * m_sizeAccel.cx;
				if(move.cx > 0)
					move.cx = 0;
			}
		}

		if(move.cy && m_sizeAccel.cy > 0)
		{
			if(move.cy > 0)
			{
				move.cy -= nMult * m_sizeAccel.cy;
				if(move.cy < 0)
					move.cy = 0;
			}
			else
			{
				move.cy += nMult * m_sizeAccel.cy;
				if(move.cy > 0)
					move.cy = 0;
			}
		}
	}

public:
	CTouchWindow()
		:	m_bTimer	(false),
			m_bScroll	(false),
			m_sizeScroll(DRA::SCALEX(16), DRA::SCALEY(16)),
			m_sizeAccel	( 1,  1),
			m_sizeSens	( 4,  4),
			m_dwTickIni	(0),
			m_dwTickDown(0),
			m_dwTickMove(0)
	{
	}
	
	int		GetVScrollWidth	()			{ return m_sizeScroll.cx;		}
	int		GetHScrollHeight()			{ return m_sizeScroll.cy;		}
	bool	IsVScrollVisible()			{ return m_sbVer.IsVisible();	}
	bool	IsHScrollVisible()			{ return m_sbHor.IsVisible();	}

	void SetExtent(CSize size)			{ m_sizeExt = size;				}
	void SetExtent(int cx, int cy)		{ m_sizeExt = CSize(cx, cy);	}
	void SetScrollWidth(int cx, int cy)	{ m_sizeScroll.SetSize(cx, cy);	}
	void SetSensitivity(int cx, int cy)	{ m_sizeSens.SetSize(cx, cy);	}

	void SetOrigin(int x, int y)
	{
		m_ptOrg.SetPoint(x, y);
		UpdateScrollBars();
	}

	void UpdateScrollBars()
	{
		m_sbVer.SetViewData(m_ptOrg.y, m_sizeWnd.cy, m_sizeExt.cy);
		m_sbHor.SetViewData(m_ptOrg.x, m_sizeWnd.cx, m_sizeExt.cx);
	}

	void DrawScrollBars(HDC hDC)
	{
		CDCHandle	dc(hDC);
		CDC			dcMem;
		int			cyBar,
					yBar;
		CRect		rcThumb,
					rcBmp;
		HBRUSH		hOldBrush;
		HBITMAP		hOldBmp;

		if(!m_sbVer.IsVisible())
			return;

		if(m_sizeWnd.cy >= m_sizeExt.cy)
			return;

		dcMem.CreateCompatibleDC(hDC);
		if(m_bmpScrollVer.IsNull())
			m_bmpScrollVer.CreateCompatibleBitmap(hDC, m_sizeScroll.cx, m_sizeWnd.cy);
		hOldBmp = dcMem.SelectBitmap(m_bmpScrollVer);

		rcBmp.left		= 0;
		rcBmp.top		= 0;
		rcBmp.right		= m_sizeScroll.cx;
		rcBmp.bottom	= m_sizeWnd.cy;

		dcMem.FillRect(&rcBmp, (HBRUSH)GetStockObject(LTGRAY_BRUSH));

		cyBar	= m_sbVer.GetSize();
		yBar	= m_sbVer.GetOffset();

		rcThumb.top		= yBar;
		rcThumb.bottom	= yBar + cyBar;
		rcThumb.right	= rcBmp.right - 2;
		rcThumb.left	= rcBmp.left + 2;

		hOldBrush	= dcMem.SelectBrush((HBRUSH)GetStockObject(GRAY_BRUSH));
		dcMem.RoundRect(&rcThumb, CPoint(4, 4));
		dcMem.SelectBrush(hOldBrush);

#if _WIN32_WCE > 0x500
		BLENDFUNCTION	blend;

		blend.BlendOp				= AC_SRC_OVER;
		blend.BlendFlags			= 0;
		blend.AlphaFormat			= 0;
		blend.SourceConstantAlpha	= 128;

		::AlphaBlend(dc, m_sizeWnd.cx - m_sizeScroll.cx, 0, m_sizeScroll.cx, m_sizeWnd.cy, 
						dcMem, 0, 0, rcBmp.right, rcBmp.bottom, blend);
#else
		dc.BitBlt(m_sizeWnd.cx - m_sizeScroll.cx, 0, m_sizeScroll.cx, m_sizeWnd.cy, 
						dcMem, 0, 0, SRCCOPY);
#endif

		dcMem.SelectBitmap(hOldBmp);
	}

	BEGIN_MSG_MAP(CTouchWindow<T>)
		MESSAGE_HANDLER(WM_LBUTTONDOWN,	OnLButtonDown	)
		MESSAGE_HANDLER(WM_LBUTTONUP,	OnLButtonUp		)
		MESSAGE_HANDLER(WM_MOUSEMOVE,	OnMouseMove		)
		MESSAGE_HANDLER(WM_TIMER,		OnTimer			)
		MESSAGE_HANDLER(WM_SIZE,		OnSize			)
	END_MSG_MAP()

	LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		CPoint	pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		T*		pWnd = static_cast<T*>(this);

		if(m_bTimer)
		{
			pWnd->KillTimer(SCROLL_TIMER);
			m_bTimer = false;
		}

		m_ptLast = pt;

		// Check if the user is clicking on any of the scroll bars
		if(m_sbVer.IsVisible() && (pt.x >= m_sizeWnd.cx - m_sizeScroll.cx))
		{
			m_sbVer.SetHitPos(pt.y);
		}
		else if(m_sbHor.IsVisible() && (pt.y >= m_sizeWnd.cy - m_sizeScroll.cy))
		{
			m_sbHor.SetHitPos(pt.x);
		}
		else
		{
			// User is clicking the main window area
			m_sizeMove.SetSize(0, 0);

			m_dwTickIni		= GetTickCount();
			m_dwTickDown	= m_dwTickIni;
			m_bScroll		= false;

			m_sbVer.SetHitArea(SBHIT_NONE);
			m_sbHor.SetHitArea(SBHIT_NONE);
		}

		return 0;
	}

	LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		CPoint	pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		T*		pWnd		= static_cast<T*>(this);

		if(m_bScroll)
		{
			//CSize	move		= m_ptLast - pt;
			//DWORD	dwTickNow	= GetTickCount();

			if(m_sizeSens.cx < 0)
				m_sizeMove.cx = 0;
			if(m_sizeSens.cy < 0)
				m_sizeMove.cy = 0;

			if(abs(m_sizeMove.cx) > m_sizeSens.cx || abs(m_sizeMove.cy) > m_sizeSens.cy)
			{
				if(m_dwTickMove != 0)
				{
					pWnd->SetTimer(SCROLL_TIMER, m_dwTickMove);
					m_bTimer = true;
				}
			}
		}

		m_bScroll = false;
		return 0;
	}

	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

		if(wParam & MK_LBUTTON)
		{
			T*		pWnd = static_cast<T*>(this);
			CSize	move = m_ptLast - pt;
			DWORD	dwTickNow;

			m_ptLast = pt;

			// Check for motion sensitivity
			if((m_sizeSens.cx < 0) || (abs(move.cx) <= m_sizeSens.cx))
				move.cx = 0;

			if((m_sizeSens.cy < 0) || (abs(move.cy) <= m_sizeSens.cy))
				move.cy = 0;

			if((move.cx == 0) && (move.cy == 0))
				return 0;

			m_bScroll = false;

			if(m_sbVer.GetHitArea() != SBHIT_NONE)
			{
				m_sbVer.SetOffset(m_sbVer.GetOffset() - move.cy);
				m_ptOrg.y = m_sbVer.GetViewOffset(m_sizeWnd.cy, m_sizeExt.cy);
			}
			else if(m_sbHor.GetHitArea() != SBHIT_NONE)
			{
				m_sbHor.SetOffset(m_sbHor.GetOffset() - move.cx);
				m_ptOrg.x = m_sbVer.GetViewOffset(m_sizeWnd.cx, m_sizeExt.cx);
			}
			else
			{
				if(m_sizeExt.cx > m_sizeWnd.cx)
				{
					m_sizeMove.cx	= move.cx;
					m_ptOrg.x		+= move.cx;
				}
				else
					move.cx = 0;

				if(m_sizeExt.cy > m_sizeWnd.cy)
				{
					m_sizeMove.cy	= move.cy;
					m_ptOrg.y		+= move.cy;
				}
				else
					move.cy = 0;

				m_bScroll = (move.cx != 0) || (move.cy != 0);
			}

			// Check if the scroll is out of bound
			if(m_ptOrg.y < 0)
				m_ptOrg.y = 0;
			else if((m_sizeExt.cy > m_sizeWnd.cy) && (m_ptOrg.y + m_sizeWnd.cy > m_sizeExt.cy))
				m_ptOrg.y = m_sizeExt.cy - m_sizeWnd.cy;

			if(m_ptOrg.x < 0)
				m_ptOrg.x = 0;
			else if((m_sizeExt.cx > m_sizeWnd.cx) && (m_ptOrg.x + m_sizeWnd.cx > m_sizeExt.cx))
				m_ptOrg.x = m_sizeExt.cx - m_sizeWnd.cx;

			m_sbVer.SetViewData(m_ptOrg.y, m_sizeWnd.cy, m_sizeExt.cy);
			m_sbHor.SetViewData(m_ptOrg.x, m_sizeWnd.cx, m_sizeExt.cx);

			pWnd->InvalidateRect(NULL, FALSE);
			pWnd->UpdateWindow();

			dwTickNow    = GetTickCount();
			m_dwTickMove = dwTickNow - m_dwTickIni;
			m_dwTickIni  = dwTickNow;
		}

		return 0;
	}

	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		CSize size(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

		if(size != m_sizeWnd)
		{
			T* pWnd = static_cast<T*>(this);

			m_sizeWnd = size;

			m_sbVer.SetViewData(m_ptOrg.y, size.cy, m_sizeExt.cy);
			m_sbHor.SetViewData(m_ptOrg.x, size.cx, m_sizeExt.cx);

			if(!m_bmpScrollVer.IsNull())
				m_bmpScrollVer.DeleteObject();
			if(!m_bmpScrollHor.IsNull())
				m_bmpScrollHor.DeleteObject();

			pWnd->InvalidateRect(NULL, FALSE);
			pWnd->UpdateWindow();
		}
		return 0;
	}

	LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if(wParam == SCROLL_TIMER)
		{
			T*		pWnd	= static_cast<T*>(this);
			CSize	move	= m_sizeMove;

			Decelerate(move);

			// Move the origin
			m_ptOrg += move;

			// Check for bounds
			if(m_ptOrg.x < 0)
			{
				m_ptOrg.x		= 0;
				m_sizeMove.cx	= 0;
			}
			else if(m_ptOrg.x + m_sizeWnd.cx > m_sizeExt.cx)
			{
				m_ptOrg.x		= m_sizeExt.cx - m_sizeWnd.cx;
				m_sizeMove.cx	= 0;
			}

			if(m_ptOrg.y < 0)
			{
				m_ptOrg.y		= 0;
				m_sizeMove.cy	= 0;
			}
			else if(m_ptOrg.y + m_sizeWnd.cy > m_sizeExt.cy)
			{
				m_ptOrg.y		= m_sizeExt.cy - m_sizeWnd.cy;
				m_sizeMove.cy	= 0;
			}

			// Check if the speed reached zero
			if(move.cx == 0 && move.cy == 0)
			{
				pWnd->KillTimer(SCROLL_TIMER);
				m_bTimer = false;
			}

			m_sbVer.SetViewData(m_ptOrg.y, m_sizeWnd.cy, m_sizeExt.cy);
			m_sbHor.SetViewData(m_ptOrg.x, m_sizeWnd.cx, m_sizeExt.cx);

			pWnd->InvalidateRect(NULL, FALSE);
			pWnd->UpdateWindow();
		}

		return 0;
	}
};


//-------------------------------------------------------------------------
//
//	CTouchList
//
//-------------------------------------------------------------------------


// Forwad declaration
class CTouchListBase;


#define TLIS_SELECTED	0x00000001
#define TLIS_HASFOCUS	0x00000002
#define TLIS_ENABLED	0x00000004

#define	TLIHIT_NONE		0x00000000
#define TLIHIT_SELECT	0x00000001
#define TLIHIT_TEXT		0x00000002


// CTouchListItem
//
//		A touch list item
//
class CTouchListItem
{
protected:
	CTouchListBase*	m_pList;
	DWORD			m_dwData,
					m_dwState;
	volatile int	m_nHeight;
	bool			m_bManaged;			// Deleted by container?

public:
	CTouchListItem()
	:	m_pList		(NULL),
		m_dwData	(0),
		m_dwState	(0),
		m_nHeight	(0),
		m_bManaged	(true)
	{
	}

	virtual ~CTouchListItem()
	{
	}

	bool	IsManaged	()	{ return m_bManaged;						}
	bool	IsSelected	()	{ return (m_dwState & TLIS_SELECTED) != 0;	}
	bool	IsEnabled	()	{ return (m_dwState & TLIS_ENABLED)  != 0;	}
	bool	HasFocus	()	{ return (m_dwState & TLIS_HASFOCUS) != 0;	}

	DWORD	GetData()				{ return m_dwData;		}
	void	SetData(DWORD dwData)	{ m_dwData = dwData;	}

	DWORD	GetState()							{ return m_dwState;		}
	void	SetState(DWORD dwFlags, bool bSet)	{ m_dwState = bSet ? (m_dwState | dwFlags) : (m_dwState & ~dwFlags);	}

	void	SetSelected	(bool bSelected)		{ SetState(TLIS_SELECTED, bSelected);	}
	void	SetFocused	(bool bFocused)			{ SetState(TLIS_HASFOCUS, bFocused );	}
	void	SetEnabled	(bool bEnabled)			{ SetState(TLIS_ENABLED,  bEnabled );	}
	void	SetManaged	(bool bManaged)			{ m_bManaged = bManaged;				}

	int		GetHeight()				{ return m_nHeight;		}
	void	SetHeight(int nHeight)	{ m_nHeight = nHeight;	}

	void			SetList	(CTouchListBase* pList)	{ m_pList = pList; }
	CTouchListBase*	GetList	()	{ return m_pList;	}

	virtual	void	Draw	(HDC hDC, int iItem, CRect& rcItem)			{ }
	virtual int		HitTest	(int iItem, CRect& rcItem, CPoint& ptHit)	{ return TLIHIT_SELECT; }
};


#define TLN_FIRST		(0U - 2000U)
#define TLN_LAST		(0U - 2100U)

#define TLN_ITEMSELECTED	(TLN_FIRST - 0)
#define TLN_ITEMACTIVATED	(TLN_FIRST - 1)
#define TLN_ITEMGOTFOCUS	(TLN_FIRST - 2)


typedef struct _NMTOUCHLIST
{
	NMHDR	hdr;
	int		iItem;			// Item index
	DWORD	dwState;		// Item state
} NMTOUCHLIST;


// CTouchListBase
//
//		Base class for the touch list
//
class CTouchListBase
{
protected:
	CTouchArray<CTouchListItem*>	m_items;
	CTouchArray<size_t>			m_selected;

	

	int			m_nItems,
				m_iClickItem,
				m_iActiveItem,
				m_iFocusItem;
	bool		m_bSingleSel;
	CBitmap		m_bmpMem;
	CSize		m_cxyBmp;

public:
	CTouchListBase()
		:	m_nItems		(0),
			m_iClickItem	(-1),
			m_iActiveItem	(-1),
			m_iFocusItem	(-1),		// Item with the focus
			m_bSingleSel	(true),
			m_cxyBmp		(0, 0)
	{
	}

	void AddItem(CTouchListItem* pItem)
	{
		pItem->SetList(this);
		m_items.Add(pItem);
		++m_nItems;
	}

	void InsertItem(CTouchListItem* pItem, int iIndex)
	{
		pItem->SetList(this);
		m_items.InsertAt(iIndex, pItem);
		++m_nItems;
	}

	void RemoveItem(int iIndex)
	{
		if(m_items[iIndex]->IsManaged())
			delete m_items[iIndex];

		m_items.RemoveAt(iIndex);
		--m_nItems;
	}

	int GetItemCount() { return (int)m_items.GetCount(); }

	int		GetActiveItem	()		{ return m_iActiveItem;	}
	int		GetFocusItem	()		{ return m_iFocusItem;	}

	CTouchListItem*	GetItem	(int iItem)	{ return m_items[iItem];	}

	int GetItemIndex(CTouchListItem* pItem)
	{
		size_t i, n = m_items.GetCount();

		for(i = 0; i < n; ++i)
		{
			if(m_items[i] == pItem)
				return (int)i;
		}
		return -1;
	}

	int GetItemAt(int yPos)
	{
		int i, n = (int)m_items.GetCount(),
			y = 0;

		for(i = 0; i < n; ++i)
		{
			y += m_items[i]->GetHeight();
			if(yPos <= y)
				return i;
		}
		return -1;
	}

	int GetItemHeightSum(int nItems)
	{
		int i, h = 0;

		for(i = 0; i < nItems; ++i)
			h += m_items[i]->GetHeight();
		return h;
	}

	int GetTotalItemHeight()
	{
		return GetItemHeightSum((int)m_items.GetCount());
	}
};


// CTouchList
//
//		Implements a touch list
//
template <class TBase>
class CTouchList :	public CWindowImpl<CTouchList<TBase> >,
					public CTouchWindow<CTouchList<TBase> >,
					public CTouchListBase
{
protected:

	bool IsValidItem(int iIndex) { return iIndex >= 0 && iIndex < (int)m_items.GetCount(); }

	void NotifyItemChange(int iIndex, UINT code, DWORD dwState)
	{
		NMTOUCHLIST nmtl;
		TBase*		pT			= static_cast<TBase*>(this);
		CWindow		wndParent	= GetParent();

		nmtl.hdr.code		= code;
		nmtl.hdr.hwndFrom	= m_hWnd;
		nmtl.hdr.idFrom		= ::GetDlgCtrlID(m_hWnd);
		nmtl.iItem			= iIndex;
		nmtl.dwState		= dwState;

		// Call "virtual" method in derived class
		pT->OnItemChangedState(nmtl);

		wndParent.SendMessage(WM_NOTIFY, nmtl.hdr.idFrom, (LPARAM)&nmtl);
	}

public:
	CTouchList()
	{
	}

	DECLARE_WND_CLASS(NULL)

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		pMsg;
		return FALSE;
	}

	void ClearItems()
	{
		size_t i;

		m_selected.RemoveAll();
		for(i = 0; i < m_items.GetCount(); ++i)
		{
			CTouchListItem* pItem = m_items[i];

			if(pItem != NULL && pItem->IsManaged())
				delete pItem;
			m_items[i] = NULL;
		}

		m_items.RemoveAll();
		SetExtent(m_sizeWnd.cx, 0);
		m_nItems = 0;
		m_iFocusItem = -1;
		SetListPos(0, 0);
	}
	
	void SetItemCount(int nItems)
	{
		int i;

		ClearItems();

		m_nItems = nItems; 
		m_items.SetCount(nItems);	

		for(i = 0; i < nItems; ++i)
			m_items[i] = NULL;

		SetExtent(m_sizeWnd.cx, GetTotalItemHeight());
		SetListPos(m_ptOrg.x, m_ptOrg.y);
	}

	void TotalItemHeightUpdated()
	{
		m_sizeExt.cy = GetTotalItemHeight();
		UpdateScrollBars();
	}

	void SetListPos(int xOrg, int yOrg)
	{
		if(IsWindow())
		{
			CRect	rc;

			GetClientRect(&rc);
			m_sbVer.SetViewData(yOrg, rc.Height(), GetTotalItemHeight());
		}

		m_ptOrg.x = xOrg;
		m_ptOrg.y = yOrg;
	}

	int GetFirstVisibleItem()
	{
		return GetItemAt(m_ptOrg.y);
	}

	void ClearSelection()
	{
		size_t i;

		for(i = 0; i < m_selected.GetCount(); ++i)
		{
			size_t iItem = m_selected[i];

			m_items[iItem]->SetState(TLIS_SELECTED, false);
		}
		m_selected.RemoveAll();
	}

	void OnItemChangedState(NMTOUCHLIST& nmtl)
	{
	}

	void OnItemGotFocus(NMTOUCHLIST& nmtl)
	{
	}

	void OnItemSelected(NMTOUCHLIST& nmtl)
	{
	}

	void OnItemActivated(NMTOUCHLIST& nmtl)
	{
	}

	// RedrawItem
	//
	//		Redraws the item at the given index
	//
	void RedrawItem(int iIndex, bool bUpdateWindow = true)
	{
		ATLASSERT(IsValidItem(iIndex));

		if(!IsWindow())
			return;

		CRect rc;

		GetItemRect(iIndex, rc);
		InvalidateRect(&rc, FALSE);
		if(bUpdateWindow)
			UpdateWindow();
	}

	// EnsureVisible
	//
	//		Scrolls the item into view
	//
	void EnsureVisible(int iItem)
	{
		CRect	rc;

		if(m_sizeWnd.cy == 0 || m_sizeWnd.cx == 0)
			return;

		GetItemRect(iItem, rc);
		if(rc.top < 0)
		{
			// Item is totally or partially hidden to the top
			int	dy		= -rc.top,
				ym		= DRA::SCALEY(8),
				yy,
				yOld	= m_ptOrg.y;

			for(yy = ym; yy < dy; yy += ym)
			{
				SetOrigin(0, yOld - yy);
				InvalidateRect(NULL, FALSE);
				UpdateWindow();
			}

			SetOrigin(0, yOld - dy);
			InvalidateRect(NULL, FALSE);
			UpdateWindow();
		}
		else if(rc.bottom > m_sizeWnd.cy)
		{
			// Item is totally or partially hidden to the bottom
			int dy		= rc.bottom - m_sizeWnd.cy,
				ym		= DRA::SCALEY(8),
				yy,
				yOld	= m_ptOrg.y;

			for(yy = ym; yy < dy; yy += ym)
			{
				SetOrigin(0, yOld + yy);
				InvalidateRect(NULL, FALSE);
				UpdateWindow();
			}

			SetOrigin(0, yOld + dy);
			InvalidateRect(NULL, FALSE);
			UpdateWindow();
		}
	}

	
	int HitTest(CPoint pt, int& iItem)
	{
		iItem = GetItemAt(m_ptOrg.y + pt.y);

		if(IsValidItem(iItem))
		{
			CRect	rcItem;

			GetItemRect(iItem, rcItem);
			return m_items[iItem]->HitTest(iItem, rcItem,pt);
		}
		return TLIHIT_NONE;
	}


	// FocusItem
	//
	//		Sets the focus on an item. Only one item can have the focus at any one time.
	//
	void FocusItem(int iItem)
	{
		// Check if old and new are the same. If so, skip further processing.
		if(iItem == m_iFocusItem)
			return;

		// Start by removing the focus from the current item
		if(IsValidItem(m_iFocusItem))
		{
			m_items[m_iFocusItem]->SetFocused(false);
			NotifyItemChange(m_iFocusItem, TLN_ITEMGOTFOCUS, 0);
		}

		// Now set the focus to the new item
		if(IsValidItem(iItem))
		{
			EnsureVisible(iItem);

			m_items[iItem]->SetFocused(true);
			NotifyItemChange(iItem, TLN_ITEMGOTFOCUS, 1);
			m_iFocusItem = iItem;
		}
		else
			m_iFocusItem = -1;
	}


	// SelectItem
	//
	//		Selects an item on the list. If the list is in single-selection mode, 
	//		de-selects the previous item.
	//
	void SelectItem(int iItem)
	{
		// Is this a single-selection list?
		// If so, clear the last selection
		if(m_bSingleSel && m_selected.GetCount())
		{
			int iOld = (int)m_selected[0];

			m_items[iOld]->SetState(TLIS_SELECTED, false);

			NotifyItemChange(iOld, TLN_ITEMSELECTED, 0);

			ClearSelection();
		}

		// Check if the new item index is in bounds
		if(IsValidItem(iItem))
		{
			// Now, select the new item
			m_items[iItem]->SetState(TLIS_SELECTED, true);
			m_selected.Add((size_t)iItem);

			NotifyItemChange(iItem, TLN_ITEMSELECTED, 1);
		}
	}

	void ActivateItem(int iItem, bool bActive)
	{
		// Deactivate the old active item, if any
		if(bActive && IsValidItem(m_iActiveItem))
			NotifyItemChange(m_iActiveItem, TLN_ITEMACTIVATED, 0);

		m_iActiveItem = bActive ? iItem : -1;
		
		if(IsValidItem(iItem))
			NotifyItemChange(iItem, TLN_ITEMACTIVATED, bActive ? 1 : 0);
	}

	void PreDrawList(HDC hDC, CRect &rcClient)
	{
	}

	void PostDrawList(HDC hDC)
	{
	}

	void DrawItem(HDC hDC, int iItem, CRect &rcItem)
	{
	}

	CTouchListItem* NewListItem(int iItem)
	{
		return new CTouchListItem;
	}

	void GetItemRect(int iItem, RECT& rc)
	{
		GetClientRect(&rc);

		rc.top		= GetItemHeightSum(iItem) - m_ptOrg.y;
		rc.bottom	= rc.top + m_items[iItem]->GetHeight();
	}

	int GetItemVisibility(int iItem)
	{
		return 0;
	}

	// GetNextItemIndex
	//
	//		Calculates the next item index for navigation purposes.
	//		Override in your derived class for custom behavior.
	//
	int GetNextItemIndex(int iItem, int delta)
	{
		return iItem + delta;
	}

	BEGIN_MSG_MAP(CTouchList<TBase>)

		MESSAGE_HANDLER		(WM_PAINT,	OnPaint	)
		MESSAGE_HANDLER		(WM_SIZE,	OnSize	)
		MSG_WM_ERASEBKGND	(OnEraseBackground)
		MSG_WM_LBUTTONDOWN	(OnLButtonDown)
		MSG_WM_LBUTTONUP	(OnLButtonUp)
//		MSG_WM_MOUSEMOVE(OnMouseMove)
		MSG_WM_DESTROY		(OnDestroy)
		MSG_WM_KEYUP		(OnKeyUp)

		CHAIN_MSG_MAP(CTouchWindow<CTouchList<TBase> >)
	END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//	LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//	LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		TBase*			pT	= static_cast<TBase*>(this);
		CPaintDC		dc(m_hWnd);
		CDC				dcMem;
		HBITMAP			hOldBmp;
		CRect			rcClient,
						rcItem,
						rcPaint(dc.m_ps.rcPaint),
						rcIntersect;
		int				iItem,
						cyItem;
		BOOL			bOk;
		DWORD			dwError = 0;
		CTouchListItem*	pItem;
		
		dcMem.CreateCompatibleDC(dc);

		GetClientRect(&rcClient);
		rcItem = rcClient;

		// Check if the second-plane bitmap needs to be recreated
		if(m_cxyBmp.cx != rcClient.Width() && m_cxyBmp.cy != rcClient.Height())
		{
			m_cxyBmp.cx = rcClient.Width();
			m_cxyBmp.cy = rcClient.Height();

			if(!m_bmpMem.IsNull())
				m_bmpMem.DeleteObject();
			m_bmpMem.CreateCompatibleBitmap(dc, m_cxyBmp.cx, m_cxyBmp.cy);
		}

		hOldBmp = dcMem.SelectBitmap(m_bmpMem);


		// Calculate the first item to display
		iItem = GetFirstVisibleItem();
		if(iItem != -1)
		{
			cyItem			= m_items[iItem]->GetHeight();
			rcItem.top		= GetItemHeightSum(iItem) - m_ptOrg.y;
			rcItem.bottom	= rcItem.top + cyItem;

			pT->PreDrawList(dcMem, rcClient);
			


			while((rcItem.top < rcClient.bottom) && (iItem >= 0) && (iItem < m_nItems))
			{
				CRect	rcTest;

				pItem = m_items[iItem];

				if(pItem == NULL)
					pItem = m_items[iItem] = pT->NewListItem(iItem);

				// Calculate the intersection between the item's rect and the paint rect
				rcTest.IntersectRect(rcPaint, rcItem);

				// Paint only if the the intersection rect is not empty
				if(!rcTest.IsRectEmpty())				
					pT->DrawItem(dcMem, iItem, rcItem);

				cyItem			= pItem->GetHeight();
				if (iItem < m_nItems - 1)
				{
					rcItem.top		+= cyItem;
					rcItem.bottom	= rcItem.top + m_items[iItem+1]->GetHeight();
				}
				++iItem;
			}
		}

		// Check if the list did not fill the whole window
		if((rcItem.top < rcClient.bottom))
		{
			CRect	rc(rcClient);
			//rc.top = (iItem == -1) ? rcItem.top : rcItem.bottom;
			 if (iItem != -1)
				 rc.top = rcItem.bottom;

			if(!dcMem.FillRect(&rc, (HBRUSH)GetStockObject(WHITE_BRUSH)))
				dwError = GetLastError();
		}

		pT->DrawScrollBars(dcMem);

		pT->PostDrawList(dcMem);

		int xPaint = dc.m_ps.rcPaint.left;
		int yPaint = dc.m_ps.rcPaint.top;

		// Paint only the invalidated area for performance reasons
		bOk = dc.BitBlt(xPaint, yPaint, m_cxyBmp.cx, m_cxyBmp.cy, dcMem, xPaint, yPaint, SRCCOPY);
		//bOk = dc.BitBlt(0, 0, m_cxyBmp.cx, m_cxyBmp.cy, dcMem, 0, 0, SRCCOPY);

		// This is here for debugging purposes
		if(!bOk)
			dwError = GetLastError();
		dcMem.SelectBitmap(hOldBmp);

		return 0;
	}

	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		CSize size(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

		SetExtent(size.cx, GetTotalItemHeight());

		bHandled = FALSE;

		return 0;
	}

	LRESULT OnEraseBackground(HDC hDC)
	{
		return TRUE;
	}

	LRESULT OnLButtonDown(UINT flags, CPoint pt)
	{
		SetMsgHandled(FALSE);

		if(m_sbVer.IsVisible() && pt.x > m_sizeWnd.cx - m_sizeScroll.cx)
			m_sbVer.SetHitPos(pt.x);
		else
			m_sbVer.SetHitArea(SBHIT_NONE);

		m_iClickItem = GetItemAt(m_ptOrg.y + pt.y);
		FocusItem(m_iClickItem);

		return 0;
	}

	LRESULT OnLButtonUp(UINT flags, CPoint pt)
	{
		SetMsgHandled(FALSE);

		if(m_sbVer.GetHitArea() == SBHIT_NONE)
		{
			DWORD	dwTickCount	= GetTickCount() - m_dwTickDown;

			// Select the item only if we saw a button down / up within 200 milliseconds
			if(dwTickCount < 200)
			{
				int	iItem,
					nHitTest = HitTest(pt, iItem);

				if(iItem != m_iClickItem)
					return 0;

				KillTimer(SCROLL_TIMER);
				m_bTimer = false;
				
				if(nHitTest == TLIHIT_SELECT)
				{
					// Select and activate the item
					SelectItem	(iItem);
					ActivateItem(iItem, true);
				}
		
				SetMsgHandled(TRUE);
			}
		}

		return 0;
	}

	LRESULT OnDestroy(void)
	{
		//You should call SetMsgHandled(FALSE) or set bHandled = FALSE for the main window of your application
		ClearItems();
		SetMsgHandled(FALSE);
		return 0;
	}

	// OnKeyUp
	//
	//		The user released a key
	//
	void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
	{
		TBase*	pT	= static_cast<TBase*>(this);

		switch(nChar)
		{
		case VK_UP:
			if(m_iFocusItem == -1)
				FocusItem((int)m_items.GetCount() - 1);
			else
			{
				int iNext = pT->GetNextItemIndex(m_iFocusItem, -1);
				FocusItem(iNext);
			}
			break;

		case VK_DOWN:
			if(m_iFocusItem == -1)
				FocusItem(0);
			else
			{
				int iNext = pT->GetNextItemIndex(m_iFocusItem, 1);
				FocusItem(iNext);
			}
			break;

		case VK_RETURN:
			if(IsValidItem(m_iFocusItem))
				ActivateItem(m_iFocusItem, true);
			break;

		default:
			// All other keys are processed by the base class
			SetMsgHandled(FALSE);
		}
	}

};

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 GNU Lesser General Public License (LGPLv3)


Written By
Software Developer Smartmobili
France France
I have founded a company specialized in mobile development and I am always trying to develop multi-platform things.
I am currently looking for new interesting job, so if you need someone please let me know.

Comments and Discussions