Click here to Skip to main content
15,895,011 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 256.1K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
/*************************************************************************
				Class Implementation : CUGExpandType
**************************************************************************
	Source file : UGCTrado.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 "UGCTExpand.h"

#ifdef UGEXPANDUSEMAIL
// This is needed for the mark all read/unread functionality
#include "UGCTMail.h"
#endif

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

/***************************************************
CUGExpandType - Constructor
****************************************************/
CUGExpandType::CUGExpandType()
{

	m_canOverLap = TRUE;
	SetTextEditMode(FALSE);
	m_font.CreateFont(13, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, 0, OUT_DEFAULT_PRECIS, 
		CLIP_DEFAULT_PRECIS,   DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("Tahoma"));

}
/***************************************************
~CUGExpandType - Destructor
****************************************************/
CUGExpandType::~CUGExpandType()
{
}

/***************************************************
GetName  - overloaded CUGCellType::GetName
	Returns a readable name for the cell type.
	Returned value is used to help end-users
	to see what cell type are available.

    **See CUGCellType::GetName for more details
	about this function

Params:
	<none>
Return:
	cell type name
****************************************************/
LPCTSTR CUGExpandType::GetName()
{
	return _T("Expand Button");
}

/***************************************************
GetUGID  - overloaded CUGCellType::GetUGID
	Returns a GUID for the cell type, this number
	is unique for each cell type and never changes.
	This number can be used to find the cell types
	added to this instance of the Ultimate Grid.

    **See CUGCellType::GetUGID for more details
	about this function

Params:
	<none>
Returns:
	UGID (which is actually a GUID)
****************************************************/
LPCUGID CUGExpandType::GetUGID()
{
	static const UGID ugid = 
	{ 0x3ED578E, 0xBE0C, 0x11DA, { 0xB0, 0x12, 0xB6, 0x22, 0xA1, 0xEF, 0x54, 0x92 } };

	return &ugid;
}

/***************************************************
ExpandOrContract  - This method toggles the cell at
	the selected position to expand or contract, 
	which basically means walking from that cell until 
	we find one that is of the Expand cell type and 
	either hiding or showing the cells

Params:
	col - the column of the cell to expand from
	row	- the row of the cell to expand from
Return:
	none
****************************************************/
// v7.2 - update 03 - added OnCellTypeNotify call.
void CUGExpandType::ExpandOrContract(int col, int row)
{
    CUGCtrl * ctrl = this->m_ctrl;

    CUGCell thisCell;
    ctrl->GetCell(col, row, &thisCell);

    CellData * cellData = GetCellData(col, row);

    int numberOfRows = cellData->numberOfRows;

	// v7.2 - update 03 - incorporate OnCellTypeNotify callback to allow ID
	// of operation - param indicates current state - if TRUE, expanded 
	// and about to collapse, FALSE collapsed and about to expand.
	// Return FALSE from derived CUGCtrl::OnCellTypeNotify to abort action.
    if(m_ctrl->OnCellTypeNotify(m_ID, col, row, UGCT_EXPANDCONTRACT, cellData->isOpen) == FALSE) {
        return;
    }

	bool isOpen = !cellData->isOpen;

	if (cellData->isSelected)
	{
		cellData->isOpen = isOpen;
	}
	else
	{
		m_ctrl->Invalidate();
		return;
	}

	int initialRow = row;

	CUGCell cell;
	while(true)
	{
		if (row >= ctrl->GetNumberRows())
			break;

		if (numberOfRows > 0 && row - initialRow == numberOfRows)
			break;

		// UG_ERROR is returned in cases where we don't mind, 
		// specifically, if the grid can't work out the cell type.
		// So, we don't do anything in that case, and we don't check 
		// the return type.
		ctrl->GetCell(col, ++row, &cell);

		if (cell.GetCellType() == thisCell.GetCellType())
		{
			break;
		}

        if (!isOpen)
        {
            int height = ctrl->GetRowHeight(row);;

            // v7.2 - update 03 - 0 row height was not stored, causing
			//        rows that were hidden to 'reappear' on collapse/expand.
			//		  Reported by atillaselem
//			if (height > 0)
            if (height >= 0)
            {
                m_mapColumnHeights[row] = height;
                height = 0;
                ctrl->OnRowSizing(row,&height);
                ctrl->SetRowHeight(row,height);
            }				
        }
        else
        {
			int height = m_mapColumnHeights[row];
			ctrl->OnRowSizing(row,&height);
			ctrl->SetRowHeight(row,height);
		}

		ctrl->Invalidate();
	}
}

