Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / Win32

Creation and memory mapping existing DBF files as alternative of data serialization during work with modified CListCtrl classes in virtual mode on dialogs of MDI application

Rate me:
Please Sign up or sign in to vote.
4.64/5 (6 votes)
22 Jul 2009CPOL11 min read 47K   1.5K   26  
The demonstration of reading, writing and creation of standard DBF files with random access in memory instead of serialization of typical MFC application for descendants of CListCtrl classes in Virtual Mode.
/////////////////////////////////////////////////////////////////////////////
// ListCtrlEx.cpp : implementation file
/////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ListCtrlEx.h"

#include "..\MainView.h"
#include "..\Dbf\Dbf.h"

/////////////////////////////////////////////////////////////////////////////
// MESSAGE MAP
/////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
	//{{AFX_MSG_MAP(CChildFrame)
	ON_WM_MEASUREITEM_REFLECT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx
/////////////////////////////////////////////////////////////////////////////
CListCtrlEx::CListCtrlEx() {
	//*** Main application pointer
	m_pMainApp = reinterpret_cast<CMainApp *>(AfxGetApp());

	if(!m_pMainApp) {
		_M("CListCtrlEx: Empty object of the CMainApp class!");
		return;
	}

	//*** Table Id
	m_eTable = m_pMainApp->m_eTable;

	//*** Current meta table structure
	m_MetaTable = m_pMainApp->m_aMetaTable[m_eTable];

	//*** Current document
	m_pDoc = NULL;
}  // CListCtrlEx

/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx
/////////////////////////////////////////////////////////////////////////////
CListCtrlEx::~CListCtrlEx() {
}  // ~CListCtrlEx

/////////////////////////////////////////////////////////////////////////////
// MeasureItem
/////////////////////////////////////////////////////////////////////////////
void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT pMIS) {
	//*** The table list height
	pMIS->itemHeight = m_MetaTable.nListHeight;
}  // MeasureItem

