Click here to Skip to main content
15,881,281 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.5K   23.6K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
/*************************************************************************
				Class Implementation : CUGTopHdg
**************************************************************************
	Source file : UGTopHdg.cpp
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
*************************************************************************/
#include "stdafx.h"
#include "UGCtrl.h"
#include "UGCell.h"
// define WM_HELPHITTEST messages
#include <afxpriv.h>

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

/***************************************************
	Standard construction/desrtuction
***************************************************/
CUGTopHdg::CUGTopHdg()
{
	//init the varialbes
	m_isSizing	= FALSE;
	m_canSize	= FALSE;
	
	//set the last focus rect position
	m_focusRect.left	= -1;
	m_focusRect.right	= -1;
	m_focusRect.top		= -1;
	m_focusRect.bottom	= -1;

	m_swapStartCol = -1;	//columns to swap
	m_swapEndCol = -1;	
}

CUGTopHdg::~CUGTopHdg()
{
}

/********************************************
	Message handlers
*********************************************/
BEGIN_MESSAGE_MAP(CUGTopHdg, CWnd)
	//{{AFX_MSG_MAP(CUGTopHdg)
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_SETCURSOR()
	ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
	ON_WM_HELPINFO()
	ON_NOTIFY_EX( TTN_NEEDTEXT, 0, ToolTipNeedText )
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/***************************************************
OnPaint
	This routine is responsible for gathering information on cells to draw,
	and draw in an optimized fashion.
Params:
	<none>
Returns:
	<none>
*****************************************************/
void CUGTopHdg::OnPaint() 
{
	if ( m_GI->m_paintMode == FALSE )
		return;

	CPaintDC dc(this); // device context for painting
	
	m_drawHint.AddHint(0,m_GI->m_numberTopHdgRows * -1,m_GI->m_numberCols,0);

	DrawCellsIntern(&dc);
	
	m_drawHint.ClearHints();
}

/***************************************************
DrawCellsIntern
	function is the key to the fast redraw functionality in the Ultimate Grid.
	This function is responsible for drawing cells within the grid area, it
	makes sure that only the cells that are marked as invalid
	(by calling CUGDrawHint::IsInvalid function) are redrawn.
Params:
	dc		- pointer DC to draw on
Returns:
	<none>
*****************************************************/
void CUGTopHdg::DrawCellsIntern(CDC *dc)
{	
	CRect rect(0,0,0,0), cellRect;
  	CUGCell cell;
	CUGCellType * cellType;
	int dcID;
	int xIndex,col;
	long yIndex,row;
	
	m_ctrl->OnScreenDCSetup(dc,NULL,UG_TOPHEADING);
	
	//set the default font
	if(m_GI->m_defFont != NULL)
		dc->SelectObject((CFont *) m_GI->m_defFont);

	int blankRight = 0;

	for(yIndex = (m_GI->m_numberTopHdgRows * -1); yIndex < 0 ; yIndex++)
	{
		row = yIndex;
	
		for(xIndex = 0;xIndex < m_GI->m_numberCols;xIndex++)
		{
			if(xIndex == m_GI->m_numLockCols)
				xIndex = m_GI->m_leftCol;
			col = xIndex;
			row = yIndex;
	
			//draw if invalid
			if(m_drawHint.IsInvalid(col,row) != FALSE)
			{
				GetCellRect(col,row,&rect);
				CopyRect(&cellRect,&rect);

				m_ctrl->GetCellIndirect(col,row,&cell);

				if(cell.IsPropertySet(UGCELL_JOIN_SET))
				{
					GetCellRect(col,row,&cellRect);
					m_ctrl->GetJoinStartCell(&col,&row,&cell);
					if(m_drawHint.IsValid(col,row))
						continue;
					m_drawHint.SetAsValid(col,row);
				}
					
				if(cellRect.left < cellRect.right)
				{
					cellType = m_ctrl->GetCellType(cell.GetCellType());

					dcID = dc->SaveDC();
					
					if(m_swapEndCol >=0 && col == m_swapStartCol)
						cellType->OnDraw(dc,&cellRect,col,row,&cell,1,0);
					else
						cellType->OnDraw(dc,&cellRect,col,row,&cell,0,0);

					dc->RestoreDC(dcID);
				}
			}
			if(rect.right > m_GI->m_gridWidth)
				break;
		}
		if(blankRight < rect.right)
			blankRight = rect.right;
	}
	if(blankRight < m_GI->m_gridWidth)
	{
		rect.top = 0;
		rect.bottom = m_GI->m_topHdgHeight;
		rect.left = blankRight;
		rect.right = m_GI->m_gridWidth;
		// fill-in the area that is not covered by cells
		CBrush brush( m_ctrl->OnGetDefBackColor( UG_TOPHEADING ));
		dc->FillRect( rect, &brush );
	}
}