/***************************************************
OnLClicked - overloaded CUGCellType::OnLClicked
	Checks to see if the area where the radio button 
	is drawn within the cell has been clicked.
	If so then the state of the 

    **See CUGCellType::OnLClicked for more details
	about this function
****************************************************/
BOOL CUGExpandType::OnLClicked(int col,long row,int updn,RECT *rect,POINT *point)
{
	if(updn)
	{
		// Work out if the user clicked on the box, and if so,
		// set the property to true so that it will expand/contract
		RECT rcBox = *rect;
		SIZE sz;

		if (!UGXPThemes::GetThemePartSize(L"TREEVIEW", NULL, 2, 1, &rcBox, 2, &sz))
		{
			sz.cx = sz.cy = 11;
		}

		const int height = rect->bottom - rect->top;
		const int offset = (height - sz.cy) /2;

		rcBox.top = rect->top + offset;
		rcBox.bottom = rcBox.top + sz.cy;
		rcBox.left = rect->left + offset;
		rcBox.right = rcBox.left + sz.cx;

		if (PtInRect(&rcBox, *point))
		{
			CellData * cellData = GetCellData(col, row);

			cellData->isSelected = cellData->wasSelected = true;
//			m_mapSelectedColumns[row].first = m_mapSelectedColumns[row].second = true;
		}
			
		ExpandOrContract(col, row);
			
		return TRUE;
	}
	return FALSE;
}

/***************************************************
OnRClicked - overloaded CUGCellType::OnRClicked
	Checks to see if the area where the radio button 
	is drawn within the cell has been clicked.
	If so then the state of the 

    **See CUGCellType::OnRClicked for more details
	about this function
****************************************************/
BOOL CUGExpandType::OnRClicked(int col,long row,int updn,RECT *rect,POINT *point)
{
	BOOL retval = FALSE;

	if(updn)
	{
		m_ctrl->OnCellTypeNotify(m_ID, col, row, 0, (LONG_PTR)point);

		retval = TRUE;
	}

	return retval;
}

/***************************************************
OnKeyDown - overloaded CUGCellType::OnKeyDown

    **See CUGCellType::OnKeyDown for more details
	about this function
****************************************************/
BOOL CUGExpandType::OnKeyDown(int col,long row,UINT *vcKey)
{
	UNREFERENCED_PARAMETER(col);
	UNREFERENCED_PARAMETER(row);

	if ( *vcKey == VK_SPACE )
	{	
		ExpandOrContract(col, row);

		return TRUE;
	}
	return FALSE;
}

