// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"

#ifdef AFX_DB_SEG
#pragma code_seg(AFX_DB_SEG)
#endif

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

#define new DEBUG_NEW

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

IMPLEMENT_DYNAMIC(CDaoRecordView, CFormView)

BEGIN_MESSAGE_MAP(CDaoRecordView, CFormView)
	//{{AFX_MSG_MAP(CDaoRecordView)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
	ON_COMMAND_EX(ID_RECORD_FIRST, OnMove)
	ON_UPDATE_COMMAND_UI(ID_RECORD_FIRST, OnUpdateRecordFirst)
	ON_COMMAND_EX(ID_RECORD_PREV, OnMove)
	ON_UPDATE_COMMAND_UI(ID_RECORD_PREV, OnUpdateRecordPrev)
	ON_COMMAND_EX(ID_RECORD_NEXT, OnMove)
	ON_UPDATE_COMMAND_UI(ID_RECORD_NEXT, OnUpdateRecordNext)
	ON_COMMAND_EX(ID_RECORD_LAST, OnMove)
	ON_UPDATE_COMMAND_UI(ID_RECORD_LAST, OnUpdateRecordLast)
END_MESSAGE_MAP()

CDaoRecordView::CDaoRecordView(LPCTSTR lpszTemplateName)
	: CFormView(lpszTemplateName)
{
	m_nStatus = 0;

	// Setup dummy bookmarks
	m_varBookmarkCurrent = 1L;
	m_varBookmarkFirst = m_varBookmarkLast = 0L;
}

CDaoRecordView::CDaoRecordView(UINT nIDTemplate)
	: CFormView(nIDTemplate)
{
	m_nStatus = 0;

	// Setup dummy bookmarks
	m_varBookmarkCurrent = 1L;
	m_varBookmarkFirst = m_varBookmarkLast = 0L;
}

CDaoRecordView::~CDaoRecordView()
{
}

void CDaoRecordView::OnInitialUpdate()
{
	ASSERT_VALID(this);

	CDaoRecordset* pRecordset = OnGetRecordset();
	// recordset must be allocated already
	ASSERT(pRecordset != NULL);

	if (!pRecordset->IsOpen())
	{
		CWaitCursor wait;
		pRecordset->Open();
	}

	if (!pRecordset->IsEOF())
	{
		// Determine recordset properties for move button enabling
		if (pRecordset->CanBookmark())
		{
			// Get the bookmark of the first record
			m_varBookmarkCurrent = pRecordset->GetBookmark();
			m_varBookmarkFirst = m_varBookmarkCurrent;
		}

		// Enable forward scrolling buttons
		m_nStatus |= AFX_DAOVIEW_SCROLL_NEXT;

		// Enable backward scrolling buttons if possible
		if (pRecordset->CanScroll())
		{
			m_nStatus |= AFX_DAOVIEW_SCROLL_LAST;
			m_nStatus |= AFX_DAOVIEW_SCROLL_BACKWARD;
		}
		else
		{
			m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
			m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
		}
	}
	else
	{
		// Disable scrolling
		m_nStatus &= ~AFX_DAOVIEW_SCROLL_NEXT;
		m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
		m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
	}

	CFormView::OnInitialUpdate();
}