/************************************************
Update 
	function makes sure that the last row in the top heading has proper
	height before the window is redrawn.
Params:
	<none>
Return:
	<none>
*************************************************/
void CUGTopHdg::Update()
{	
	//calc the last row height
	//find the row
	int height = 0;

	// For VC6/2002/2003/2005 compatibility
	int yIndex= -1;

	for(;yIndex > (m_GI->m_numberTopHdgRows * -1) ;yIndex--)
	{
		height += GetTHRowHeight(yIndex);
	}

	height = m_GI->m_topHdgHeight - height;

	if(height < 0)
		height = 0;

	m_ctrl->SetTH_RowHeight(yIndex,height);

	//redraw the window
	InvalidateRect(NULL);
	UpdateWindow();
}

/************************************************
Moved
	function makes sure that the grid view has moved horizontally
	before the window update is allowed.  With this check the Ultimate
	Grid eliminates un-necessary redraws.
Params:
	<none>
Returns:
	<none>
*************************************************/
void CUGTopHdg::Moved()
{
	if(m_GI->m_leftCol == m_GI->m_lastLeftCol)
		return;

	//redraw the window
	InvalidateRect(NULL);
	UpdateWindow();
}

/***************************************************
CheckForUserResize
	function is called when user clicks the left mouse button or moves the
	mouse cursor over the area in the top heading that would allow column
	or row sizing.  This function is used to determine if sizing is allowed.
	Instead of having a return value, this function sets member vairables
	to proper state.
Params:
	point		- pos of the mouse pointer
Returns:
	<none>
*****************************************************/
void CUGTopHdg::CheckForUserResize(CPoint *point)
{
	if(m_GI->m_userSizingMode == FALSE)
		return;

	//top heading column sizing
	int width = 0;
	for(int col = 0;col < m_GI->m_numberCols;col++)
	{	
		if(col == m_GI->m_numLockCols && col < m_GI->m_leftCol)
			col = m_GI->m_leftCol;
		
		width += m_ctrl->GetColWidth(col);
		if(width > m_GI->m_gridWidth)
			break;

        if(point->x < width+3 && point->x > width-3)
        {	
            if(m_ctrl->GetColWidth(col+1) == 0 && (col+1) < m_GI->m_numberCols)
                col++;

            if(m_ctrl->OnCanSizeCol(col) == FALSE)
                return;

            m_canSize = TRUE;
            m_colOrRowSizing	= 0;				// 0-col 1-row
            m_sizingColRow		= col;				//column/row being sized
            m_sizingStartSize	= m_ctrl->GetColWidth(col);//original size
            m_sizingStartPos	= point->x;			//original start pos

            SetCursor(m_GI->m_WEResizseCursor);
            return;
        }
    }

	//top heading row sizing
	int height = m_GI->m_topHdgHeight;
	for(int row = 0; row < m_GI->m_numberTopHdgRows; row++)
	{		
		if(point->y < height+3  && point->y > height-3)
		{		
			if(m_ctrl->OnCanSizeTopHdg() == FALSE)
				return;
			
			m_canSize = TRUE;
			m_colOrRowSizing	= 1;				// 0-col 1-row
			m_sizingColRow		= row;				//column/row being sized
			m_sizingStartSize	= m_GI->m_topHdgHeights[row];//original size
			m_sizingStartPos	= point->y;			//original start pos
			m_sizingStartHeight = m_GI->m_topHdgHeight;

			SetCursor(m_GI->m_NSResizseCursor);
			return;
		}
		height -= m_GI->m_topHdgHeights[row];
	}

	if(m_canSize)
	{
		m_canSize = FALSE;
		SetCursor(m_GI->m_arrowCursor);
	}
}
/************************************************
OnMouseMove
	Checks to see if the mouse is over a cell separation line, if it is then it
	checks to see if sizing is allowed.  If it is then the cusor is changed to
	the sizing cusror.
	If sizing is in progress then column width or top heading row height are
	updated.
	This is also where the column swapping operation is handled.
Params:
	nFlags		- mouse button states
	point		- point where the mouse is
Return:
	<none>
*************************************************/
void CUGTopHdg::OnMouseMove(UINT nFlags, CPoint point) 
{
	//check to see if the mouse is over a cell separation 
	//if the mouse is not currently sizing
	if(m_isSizing == FALSE && (nFlags&MK_LBUTTON) == 0 && m_GI->m_userSizingMode >0)
	{
		//check for user resize position
		CheckForUserResize(&point);		
	}
	else if(m_isSizing)
	{
		if(m_colOrRowSizing == 0)	//col sizing
		{
			if(point.x < m_sizingStartPos - m_sizingStartSize)
				point.x = m_sizingStartPos - m_sizingStartSize;

			int width = m_sizingStartSize + (point.x - m_sizingStartPos);

			// send notifications to cell types.  In order to provide acceptable performance
			// this notification will only be sent to the visible rows.
			for(int nIndex=m_ctrl->GetTopRow(); nIndex<m_ctrl->GetBottomRow(); nIndex++)
			{
				CUGCellType* pCellType=m_ctrl->GetCellType(m_sizingColRow,nIndex);
				if(pCellType!=NULL)
				{
					pCellType->OnChangingCellWidth(m_sizingColRow,nIndex,&width);
				}
			}
			//send notification to control
			m_ctrl->OnColSizing(m_sizingColRow,&width);

			//just draw a focus rect
			if(m_GI->m_userSizingMode == 1)
			{
				m_ctrl->SetColWidth(m_sizingColRow,width);
				Update();
				
				CDC* dc = m_ctrl->m_CUGGrid->GetDC();
				dc->DrawFocusRect(&m_focusRect);
				m_focusRect.top = 0;
				m_focusRect.bottom = m_GI->m_gridHeight;
				m_focusRect.left = point.x-1;
				m_focusRect.right = point.x+1;
				dc->DrawFocusRect(&m_focusRect);
				m_ctrl->m_CUGGrid->ReleaseDC(dc);

			}
			else	//update on the fly
			{
				m_ctrl->SetColWidth(m_sizingColRow,width);
				m_ctrl->RedrawAll();				
			}
		}
		else	//row sizing
		{
			int height = m_sizingStartSize + (point.y - m_sizingStartPos);
			if(height <0)
				height = 0;
			m_GI->m_topHdgHeights[m_sizingColRow] = height;

			height = m_sizingStartHeight + (point.y - m_sizingStartPos);
			if(height <0)
				height = 0;

			if(m_ctrl->OnTopHdgSizing(&height) == TRUE)
			{
				m_GI->m_topHdgHeight = height;
				m_ctrl->AdjustComponentSizes();
			}
		}
	}
	//check for column swapping
	else if(m_GI->m_enableColSwapping && m_swapStartCol >= 0)
	{	
		MSG msg;
		
		//while column swapping enable mouse scrolling of the grid
		if(point.x < 0 || point.x > m_GI->m_gridWidth)
		{	
			//remove the focus rectangle
			CDC* dc = m_ctrl->m_CUGGrid->GetDC();
			dc->DrawFocusRect(&m_focusRect);
			m_focusRect.left	= -1;
			m_focusRect.right	= -1;
			m_ctrl->m_CUGGrid->ReleaseDC(dc);		

			while(1)
			{
				if(point.x < 0)
					m_ctrl->MoveLeftCol(UG_LINEUP);
				else if(point.x > m_GI->m_gridWidth)
					m_ctrl->MoveLeftCol(UG_LINEDOWN);

				//check for messages, if ther are none then scroll some more
				while(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
				{
					if(msg.message == WM_MOUSEMOVE || msg.message == WM_LBUTTONUP)
						return;

					GetMessage(&msg,NULL,0,0);
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}
		}
		else
		{
			int col,row;
			RECT rect;

			//find the column that the mouse is over
			if(GetCellFromPoint(&point,&col,&row,&rect) != UG_SUCCESS)
				return;

			//only start swapping if the mouse has moved over
			//a different column since the mouse button was pressed
			if(col != m_swapStartCol || m_swapEndCol >=0)
			{	
				if(( point.x - rect.left) > ((rect.right - rect.left)/2))
				{
					col ++;
					rect.left = rect.right;
				}

				if(col > m_GI->m_numberCols)
					col = m_GI->m_numberCols;

				//the firt time this is called redraw the top heading
				//so that the startswap cell is updated
				if(m_swapEndCol < 0)
				{
					m_swapEndCol = col;
					Update();
				}

				//store the current col number
				m_swapEndCol = col;

				//draw a focus rect showing where the swap will
				//take place
				CDC* dc = m_ctrl->m_CUGGrid->GetDC();
				dc->DrawFocusRect(&m_focusRect);
				m_focusRect.top = 0;
				m_focusRect.bottom = m_GI->m_gridHeight;
				m_focusRect.left = rect.left;
				m_focusRect.right = rect.left+2;
				dc->DrawFocusRect(&m_focusRect);
				m_ctrl->m_CUGGrid->ReleaseDC(dc);		
			}
		}
	}
}

/************************************************
OnLButtonDown
	Finds the cell that was clicked in, sends a notification
	updates the current cells position.
	It also sets mouse capture, which will later be used during column
	swaping operation.
Params:
	nFlags		- please see MSDN for more information on the parameters.
	point
Returns:
	<none>
*************************************************/
void CUGTopHdg::OnLButtonDown(UINT nFlags, CPoint point)
{
	int col;
	int row;
	RECT rect;

	UNREFERENCED_PARAMETER(nFlags);

	if(GetFocus() != m_ctrl->m_CUGGrid)
		m_ctrl->m_CUGGrid->SetFocus();

	if(m_canSize)
	{
		m_isSizing = TRUE;
		SetCapture();
	}
	else if(GetCellFromPoint(&point,&col,&row,&rect) == UG_SUCCESS)
	{
		//store the column where the button was pressed
		//just in case a swap is to take place
		if(m_ctrl->OnColSwapStart(col) != FALSE)
			m_swapStartCol = col;

		//send a notification to the cell type	
		BOOL processed = m_ctrl->GetCellType(col,row)->OnLClicked(col,row,1,&rect,&point);
		//send a notification to the main grid class
		m_ctrl->OnTH_LClicked(col,row,1,&rect,&point,processed);
	}
	
	SetCapture();
}

/************************************************
OnLButtonUp
	Finds the cell that was clicked in
	Sends a notification to CUCtrl::OnTH_LClicked
	Releases the mouse capture, and completes column swapping (if enabled)
Params:
	nFlags		- please see MSDN for more information on the parameters.
	point
Returns:
	<none>
*************************************************/
void CUGTopHdg::OnLButtonUp(UINT nFlags, CPoint point) 
{
	int col;
	int row;
	RECT rect;

	UNREFERENCED_PARAMETER(nFlags);

	if(m_isSizing){

		if(m_colOrRowSizing == 0)	//col sizing
		{
			//send notifications
			int width = m_ctrl->GetColWidth(m_sizingColRow);
			// send notifications to cell types.  In order to provide acceptable performance
			// this notification will only be sent to the visible rows.
			for(int nIndex=m_ctrl->GetTopRow(); nIndex<m_ctrl->GetBottomRow(); nIndex++)
			{
				CUGCellType* pCellType=m_ctrl->GetCellType(m_sizingColRow,nIndex);
				if(pCellType!=NULL)
				{
					pCellType->OnChangedCellWidth(m_sizingColRow,nIndex,&width);
				}
			}
			m_ctrl->OnColSized(m_sizingColRow,&width);
			if(width != m_ctrl->GetColWidth(m_sizingColRow))
				m_ctrl->SetColWidth(m_sizingColRow,width);
		}
		else
		{
			m_ctrl->OnTopHdgSized(&m_GI->m_topHdgHeight);
		}
		m_isSizing			= FALSE;

		//send notification
		m_ctrl->m_CUGGrid->InvalidateRect(NULL);
		m_ctrl->AdjustComponentSizes();
	}	
	else if(GetCellFromPoint(&point,&col,&row,&rect) == UG_SUCCESS)
	{
		//send a notification to the cell type	
		BOOL processed = m_ctrl->GetCellType(col,row)->OnLClicked(col,row,0,&rect,&point);
		//send a notification to the main grid class
		m_ctrl->OnTH_LClicked(col,row,0,&rect,&point,processed);

	}

	//column swapping
	if(m_GI->m_enableColSwapping && m_swapStartCol >= 0)
	{	
		int end = m_swapEndCol;
		if(m_swapStartCol < end) // this needs to be done since the internal
			end --;				 // calc. does not take into account that the 
								 // start col will be removed and all the cols
								 // are going to slide over one position
		if(m_ctrl->OnCanColSwap(m_swapStartCol,end) != FALSE)
		{
			m_ctrl->MoveColPosition(m_swapStartCol,m_swapEndCol,TRUE);

			// send OnColSwapped notification to inform user that the swap is completed
			m_ctrl->OnColSwapped(m_swapStartCol,end);

			//only redraw if swapping or a potential swap took place
			if(m_swapEndCol >=0)
			{
				m_swapEndCol = -1;	
				m_ctrl->RedrawAll();
			}
			else
			{
				Invalidate();
			}
		}
		else
		{
			m_swapEndCol = -1;
			m_ctrl->RedrawAll();
		}
	}

	//reset variables
	m_swapStartCol		= -1;
	m_swapEndCol		= -1;	
	m_focusRect.left	= -1;
	m_focusRect.right	= -1;

	ReleaseCapture();
}

/************************************************
OnLButtonDblClk
	Finds the cell that was clicked in and sends a notification to
	CUCtrl::OnTH_DClicked.
	If the user double clicked on the column separator area than calls
	BestFit on 20 rows starting on the top most visible row.  Once this
	operation is completed the OnChangedCellWidth, and OnColSized 
	notifications will be sent.
Params:
	<none>
Returns:
	<none>
*************************************************/
void CUGTopHdg::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	int		col,
			row;
	RECT	rect;
	BOOL	processed = FALSE;

	UNREFERENCED_PARAMETER(nFlags);

	if(m_canSize)
	{	
		//check to see if the column should be BestFit
		if(m_GI->m_userBestSizeFlag)
		{
			m_ctrl->BestFit(m_sizingColRow,m_sizingColRow,20,UG_BESTFIT_TOPHEADINGS);
			CheckForUserResize(&point);

			//send notifications
			int width = m_ctrl->GetColWidth(m_sizingColRow);
			// send notifications to cell types.  In order to provide acceptable performance
			// this notification will only be sent to the visible rows.
			for(int nIndex=m_ctrl->GetTopRow(); nIndex<m_ctrl->GetBottomRow(); nIndex++)
			{
				CUGCellType* pCellType=m_ctrl->GetCellType(m_sizingColRow,nIndex);
				if(pCellType!=NULL)
				{
					pCellType->OnChangedCellWidth(m_sizingColRow,nIndex,&width);
				}
			}
			m_ctrl->OnColSized(m_sizingColRow,&width);
			if(width != m_ctrl->GetColWidth(m_sizingColRow))
				m_ctrl->SetColWidth(m_sizingColRow,width);

			m_isSizing = FALSE;
		}
	}
	else if(GetCellFromPoint(&point,&col,&row,&rect) == UG_SUCCESS)
	{
		//send a notification to the cell type	
		processed = m_ctrl->GetCellType(col,row)->OnDClicked(col,row,&rect,&point);
		//send a notification to the main grid class
		m_ctrl->OnTH_DClicked(col,row,&rect,&point,processed);
	}
}

/************************************************
OnRButtonDown
	function sends a mouse click notification to the main grid class, 
	checks to see if menus are enabled. If so then menu specific
	notifications are sent as well.
Params:
	nFlags		- mouse button states
	point		- point where the mouse is
Return:
	<none>
*************************************************/
void CUGTopHdg::OnRButtonDown(UINT nFlags, CPoint point) 
{
	int col;
	int row;
	RECT rect;

	UNREFERENCED_PARAMETER(nFlags);

	if(GetFocus() != m_ctrl->m_CUGGrid)
		m_ctrl->m_CUGGrid->SetFocus();

	if(GetCellFromPoint(&point,&col,&row,&rect) == UG_SUCCESS)
	{
		//send a notification to the cell type	
		BOOL processed = m_ctrl->GetCellType(col,row)->OnRClicked(col,row,0,&rect,&point);
		//send a notification to the main grid class
		m_ctrl->OnTH_RClicked(col,row,1,&rect,&point,processed);
	}

	if(m_GI->m_enablePopupMenu)
	{
		ClientToScreen(&point);
		m_ctrl->StartMenu(col,row,&point,UG_TOPHEADING);
	}
}

/************************************************
OnRButtonUp
	Sends a mouse click notification to the main grid class.
Params:
	nFlags		- mouse button states
	point		- point where the mouse is
Return:
	<none>
*************************************************/
void CUGTopHdg::OnRButtonUp(UINT nFlags, CPoint point) 
{
	int col;
	int row;
	RECT rect;

	UNREFERENCED_PARAMETER(nFlags);

	if(GetCellFromPoint(&point,&col,&row,&rect) == UG_SUCCESS)
	{
		//send a notification to the cell type	
		BOOL processed = m_ctrl->GetCellType(col,row)->OnRClicked(col,row,0,&rect,&point);
		//send a notification to the main grid class
		m_ctrl->OnTH_RClicked(col,row,0,&rect,&point,processed);
	}
}

/************************************************
OnSetCursor
	The framework calls this member function if mouse input is not captured
	and the mouse causes cursor movement within the CWnd object.
Params:
	pWnd		- please see MSDN for more information on the parameters.
	nHitTest
	message
Returns:
	Nonzero to halt further processing, or 0 to continue.
*************************************************/
BOOL CUGTopHdg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	UNREFERENCED_PARAMETER(*pWnd);
	UNREFERENCED_PARAMETER(nHitTest);
	UNREFERENCED_PARAMETER(message);

	if(!m_canSize)
	{
		SetCursor(m_GI->m_arrowCursor);
		return 1;
	}
	else if(m_colOrRowSizing == 0)
		SetCursor(m_GI->m_WEResizseCursor);
	else
		SetCursor(m_GI->m_NSResizseCursor);

	return 1;
	
}