/***************************************************
OnDraw - overloaded CUGCellType::OnDraw

    **See CUGCellType::OnDraw for more details
	about this function
****************************************************/
void CUGExpandType::OnDraw(CDC *dc,RECT *rect,int col,long row,
						 CUGCell *cell,int selected,int current)
{
	COLORREF foreCol = RGB(49,106,197);
	COLORREF textCol = RGB(55, 104, 185);

	if (!m_drawThemesSet)
		m_useThemes = cell->UseThemes();

	CellData * cellData = GetCellData(col, row);

	UNREFERENCED_PARAMETER(col);
	UNREFERENCED_PARAMETER(row);
	UNREFERENCED_PARAMETER(selected);
	UNREFERENCED_PARAMETER(current);
	
	cellData->isSelected = (cellData->wasSelected && current > 0);
	cellData->wasSelected = (current>0);

	bool isOpen = cellData->isOpen;

	DrawBorder(dc,rect,rect,cell);

	long properties = cell->GetPropertyFlags();

	int saveID = dc->SaveDC();
	
	CRgn rgn;
	rgn.CreateRectRgnIndirect(rect);
	dc->SelectClipRgn(&rgn);

	int alignment;
	if(properties&UGCELL_ALIGNMENT_SET)
		alignment = cell->GetAlignment();
	else
		alignment = 0;

	CFont * pFont = NULL;
	//select the font
	if(properties&UGCELL_FONT_SET )
		pFont = dc->SelectObject(cell->GetFont());

	RECT rcBox = *rect;
	SIZE sz;

	if (!UGXPThemes::GetThemePartSize(L"TREEVIEW", NULL, 2, ((isOpen) ? 2 : 1), &rcBox, 2, &sz))
	{
		sz.cx = sz.cy = 11;
	}

	const int height = rect->bottom - rect->top;
	const int offset = height - (int)(sz.cy * 1.5);
	rcBox.top = rect->top + offset;
	rcBox.bottom = rcBox.top + sz.cy;
	rcBox.left = rect->left + 3;
	rcBox.right = rcBox.left + sz.cx;

	RECT rcText = rcBox;
	rcText.top -= 3;
	rcText.right = rect->right;
	rcText.left = rcBox.right + 8;

	//check the selected and current states
	if(selected || current)
	{
		dc->SetTextColor(RGB(255, 255, 255));
	}
	else
	{
		dc->SetTextColor(textCol);
	}

	// Try drawing with themes first
	// This will just fail if themes are turned off.
	bool xpThemeSucceeded = UGXPThemes::DrawBackground(NULL, *dc, L"HEADER", 1, 2, rect, rect);
	xpThemeSucceeded &= UGXPThemes::DrawBackground(NULL, *dc, L"TREEVIEW", 2, ((isOpen) ? 2 : 1), &rcBox, &rcBox);
	xpThemeSucceeded &= UGXPThemes::WriteText(NULL, *dc, _T("TREEVIEW"), 2, ((isOpen) ? 2 : 1), cell->GetText(),
		cell->GetTextLength(), DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS, &rcText);

	if (!xpThemeSucceeded)
	{
		dc->FillSolidRect(rect, (selected >0 || current > 0) ? foreCol : RGB(255, 255, 255));

		if (!m_buttonBack.m_hObject)
		{
			MakeBitmap(dc, rcBox);
		}

		CDC dcButton;
		dcButton.CreateCompatibleDC(dc);
		CBitmap * pOldBitmap = dcButton.SelectObject(&m_buttonBack);
		dc->BitBlt(rcBox.left, rcBox.top, rcBox.right - rcBox.left, rcBox.bottom - rcBox.top, &dcButton, 0, 0, SRCCOPY);

		dcButton.SelectObject(pOldBitmap);

		CGdiObject * oldPen = dc->SelectStockObject(BLACK_PEN);
		dc->MoveTo(rcBox.left + 3, rcBox.top + sz.cy / 2);
		dc->LineTo(rcBox.right - 3, rcBox.top + sz.cy / 2);

		if (!isOpen)
		{
			const int middleX = rcBox.left + (rcBox.right - rcBox.left) / 2;
			dc->MoveTo(middleX, rcBox.top + 3);
			dc->LineTo(middleX, rcBox.bottom - 3);
		}

		dc->SelectObject(oldPen);

		CPen lowerPen(PS_SOLID, 2, RGB(123,163,224));

		oldPen = dc->SelectObject(&lowerPen);
		dc->MoveTo(rect->left, rect->bottom-1);
		dc->LineTo(rect->right, rect->bottom-1);
		dc->SelectObject(oldPen);

		CFont * pOldFont = dc->SelectObject(&m_font);
		dc->DrawText(cell->GetText(), &rcText, DT_END_ELLIPSIS);
		dc->SelectObject(pOldFont);
	}

	if (pFont)
		dc->SelectObject(pFont);

	dc->RestoreDC(saveID);
}