BOOL CDaoRecordView::OnMove(UINT nIDMoveCommand)
{
	ASSERT_VALID(this);

	CDaoRecordset* pSet = OnGetRecordset();
	if (pSet->CanUpdate())
	{
		pSet->Edit();
		if (!UpdateData())
			return TRUE;

		if (pSet->IsFieldDirty(NULL))
			pSet->Update();
		else
			pSet->CancelUpdate();
	}

	BOOL bBookmarkable = pSet->CanBookmark();
	BOOL bScrollable = pSet->CanScroll();

	switch (nIDMoveCommand)
	{
		case ID_RECORD_PREV:
			pSet->MovePrev();

			if (!pSet->IsBOF())
			{
				if (bBookmarkable)
					m_varBookmarkCurrent = pSet->GetBookmark();

				// Enable forward scrolling
				m_nStatus |= AFX_DAOVIEW_SCROLL_NEXT;

				if (bScrollable)
				{
					m_nStatus |= AFX_DAOVIEW_SCROLL_LAST;

					if (IsOnFirstRecord())
						// Disable backward scrolling
						m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
					else
						m_nStatus |= AFX_DAOVIEW_SCROLL_BACKWARD;
				}
				else
				{
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
				}


				break;
			}
			// Fall through to reset to first record

		case ID_RECORD_FIRST:
			pSet->MoveFirst();

			// backward scrolling never allowed after movefirst
			m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;

			if (pSet->IsEOF())
			{
				// Empty recordset, disable forward too
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_NEXT;
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
			}
			else
			{
				if (bBookmarkable)
				{
					m_varBookmarkCurrent = pSet->GetBookmark();
					m_varBookmarkFirst = m_varBookmarkCurrent;
				}

				// Enable forward scrolling
				m_nStatus |= AFX_DAOVIEW_SCROLL_NEXT;

				if (bScrollable)
					m_nStatus |= AFX_DAOVIEW_SCROLL_LAST;
				else
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
			}

			break;

		case ID_RECORD_NEXT:
			pSet->MoveNext();

			if (!pSet->IsEOF())
			{
				if (bBookmarkable)
					m_varBookmarkCurrent = pSet->GetBookmark();

				if (IsOnLastRecord())
				{
					// Disable forward scrolling
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_NEXT;
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
				}
				else
				{
					m_nStatus |= AFX_DAOVIEW_SCROLL_NEXT;
					m_nStatus |= AFX_DAOVIEW_SCROLL_LAST;
				}

				if (bScrollable)
					m_nStatus |= AFX_DAOVIEW_SCROLL_BACKWARD;
				else
				{
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
				}

				break;
			}

			// Can't fall through to move last
			if (!bScrollable)
			{
				// At the end of forward only recordset
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_NEXT;
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
				break;
			}

			// Fall through to reset to last record

		case ID_RECORD_LAST:
			pSet->MoveLast();

			// forward scrolling never allowed after movelast
			m_nStatus &= ~AFX_DAOVIEW_SCROLL_NEXT;
			m_nStatus &= ~AFX_DAOVIEW_SCROLL_LAST;

			if (pSet->IsBOF())
			{
				// Empty recordset, disable backward too
				m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
			}
			else
			{
				if (bBookmarkable)
				{
					m_varBookmarkCurrent = pSet->GetBookmark();
					m_varBookmarkLast = m_varBookmarkCurrent;
				}

				// Enable backward scrolling
				if (bBookmarkable)
					m_nStatus |= AFX_DAOVIEW_SCROLL_BACKWARD;
				else
					m_nStatus &= ~AFX_DAOVIEW_SCROLL_BACKWARD;
			}

			break;

		default:
			// Unexpected case value
			ASSERT(FALSE);
	}

	// Show results of move operation
	UpdateData(FALSE);
	return TRUE;
}

BOOL CDaoRecordView::IsOnFirstRecord()
{
	ASSERT_VALID(this);
	return (m_varBookmarkCurrent == m_varBookmarkFirst);
}

BOOL CDaoRecordView::IsOnLastRecord()
{
	ASSERT_VALID(this);
	return (m_varBookmarkCurrent == m_varBookmarkLast);
}

/////////////////////////////////////////////////////////////////////////////
// DDX Cover functions for use with fields of a recordset

/////////////////////////////////////////////////////////////////////////////
// Simple field formatting to text item

BOOL AFXAPI AfxFieldText(CDataExchange* pDX, int nIDC, void* pv,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
	TCHAR szT[2];
	if (pDX->m_bSaveAndValidate)
	{
		::GetWindowText(hWndCtrl, szT, _countof(szT));
		if (szT[0] == '\0')
		{
			// If edit buffer not NULL prior to update, set it dirty
			// to catch case of setting field value from PSEUDO NULL to NULL.
			if (!pRecordset->IsFieldNull(pv))
				pRecordset->SetFieldDirty(pv, TRUE);
			pRecordset->SetFieldNull(pv);
			return TRUE;
		}
		else
		{
			// If edit buffer NULL prior to update, set it dirty
			// to catch case of setting field value to PSEUDO NULL.
			if (pRecordset->IsFieldNull(pv))
				pRecordset->SetFieldDirty(pv, TRUE);
			pRecordset->SetFieldNull(pv, FALSE);
		}
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(pv))
		{
			szT[0] = '\0';
			AfxSetWindowText(hWndCtrl, szT);
			return TRUE;
		}
	}
	return FALSE;
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, BOOL& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, BYTE& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, short& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, long& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, DWORD& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, COleCurrency& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, float& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, double& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, COleDateTime& value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, CString &value,
	CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &value, pRecordset))
		DDX_Text(pDX, nIDC, value);
}