/************************************************
PreCreateWindow
	The Utlimate Grid overwrites this function to further customize the
	top heading window that is created.
Params:
	cs			- please see MSDN for more information on the parameters.
Returns:
	Nonzero if the window creation should continue; 0 to indicate creation failure.
*************************************************/
BOOL CUGTopHdg::PreCreateWindow(CREATESTRUCT& cs) 
{
	cs.style = cs.style | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

	return CWnd::PreCreateWindow(cs);
}

/************************************************
GetCellRect
	Returns the rectangle for the given cell co-ordinates.
	If the cell is joined then the given co-ordinates 
	are modified to point to the start cell for the join.
Params
	col		- column to find the rect of
	row		- row to find the rect of
	rect	- rectangle to calculate
Return
	UG_SUCCESS	- success
*************************************************/
int CUGTopHdg::GetCellRect(int col,long row,RECT *rect)
{
	return GetCellRect(&col,&row,rect);
}
/************************************************
GetCellRect
	Returns the rectangle for the given cell co-ordinates.
	If the cell is joined then the given co-ordinates 
	are modified to point to the start cell for the join.
Params
	col		- column to find the rect of
	row		- row to find the rect of
	rect	- rectangle to calculate
Return
	UG_SUCCESS	- success, this function will never fail
*************************************************/
int CUGTopHdg::GetCellRect(int *col,long *row,RECT *rect)
{
	int xIndex,yIndex;
	int width	= 0;
	int height	= 0;

	int startCol	= *col;
	int startRow	= *row;
	int endCol		= *col;
	int endRow		= *row;

	rect->left		= 0;
	rect->top		= 0;
	rect->right		= 0;
	rect->bottom	= m_GI->m_topHdgHeight;
	
	//if the specified cell is within a join then find the joined range
	if(m_GI->m_enableJoins)
	{
		if(GetJoinRange(&startCol,&startRow,&endCol,&endRow) == UG_SUCCESS)
		{
			*col = startCol;
			*row = startRow;
		}
	}

	//find the col
	if(startCol >= m_GI->m_numLockCols)	//if the col is not within the lock region
	{
		rect->left = m_GI->m_lockColWidth;
		rect->right = m_GI->m_lockColWidth;
	}

	for(xIndex=0;xIndex<m_GI->m_numberCols;xIndex++)
	{
		if(xIndex == m_GI->m_numLockCols)
			xIndex = m_GI->m_leftCol;		
		
		if(xIndex == startCol)
			rect->left = width;
		
		width += m_ctrl->GetColWidth(xIndex);

		if(xIndex == endCol)
		{
			rect->right = width;
			break;
		}
	}

	//find the row
	for(yIndex= (m_GI->m_numberTopHdgRows * -1);yIndex < 0;yIndex++)
	{			
		if(yIndex == startRow)
			rect->top = height;

		height += GetTHRowHeight(yIndex);

		if(yIndex == endRow)
		{
			rect->bottom = height;	
			break;
		}
	}

	return UG_SUCCESS;
}