/****************************************************
GetBestSize
	Returns the best (nominal) size for a cell using
	this cell type, with the given cell properties.
Params:
	dc		- device context to use to calc the size on	
	size	- return the best size in this param
	cell	- pointer to a cell object to use for the calc.
Return:
	<none>
*****************************************************/
void CUGExpandType::GetBestSize(CDC *dc,CSize *size,CUGCell *cell)
{
	CRect rect(0,0,0,0);
	CFont* oldFont = NULL;

	if( cell->IsPropertySet( UGCELL_FONT_SET ))
		oldFont = dc->SelectObject( cell->GetFont());
	// get height of each item
	CSize itemSize = dc->GetTextExtent(_T("X"),1);
	// calculate the rect needed to draw the text
	dc->DrawText( cell->GetLabelText(), rect, DT_CALCRECT );
	dc->SelectObject( oldFont );
	// combine the results
	size->cx = rect.Width() + itemSize.cx + 8;
}

/****************************************************
MakeBitmap
	This method draws the background for the expand/
	contract button, so we don't have the hand draw it
	every time a paint event occurs.
Params:
	dc		- device context to draw bitmap onto
	rc		- rect to draw bitmap onto
Return:
	<none>
*****************************************************/

void CUGExpandType::MakeBitmap(CDC * source, RECT rc)
{
	int r1=255,g1=255,b1=255;
	int r2=176,g2=200,b2=240;

	int x1=1,y1=1;
	int x2=0,y2=0;

	CRect rect(0, 0, rc.right - rc.left, rc.bottom - rc.top);

	CDC dc;
	dc.CreateCompatibleDC(source);

	m_buttonBack.CreateCompatibleBitmap(source, rect.Width(), rect.Height());

	CBitmap *oldbmap = dc.SelectObject(&m_buttonBack);

	CPen p(PS_SOLID,1,RGB(r1,g1, b1));
	CPen *oldpen = dc.SelectObject(&p); 

	dc.MoveTo(0,0);
	dc.LineTo(rect.Width(), 0);

	dc.SelectObject(oldpen);

	while(x1 < rect.Width()-1 && y1 < rect.Height())
	{
		if(y1 < rect.Height()-1)
			y1++;
		else
			x1++;

		if(x2 < rect.Width()-1)
			x2++;
		else
			y2++;

		int i = x1+y1;
		int r = r1 + (i * (r2-r1) / (rect.Width()+rect.Height()-2));
		int g = g1 + (i * (g2-g1) / (rect.Width()+rect.Height()-2));
		int b = b1 + (i * (b2-b1) / (rect.Width()+rect.Height()-2));

		CPen p(PS_SOLID,1,RGB(r,g,b));

		CPen *oldpen = dc.SelectObject(&p); 

		dc.MoveTo(x1,y1);
		dc.LineTo(x2,y2);


		dc.SelectObject(oldpen);
	} 

	const int stepX = (rect.Width() - 2)/3;
	const int stepY = (rect.Height() - 2)/3;

	int x = 1;
	int y = 1;

	CPen p1(PS_SOLID, 1, RGB(160, 176, 192));

	CPen * oldPen = dc.SelectObject(&p1);
	dc.MoveTo(x, 0);
	dc.LineTo(x+stepX, 0);
	dc.MoveTo(0, y);
	dc.LineTo(0, y+stepY);
	x+=stepX;
	y+=stepY;

	dc.SelectObject(oldPen);

	CPen p2(PS_SOLID, 1, RGB(160, 168, 192));

	oldPen = dc.SelectObject(&p2);
	dc.MoveTo(x, 0);
	dc.LineTo(x+stepX, 0);
	dc.MoveTo(0, y);
	dc.LineTo(0, y+stepY);
	x+=stepX;
	y+=stepY;

	dc.SelectObject(oldPen);

	CPen p3(PS_SOLID, 1, RGB(144, 160, 192));

	oldPen = dc.SelectObject(&p3);
	dc.MoveTo(x, 0);
	dc.LineTo(x+stepX, 0);
	dc.MoveTo(0, y);
	dc.LineTo(0, y+stepY);

	dc.SelectObject(oldPen);

	CPen p4(PS_SOLID, 1, RGB(48, 72, 96));
	oldPen = dc.SelectObject(&p3);
	dc.MoveTo(rect.Width()-1, 1);
	dc.LineTo(rect.Width()-1, rect.Height() - 1);
	dc.LineTo(0, rect.Height() - 1);
	dc.SelectObject(oldPen);

	dc.SetPixel(0, 0, RGB(231, 235, 239));
	dc.SetPixel(0, rect.Height() - 1, RGB(203, 209, 215));
	dc.SetPixel(rect.Width()-1, rect.Height() - 1, RGB(203, 209, 215));
	dc.SetPixel(rect.Width()-1, 0, RGB(203, 209, 215));

	dc.SelectObject(oldbmap);
}