void AFXAPI DDX_FieldText(CDataExchange* pDX, int nIDC, LPTSTR pstrValue,
	int nMaxLen, CDaoRecordset* pRecordset)
{
	if (!AfxFieldText(pDX, nIDC, &pstrValue, pRecordset))
		DDX_Text(pDX, nIDC, pstrValue, nMaxLen);
}

void AFXAPI DDX_FieldLBString(CDataExchange* pDX, int nIDC, CString& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{
		int nIndex = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
		if (nIndex != -1)
		{
			int nLen = (int)::SendMessage(hWndCtrl, LB_GETTEXTLEN, nIndex, 0L);
			::SendMessage(hWndCtrl, LB_GETTEXT, nIndex,
					(LPARAM)(LPSTR)value.GetBuffer(nLen));
			if (nLen == 0)
			{
				if (pRecordset->IsFieldNullable(&value))
					pRecordset->SetFieldNull(&value, TRUE);
			}
			else
			{
				pRecordset->SetFieldNull(&value, FALSE);
			}
			value.ReleaseBuffer();
		}
		else
		{
			// no selection
			value.GetBufferSetLength(0);
			if (pRecordset->IsFieldNullable(&value))
				pRecordset->SetFieldNull(&value);
		}
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value))
		{
			SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)-1, 0L);
		}
		else
		{
			// set current selection based on data string
			if (::SendMessage(hWndCtrl, LB_SELECTSTRING, (WPARAM)-1,
			  (LPARAM)(LPCTSTR)value) == LB_ERR)
			{
				// no selection match
				TRACE0("Warning: no listbox item selected.\n");
			}
		}
	}
}

void AFXAPI DDX_FieldLBStringExact(CDataExchange* pDX, int nIDC, CString& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{
		DDX_FieldLBString(pDX, nIDC, value, pRecordset);
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value))
		{
			SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)-1, 0L);
		}
		else
		{
			// set current selection based on data string
			int i = (int)::SendMessage(hWndCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1,
			  (LPARAM)(LPCTSTR)value);
			if (i < 0)
			{
				// no selection match
				TRACE0("Warning: no listbox item selected.\n");
			}
			else
			{
				// select it
				SendMessage(hWndCtrl, LB_SETCURSEL, i, 0L);
			}
		}
	}
}

void AFXAPI DDX_FieldCBString(CDataExchange* pDX, int nIDC, CString& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{
		// just get current edit item text (or drop list static)
		int nLen = ::GetWindowTextLength(hWndCtrl);
		if (nLen != -1)
		{
			// get known length
			::GetWindowText(hWndCtrl, value.GetBuffer(nLen), nLen+1);
		}
		else
		{
			// for drop lists GetWindowTextLength does not work - assume
			//  preallocated length (or 256, whichever is larger)
			nLen = value.GetAllocLength();
			if (nLen < 256)
				nLen = 256;
			::GetWindowText(hWndCtrl, value.GetBuffer(nLen-1), nLen);
		}
		value.ReleaseBuffer();
		if (value.GetLength() == 0)
		{
			if (pRecordset->IsFieldNullable(&value))
				pRecordset->SetFieldNull(&value, TRUE);
		}
		else
		{
			pRecordset->SetFieldNull(&value, FALSE);
		}
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value))
		{
			SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)-1, 0L);
		}
		else
		{
			// set current selection based on model string
			if (::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1,
				(LPARAM)(LPCTSTR)value) == CB_ERR)
			{
				// just set the edit text (will be ignored if DROPDOWNLIST)
				AfxSetWindowText(hWndCtrl, value);
			}
		}
	}
}

void AFXAPI DDX_FieldCBStringExact(CDataExchange* pDX, int nIDC, CString& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{
		DDX_FieldCBString(pDX, nIDC, value, pRecordset);
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value))
		{
			SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)-1, 0L);
		}
		else
		{
			// set current selection based on data string
			int i = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1,
			  (LPARAM)(LPCTSTR)value);
			if (i < 0)
			{
				// no selection match
				TRACE0("Warning: no combobox item selected.\n");
			}
			else
			{
				// select it
				SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L);
			}
		}
	}
}