/************************************************
GetCellFromPoint
	Returns the column and row that lies over the given x,y co-ordinates.
	The co-ordinates are relative to the top left hand corner of the grid.
Params:
	point - point to check
	col	  - column that lies over the point
	row   - row that lies over the point
	rect  - rectangle of the cell found
Return:
	UG_SUCCESS	- if a matching cell was found 
	UG_ERROR	- point provided does not correspond to a cell
*************************************************/
int CUGTopHdg::GetCellFromPoint(CPoint *point,int *col,int *row,RECT *rect)
{
	int ptsFound = 0;
	int xIndex,yIndex;

	rect->left		=0;
	rect->top		=0;
	rect->right		=0;
	rect->bottom	=0;

	//find the col
	for(xIndex=0;xIndex<m_GI->m_numberCols;xIndex++)
	{	
		if(xIndex == m_GI->m_numLockCols)
			xIndex = m_GI->m_leftCol;
		
		rect->right += m_ctrl->GetColWidth(xIndex);

		if(rect->right > point->x)
		{
			rect->left = rect->right - m_ctrl->GetColWidth(xIndex);
			ptsFound ++;
			*col = xIndex;
			break;
		}
	}

	//find the row
	for(yIndex = -m_GI->m_numberTopHdgRows; yIndex< 0;yIndex++)
	{
		rect->bottom += GetTHRowHeight(yIndex);

		if(rect->bottom > point->y)
		{
			rect->top = rect->bottom - GetTHRowHeight(yIndex);
			ptsFound ++;
			*row = yIndex;
			break;
		}
	}

	if(ptsFound == 2)
	{
		// check if the found cell is part of a join, if
		// it is than adjust the information found
		int edCol, edRow;

		if ( GetJoinRange( col, row, &edCol, &edRow ) == UG_SUCCESS )
		{
			long tempRow = *row;
			GetCellRect( col, &tempRow, rect );
			*row = tempRow;
		}

		return UG_SUCCESS;
	}

	*col = -1;
	*row = -1;

	return UG_ERROR;
}