/****************************************************
ExpandAll
	Expands all rows using the column we specify to 
	to search for CUGCTExpand cells.
Params:
	col - the column which contains the expand cells
Return:
	<none>
*****************************************************/

void CUGExpandType::ExpandAll(int col)
{
	if (col >=0 && col < m_ctrl->GetNumberCols())
	{
		const int rowCount = m_ctrl->GetNumberRows();

		for(int row = 0; row < rowCount; ++row)
		{
			if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID())
			{
				CellData * cellData = GetCellData(col, row);
				if (!cellData->isOpen)
				{
					CellData * cellData = GetCellData(col, row);
					cellData->isSelected = true;
					ExpandOrContract(col, row);
				}
			}
		}
		m_ctrl->Invalidate();
	}
}

/****************************************************
CollapseAll
	Collapses all rows using the column we specify to 
	to search for CUGCTExpand cells.
Params:
	col - the column which contains the expand cells
Return:
	<none>
*****************************************************/
void CUGExpandType::CollapseAll(int col)
{
	if (col >=0 && col < m_ctrl->GetNumberCols())
	{
		const int rowCount = m_ctrl->GetNumberRows();

		for(int row = 0; row < rowCount; ++row)
		{
			if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID())
			{
				CellData * cellData = GetCellData(col, row);
				if (!cellData->isOpen)
				{
					cellData->isSelected = true;
					ExpandOrContract(col, row);
				}
			}
		}
		m_ctrl->Invalidate();
	}
}

/****************************************************
MarkAllRead
	Walks through the column specified, starting at 
	the row specified, looking for all mail cells in 
	the group, in order to mark them all as read.
	This function and MarkAllUnread make this class
	tightly coupled to the mail cell type, they would
	need to be removed to use this class without that
	dependancy
Params:
	col - the column which contains the expand cells
	row - the first row from which to mark mail cells 
	      as read.
Return:
	<none>
*****************************************************/
#ifdef UGEXPANDUSEMAIL
void CUGExpandType::MarkAllRead(int col, int row)
{
	if (col >=0 && col < m_ctrl->GetNumberCols())
	{
		const int rowCount = m_ctrl->GetNumberRows();

		// Make sure we're starting from a expanding cell
		if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID())
		{
			CUGCell cell;

			for(++row;row < rowCount; ++row)
			{
				// Stop when we get to the next one
				if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID()) 
					break;

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

				CUGCTMail::mailItem item = CUGCTMail::ParseString(&cell);
				
				if (item.didParseOK)
					item.isRead = true;

				CUGCTMail::SetString(item, col, row, &cell, m_ctrl);
			}
		}
		m_ctrl->Invalidate();
	}

}

/****************************************************
MarkAllUnread
	Walks through the column specified, starting at 
	the row specified, looking for all mail cells in 
	the group, in order to mark them all as unread.
	This function and MarkAllRead make this class
	tightly coupled to the mail cell type, they would
	need to be removed to use this class without that
	dependancy
Params:
	col - the column which contains the expand cells
	row - the first row from which to mark mail cells 
		  as unread.
Return:
	<none>
*****************************************************/