void AFXAPI DDX_FieldLBIndex(CDataExchange* pDX, int nIDC, int& index,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	if (!pDX->m_bSaveAndValidate &&
		(!pRecordset->IsOpen() || pRecordset->IsFieldNull(&index)))
	{
		int nIndex = 0;
		DDX_LBIndex(pDX, nIDC, nIndex);
	}
	else
		DDX_LBIndex(pDX, nIDC, index);
}

void AFXAPI DDX_FieldCBIndex(CDataExchange* pDX, int nIDC, int& index,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	if (!pDX->m_bSaveAndValidate &&
		(!pRecordset->IsOpen() || pRecordset->IsFieldNull(&index)))
	{
		int nIndex = 0;
		DDX_CBIndex(pDX, nIDC, nIndex);
	}
	else
		DDX_CBIndex(pDX, nIDC, index);
}

void AFXAPI DDX_FieldScroll(CDataExchange* pDX, int nIDC, int& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	if (!pDX->m_bSaveAndValidate &&
		(!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value)))
	{
		int nValue = 0;
		DDX_Scroll(pDX, nIDC, nValue);
	}
	else
		DDX_Scroll(pDX, nIDC, value);
}

void AFXAPI DDX_FieldSlider(CDataExchange* pDX, int nIDC, int& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	if (!pDX->m_bSaveAndValidate &&
		(!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value)))
	{
		int nValue = 0;
		DDX_Slider(pDX, nIDC, nValue);
	}
	else
		DDX_Slider(pDX, nIDC, value);
}

/////////////////////////////////////////////////////////////////////////////
// Data exchange for special controls

void AFXAPI DDX_FieldCheck(CDataExchange* pDX, int nIDC, int& value, CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{
		value = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
		ASSERT(value >= 0 && value <= 2);
		if (value == 2)
		{
			if (pRecordset->IsFieldNullable(&value))
				pRecordset->SetFieldNull(&value);
			else
			{
				TRACE0("Warning: can't set field NULL for checkbox value.\n");
				// Default to unchecked
				value = 0;
			}
		}
	}
	else
	{
		if (!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value))
		{
			int style = ((int)::GetWindowLong(hWndCtrl, GWL_STYLE) & 0xf);
			if ((style == BS_3STATE || style == BS_AUTO3STATE))
				value = 2;
			else
			{
				TRACE0("Warning: can't set checkbox value for NULL field.\n");
				// Default to unchecked
				value = 0;
			}
		}
		if (value < 0 || value > 2)
		{
			value = 0;      // default to off
			TRACE1("Warning: dialog data checkbox value (%d) out of range.\n",
				value);
		}
		::SendMessage(hWndCtrl, BM_SETCHECK, (WPARAM)value, 0L);
	}
}

void AFXAPI DDX_FieldRadio(CDataExchange* pDX, int nIDC, int& value,
	CDaoRecordset* pRecordset)
{
	ASSERT_VALID(pRecordset);

	if (!pDX->m_bSaveAndValidate &&
		(!pRecordset->IsOpen() || pRecordset->IsFieldNull(&value)))
		value = -1;
	DDX_Radio(pDX, nIDC, value);
	if (pDX->m_bSaveAndValidate)
	{
		if (value == -1 && !pRecordset->IsFieldNullable(&value))
		{
			AfxFailRadio(pDX);
		}
		else
		{
			pRecordset->SetFieldNull(&value, (value == -1));
		}
	}
}

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

#ifdef _DEBUG
void CDaoRecordView::AssertValid() const
{
	CFormView::AssertValid();
}

void CDaoRecordView::Dump(CDumpContext& dc) const
{
	ASSERT_VALID(this);

	CFormView::Dump(dc);

	dc << "m_nStatus =" << m_nStatus;
	dc << "m_varBookmarkCurrent =" << m_varBookmarkCurrent;
	dc << "m_varBookmarkFirst =" << m_varBookmarkFirst;
	dc << "m_varBookmarkLast =" << m_varBookmarkLast;

	dc << "\n";
}
#endif

//////////////////////////////////////////////////////////////////////////////
// Inline function declarations expanded out-of-line

#ifndef _AFX_ENABLE_INLINES

static char _szAfxDaoInl[] = "afxdao.inl";
#undef THIS_FILE
#define THIS_FILE _szAfxDaoInl
#define _AFXDAOVIEW_INLINE
#include "afxdao.inl"

#endif

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

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