/***************************************************
GetJoinRange
	function returns join information related to the cell specified
	in the col and row parameters.  These parameters might be changed
	to represent starting col, row position of the join.
Params:
	col, row	- identify the cell to retrieve join information on,
				  these parameters will represent the starting pos
				  of the join.
	endCol,		- will be set to the end col, row position of the
	endRow		  join.
Returns:
	UG_SUCCESS	- on success
	UG_ERROR	- if joins are disabled, or cell specified is not part of a join
*****************************************************/
int CUGTopHdg::GetJoinRange(int *col,int *row,int *endCol,int *endRow)
{
	if(m_GI->m_enableJoins == FALSE)
		return UG_ERROR;

	int startCol;
	long startRow, joinRow;
	BOOL origin;
	CUGCell cell;

	m_ctrl->GetCellIndirect(*col,*row,&cell);
	if(cell.IsPropertySet(UGCELL_JOIN_SET) == FALSE)
		return UG_ERROR;

	cell.GetJoinInfo(&origin,&startCol,&startRow);
	if(!origin)
	{
		*col += startCol;
		*row += (int)startRow;
		m_ctrl->GetCellIndirect(*col,*row,&cell);
	}

	cell.GetJoinInfo(&origin,endCol,&joinRow);
	*endCol +=*col;
	*endRow = joinRow + *row;

	return UG_SUCCESS;
}