void CUGExpandType::MarkAllUnread(int col, int row)
{
	if (col >=0 && col < m_ctrl->GetNumberCols())
	{
		const int rowCount = m_ctrl->GetNumberRows();

		// Make sure we're starting from a expanding cell
		if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID())
		{
			CUGCell cell;

			for(++row;row < rowCount; ++row)
			{
				// Stop when we get to the next one
				if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID()) 
					break;

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

				CUGCTMail::mailItem item = CUGCTMail::ParseString(&cell);
				
				if (item.didParseOK)
					item.isRead = false;

				CUGCTMail::SetString(item, col, row, &cell, m_ctrl);
			}
		}
		m_ctrl->Invalidate();
	}
}
#endif // defined UGEXPANDUSEMAIL

/****************************************************
ShowGroups
	Walks through all columns and rows, looking for 
	this cell type, and either hides ( by contraction)
	or shows the grouping cells.  As a result, in a grid
	that has more than one column, it will hide entire rows
	as there is no other way to hide a cell.
	We use a map to store the old row heights in order to 
	be able to restore them correctly when asked to.
Params:
	show - sets if we show or hide the cells.
Return:
	<none>
*****************************************************/
void CUGExpandType::ShowGroups(bool show)
{
	const int rowCount = m_ctrl->GetNumberRows();
	const int colCount = m_ctrl->GetNumberCols();

	for(int col=0;col<colCount;++col)
	{
		for(int row=0;row<rowCount;++row)
		{
			if (m_ctrl->GetCellType(col, row)->GetID() == this->GetID()) 
			{
				int height = m_ctrl->GetRowHeight(row);

				// If we're contracting, first store the height
				if (height) m_mapColumnHeights[row] = height;

				m_ctrl->SetRowHeight(row, (show) ? m_mapColumnHeights[row] : 0);
			}
		}
	}
}

/****************************************************
ResetCache
	When we change the sort order, we call this method 
	in order to reset all our caches, otherwise these 
	maps may contain values that are no longer valid, as
	they all store values by row number, which will have 
	changed.
Params:
	<none>
Return:
	<none>
*****************************************************/
void CUGExpandType::ResetCache()
{
	m_mapColumnHeights.clear();
}

/****************************************************
SetNumberOfRows
	Information about the state of the cell is stored 
	in the base cell class, via the ExtraMemory functionality.
	This struct includes a number of rows to expand/contract,
	which defaults to zero, but can be set with this function.
	When it is zero, the cells expand or contract to the next 
	cell of the expanding type.
Params:
	row - The row the expanding cell is on
	col - The column the expanding cell is on
	rows - The number of rows to expand or contract
Return:
	<none>
*****************************************************/
void CUGExpandType::SetNumberOfRows(int col, int row, int rows)
{
	CUGCell cell;

	// Make sure we're setting the number of rows on an expanding cell
	ASSERT(m_ctrl->GetCellType(col, row)->GetID() == this->GetID());

	CellData * ptr = GetCellData(col, row);
	
	ptr->numberOfRows = rows;
}

/****************************************************
GetCellData
	Information about the state of the cell is stored 
	in the base cell class, via the ExtraMemory functionality.
	This method encapsulates ensuring that this memory has been
	set ( or initialising it if necessary ), and casting the 
	return value to our CellData struct, which is internal to 
	this class.
Params:
	row - The row the expanding cell is on
	col - The column the expanding cell is on
Return:
	<none>
*****************************************************/
CUGExpandType::CellData* CUGExpandType::GetCellData(int col, int row)
{
	CUGCell cell;
	m_ctrl->GetCell(col, row, &cell);

	if (!cell.GetExtraMemPtr())
	{
		cell.AllocExtraMem(sizeof(CellData));
		::memset(cell.GetExtraMemPtr(), 0, sizeof(CellData));
		CellData* ptr = (CellData*)cell.GetExtraMemPtr();
		ptr->isOpen = true;
		ptr->isSelected = ptr->wasSelected = false;
		m_ctrl->SetCell(col, row, &cell);
	}

	return (CellData*)cell.GetExtraMemPtr();
}

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