/////////////////////////////////////////////////////////////////////////////
// OnChildNotify
/////////////////////////////////////////////////////////////////////////////
BOOL CListCtrlEx::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pResult) {
	NMHDR *pNMHdr = reinterpret_cast<NMHDR *>(lParam);

	LV_DISPINFO *pLVDI = reinterpret_cast<LV_DISPINFO *>(lParam);
	LV_ITEM *pItem = &pLVDI->item;

	if(message == WM_NOTIFY) {
		switch(pNMHdr->code) {
			case LVN_GETDISPINFO: {
				if(pItem->mask & LVIF_TEXT) {
					//*** Item row
					UINT nRow = pItem->iItem;

					//*** Item column
					UINT nCol = pItem->iSubItem;

					//*** The message buffer
					TCHAR szStr[MAXITEMTEXT];

					//*** Current document
					m_pDoc = m_pMainApp->m_apDoc[m_eTable];

					if(!m_pDoc) {
						_M("CListCtrlEx::CListCtrlEx : Empty document!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Number of fields in dbf file
					ULONG nColCount = m_pDoc->m_nFldCount;  // = m_MetaTable.nColCount;

					if(nColCount == 0) {
						_M("CListCtrlEx::OnChildNotify : Column count = 0!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					if(m_MetaTable.nColCount != nColCount) {
						swprintf(
								szStr, 
								_T("CListCtrlEx::OnChildNotify : Table (%d) and Dbf (%d) columns are different!"), 
								m_MetaTable.nColCount, 
								nColCount
						);

						_M(szStr);
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Number of records in dbf file
					ULONG nRowCount = m_pDoc->m_nRecCount;

					if(nRowCount == 0) {
						_M("CListCtrlEx::OnChildNotify : Row count = 0!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Sets row count into meta table
					//if(m_MetaTable.nRowCount != nRowCount)
					m_MetaTable.nRowCount = nRowCount;
					
					//*** Length of one data record (including delete flag)
					ULONG nRowSize = m_pDoc->m_nRecSize;

					if(nRowSize == 0) {
						_M("CListCtrlEx::OnChildNotify : Row size = 0!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Line displacement of nCol-th field of nRow-th record
					ULONG nLineInd = nRow*nRowSize + m_pDoc->m_anOff[nCol];

					if(nLineInd < 0) {
						_M("CListCtrlEx::OnChildNotify : Line index < 0!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Maps view of dbf file for its second part (where are data)
					DBF_FILE2 *m_pDbfView2 = m_pDoc->m_pDbfView2;

					if(!m_pDbfView2) {
						_M("CListCtrlEx::OnChildNotify : Empty view of dbf file!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** The Visual FoxPro records
					BYTE *aDbfRec = m_pDbfView2->aDbfRec;

					if(!aDbfRec) {
						_M("CListCtrlEx::OnChildNotify : Empty array of dbf file!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					if(!aDbfRec) {
						_M("CListCtrlEx::OnChildNotify : Empty records into dbf file!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					//*** Line table cell index
					UINT ji = nRow*nColCount + nCol;

					if(ji < 0) {
						_M("CListCtrlEx::OnChildNotify : Line cell index < 0!");
						//*** Forces to exit from the application as else will be a lot messages
						exit(-1);
					}

					if(ji < nRowCount*nColCount) {
						try {
							//*** Copies (j, i) field value of m_anLen[i]-th length
							// We do this as it hasn't null terminator
							CString sFldVal(
									(LPCSTR) &aDbfRec[nLineInd], 
									m_pDoc->m_acLen[nCol]
							);

							//*** Formates date string into German style
							if(m_pDoc->m_acFldType[nCol] == 68) {  // = 0x44 ("D") - Date
								CString sDate = 
										sFldVal.Right(2) + _T(".") + 
										sFldVal.Mid(4, 2) + _T(".") + 
										sFldVal.Left(4);
								
								sFldVal = sDate;
							}

							wcscpy((wchar_t *)(pItem->pszText), sFldVal);
						} catch (...) {
							//*** Forces to exit from the application as else will be a lot messages
							exit(-1);
						}
					} else {
						wcscpy((wchar_t *)(pItem->pszText), _T("***"));
					}
				}

				break;
			}
		}
	}

	return CListCtrl::OnChildNotify(message, wParam, lParam, pResult);
}  // OnChildNotify

/////////////////////////////////////////////////////////////////////////////
// SetColItemText
/////////////////////////////////////////////////////////////////////////////
void CListCtrlEx::SetColItemText(CDC *pDC, CString& stColText, 
			CRect& TextRect, UINT nJustify) {
	int x = 0;  // x-coordinate of reference point
	int y = 0;  // y-coordinate of reference point
	UINT nOptions = 0;  // Text-output options ETO_CLIPPED|ETO_OPAQUE

	int nTextLen = stColText.GetLength();
	HDC hDC = pDC->m_hDC;
	SIZE Size = {0};

	if(!GetTextExtentPoint(hDC, stColText, nTextLen, &Size)) {
		_M("Failed to call GetTextExtentPoint for table list!");
		return;
	}

	//*** Align the text in the whole table
	CRect TmpRect(TextRect);

	//*** x-coordinate of reference point
	x = (TextRect.left + TextRect.right - Size.cx)/2 - 1;
	x = (x < TextRect.left + 2) ? TextRect.left + 2 : x;

	//*** y-coordinate of reference point
	y = (TextRect.bottom - TextRect.top - Size.cy)/2 - 1;
	
	//*** Specifies that the current background color fills the rectangle
	nOptions |= ETO_OPAQUE;

	//*** Draw the background fast
	pDC->ExtTextOut(TextRect.left, TextRect.top, nOptions, TextRect, NULL, 0, NULL);

	TmpRect.left++;  // Cosmetic
	TmpRect.top += y;	 // y-coordinate of reference point
	TmpRect.InflateRect(-3, 0);	 // Text does not touch borders
	
	UINT nFormat = 0;

	switch(nJustify & LVCFMT_JUSTIFYMASK) {
		case LVCFMT_LEFT:
			nFormat = DT_LEFT;
			break;
		case LVCFMT_RIGHT:
			nFormat = DT_RIGHT;
			break;
		case LVCFMT_CENTER:
			nFormat = DT_CENTER;
			break;
		default:
			_M("CListCtrlEx: Error of the text formatting!");
			return;
	}

	//*** Writes the text in the TmpRect
	::DrawText(hDC, stColText, nTextLen, TmpRect, nFormat);
}  // SetColItemText

/////////////////////////////////////////////////////////////////////////////
// SetRowText
/////////////////////////////////////////////////////////////////////////////
void CListCtrlEx::SetRowText(LPDRAWITEMSTRUCT pDIS) {
	CString sItemText;

	//*** Retrieve the item rectangle size. 
	CRect TextRect = pDIS->rcItem;

	//*** You should get the pointer to the device context from the "pDIS" ptr.
	CDC *pDC = CDC::FromHandle(pDIS->hDC);
	
	//*** Selects necessary font
	pDC->SelectObject(m_MetaTable.pListFont);

	UINT nColumn = 0;

	LV_COLUMN lvc;
	lvc.mask = LVCF_FMT;

	while(nColumn < m_MetaTable.nColCount && GetColumn(nColumn, &lvc)) {
		//*** Get the text to be drawn and calculate its bounding rectangle
		sItemText = GetItemText(pDIS->itemID, nColumn);
		TextRect.right = TextRect.left + GetColumnWidth(nColumn);

		if(pDC->RectVisible(TextRect))
				SetColItemText(pDC, sItemText, TextRect, lvc.fmt);

		//*** Move the left side of the previous rect already forward to be good
		// for the next Text. The right of the previous bounding rect is the
		// left of the following
		TextRect.left = TextRect.right;
		
		nColumn++;
	}

	//*** Back to the old state
	pDIS->rcItem.right = TextRect.right;
	pDIS->rcItem.bottom = TextRect.bottom;

	//*** Restore system font
	pDC->SelectStockObject(SYSTEM_FONT);
}  // SetRowText

/////////////////////////////////////////////////////////////////////////////
// DrawItem
/////////////////////////////////////////////////////////////////////////////
void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT pDIS) {
	if(pDIS->itemID == -1)
			return;

	//*** You should get the pointer to the device context from the "pDIS" ptr.
	CDC *pDC = CDC::FromHandle(pDIS->hDC);

	switch(pDIS->itemAction) {	
		case ODA_DRAWENTIRE: {
			//*** Populate the listview with column text
			SetRowText(pDIS);
			break;
		} 
		default: {
			TCHAR szStr[MAXITEMTEXT];

			swprintf(
					szStr, 
					_T("CGridListEx::DrawItem : Unexpected case in switch : %i"),
					pDIS->itemAction
					);

			_M(szStr);
		}
	}
}  // DrawItem

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

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

Comments and Discussions