/***************************************************
GetTHRowHeight
	function returns height (in pixels) of the top heading row that user is
	interested in.  The row number passed in has to be a negaitve value ranging
	from zero to negative value of number of top heading rows set.
	((m_GI->m_numberTopHdgRows) * -1)
Params:
	row			- indicates row of interest
Returns:
	height in pixels of the row in question, or zero if
	row not found.
*****************************************************/
int CUGTopHdg::GetTHRowHeight(int row)
{
	//translate the row number into a 0 based positive index
	row = (row * -1) -1;

	if(row <0 || row > m_GI->m_numberTopHdgRows)
		return 0;

	return m_GI->m_topHdgHeights[row];
}

/************************************************
OnHelpHitTest
	Sent as a result of context sensitive help
	being activated (with mouse) over top heading
Params:
	WPARAM - not used
	LPARAM - x, y coordinates of the mouse event
Return:
	Context help ID to be displayed
*************************************************/
LRESULT CUGTopHdg::OnHelpHitTest(WPARAM, LPARAM lParam)
{
	// this message is fired as result of mouse activated
	// context help being hit over the grid.
	int col, row;
	CRect rect(0,0,0,0);
	CPoint point( LOWORD(lParam), HIWORD(lParam));
	GetCellFromPoint( &point, &col, &row, &rect );
	// return context help ID to be looked up
	return m_ctrl->OnGetContextHelpID( col, row, UG_TOPHEADING );
}

/************************************************
OnHelpInfo
	Sent as a result of context sensitive help
	being activated (with mouse) over top heading
	if the grid is on the dialog
Params:
	HELPINFO - structure that contains information on selected help topic
Return:
	TRUE or FALSE to allow further processing of this message
*************************************************/
BOOL CUGTopHdg::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	if (pHelpInfo->iContextType == HELPINFO_WINDOW)
	{
		// this message is fired as result of mouse activated help in a dialog
		int col, row;
		CRect rect(0,0,0,0);
		CPoint point;
		point.x = pHelpInfo->MousePos.x;
		point.y = pHelpInfo->MousePos.y;

		ScreenToClient( &point );
		GetCellFromPoint( &point, &col, &row, &rect );
		// call context help with ID returned by the user notification
		AfxGetApp()->WinHelp( m_ctrl->OnGetContextHelpID( col, row, UG_TOPHEADING ));
		// prevent further handling of this message
		return TRUE;
	}
	return FALSE;
}

/***************************************************
OnToolHitTest
	The framework calls this member function to detemine whether a point is in
	the bounding rectangle of the specified tool. If the point is in the
	rectangle, it retrieves information about the tool.
Params:
	point	- please see MSDN for more information on the parameters.
	pTI
Returns:
	If 1, the tooltip control was found; If -1, the tooltip control was not found.
*****************************************************/
// v7.2 - update 02 - 64-bit - changed from int to UGINTRET - see UG64Bit.h
UGINTRET CUGTopHdg::OnToolHitTest(  CPoint point, TOOLINFO *pTI ) const
{
	int col, row;
	CRect rect;
	static int lastCol = -2;
	static int lastRow = -2;

	if( m_ctrl->m_CUGTopHdg->GetCellFromPoint( &point, &col, &row, &rect ) == UG_SUCCESS)
	{
		if(col != lastCol || row != lastRow)
		{
			lastCol = col;
			lastRow = row;
			CancelToolTips();
			return -1;
		}

		// v7.2 - update 03 - added TTF_TRANSPARENT flag. This prevents a 
		//        recursive flicker of large tooltips that are forced to 
		//        encroach on the mouse position. Reported by Kuerscht
		pTI->uFlags =  TTF_TRANSPARENT | TTF_NOTBUTTON | TTF_ALWAYSTIP |TTF_IDISHWND ;
		pTI->uId = (UINT_PTR)m_hWnd;
		pTI->hwnd = (HWND)m_hWnd;
		pTI->lpszText = LPSTR_TEXTCALLBACK;
		return 1;
	}
	return -1;
}

/***************************************************
ToolTipNeedText (TTN_NEEDTEXT)
	message handler will be called whenever a text is required for a tool tip.
	The Ultimate Grid uses the CUGCtrl::OnHint virtual function to obtain
	proper text to be displayed in the too tip.
Params:
		id			- Identifier of the control that sent the notification. Not
					  used. The control id is taken from the NMHDR structure.
		pTTTStruct	- A pointer to theNMTTDISPINFO structure. This structure is
					  also discussed further in The TOOLTIPTEXT Structure.
		pResult		- A pointer to result code you can set before you return.
					  TTN_NEEDTEXT handlers can ignore the pResult parameter.
Returns:
	<none>
*****************************************************/
BOOL CUGTopHdg::ToolTipNeedText( UINT id, NMHDR* pTTTStruct, LRESULT* pResult )
{
	UNREFERENCED_PARAMETER(id);
	UNREFERENCED_PARAMETER(*pResult);

	TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pTTTStruct;

	static CString string;
	int col;
	int row;
	CPoint point;
	CRect rect;

	GetCursorPos(&point);
	ScreenToClient(&point);

	if ( GetCellFromPoint( &point,&col,&row, &rect ) == UG_SUCCESS )
	{
		if ( m_ctrl->OnHint(col,row,UG_TOPHEADING,&string) == TRUE )
		{
			// v7.2 - update 01 - Do this to Enable multiline ToolTips - reported by kassinen
			::SendMessage( pTTT->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, SHRT_MAX );
			::SendMessage( pTTT->hdr.hwndFrom, TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX );
			::SendMessage( pTTT->hdr.hwndFrom, TTM_SETDELAYTIME, TTDT_INITIAL, 200 );
			::SendMessage( pTTT->hdr.hwndFrom, TTM_SETDELAYTIME, TTDT_RESHOW, 200 );
 			pTTT->lpszText = const_cast<LPTSTR>((LPCTSTR)string);
			return TRUE;
		}
	}

	return FALSE;
}

// v7.2 update 04 - added to implement WM_PRINT handling for the top heading - TD 
LRESULT CUGTopHdg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	UNREFERENCED_PARAMETER(lParam);

	switch (message)
	{
		// TD - added to implement WM_PRINT handling for the grid
	case WM_PRINT:
		// draw all top heading cells to the DC passed in
		InvalidateRect(NULL);
		m_drawHint.AddHint(0,m_GI->m_numberTopHdgRows * -1,m_GI->m_numberCols,0);
		DrawCellsIntern(CDC::FromHandle((HDC)wParam));
		return 0;
	default:
		return CWnd::WindowProc(message, wParam, lParam);
	}
}

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
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions