|
|
//---------------------------------------------------------------------------
// CursorPosition.cpp : CursorPosition implementation
//
// Copyright (c) 1996 Microsoft Corporation, All Rights Reserved
// Developed by Sheridan Software Systems, Inc.
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "Notifier.h"
#include "RSColumn.h"
#include "RSSource.h"
#include "CursMain.h"
#include "ColUpdat.h"
#include "CursPos.h"
#include "fastguid.h"
#include "MSR2C.h"
#include "resource.h"
SZTHISFILE
//=--------------------------------------------------------------------------=
// CVDCursorPosition - Constructor
//
CVDCursorPosition::CVDCursorPosition() { m_pCursorMain = NULL; m_pRowPosition = NULL; m_pSameRowClone = NULL; m_dwEditMode = CURSOR_DBEDITMODE_NONE; m_ppColumnUpdates = NULL; m_fTempEditMode = FALSE; m_fConnected = FALSE; m_dwAdviseCookie = 0; m_fPassivated = FALSE; m_fInternalSetRow = FALSE;
#ifdef _DEBUG
g_cVDCursorPositionCreated++; #endif
}
//=--------------------------------------------------------------------------=
// ~CVDCursorPosition - Destructor
//
CVDCursorPosition::~CVDCursorPosition() { Passivate();
#ifdef _DEBUG
g_cVDCursorPositionDestroyed++; #endif
}
//=--------------------------------------------------------------------------=
// Pasivate when external ref count gets to zero
//
void CVDCursorPosition::Passivate() { if (m_fPassivated) return;
m_fPassivated = TRUE;
DestroyColumnUpdates(); ReleaseCurrentRow(); ReleaseAddRow();
LeaveFamily(); // remove myself from pCursorMain's notification family
if (m_pCursorMain) m_pCursorMain->Release(); // release associated cursor main object
if (m_fConnected) DisconnectIRowPositionChange(); // disconnect IRowPosition change
if (m_pRowPosition) m_pRowPosition->Release(); // release associated row position
}
//=--------------------------------------------------------------------------=
// Create - Create cursor position object
//=--------------------------------------------------------------------------=
// This function creates and initializes a new cursor position object
//
// Parameters:
// pRowPosition - [in] IRowPosition provider (may be NULL)
// pCursorMain - [in] backwards pointer to CVDCursorMain object
// ppCursorPosition - [out] a pointer in which to return pointer to cursor position object
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory to create object
//
// Notes:
//
HRESULT CVDCursorPosition::Create(IRowPosition * pRowPosition, CVDCursorMain * pCursorMain, CVDCursorPosition ** ppCursorPosition, CVDResourceDLL * pResourceDLL) { ASSERT_POINTER(pCursorMain, CVDCursorMain) ASSERT_POINTER(ppCursorPosition, CVDCursorPosition*)
if (!pCursorMain || !ppCursorPosition) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_ICursorMove, pResourceDLL); return E_INVALIDARG; }
*ppCursorPosition = NULL;
CVDCursorPosition * pCursorPosition = new CVDCursorPosition();
if (!pCursorPosition) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursorMove, pResourceDLL); return E_OUTOFMEMORY; }
pCursorPosition->m_pResourceDLL = pResourceDLL; pCursorPosition->m_pCursorMain = pCursorMain; pCursorPosition->m_pRowPosition = pRowPosition;
pCursorMain->AddRef(); // add reference to associated cursor main object
if (pRowPosition) // add reference to associated row position (if needed)
{ pRowPosition->AddRef();
// connect IRowPositionChange
HRESULT hr = pCursorPosition->ConnectIRowPositionChange();
if (SUCCEEDED(hr)) pCursorPosition->m_fConnected = TRUE; }
// add to pCursorMain's notification family
pCursorPosition->JoinFamily(pCursorMain);
pCursorPosition->PositionToFirstRow(); *ppCursorPosition = pCursorPosition;
return S_OK; }
//=--------------------------------------------------------------------------=
// CreateColumnUpdates - Create array of column update pointers
//
HRESULT CVDCursorPosition::CreateColumnUpdates() { const ULONG ulColumns = m_pCursorMain->GetColumnsCount();
m_ppColumnUpdates = new CVDColumnUpdate*[ulColumns];
if (!m_ppColumnUpdates) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursorUpdateARow, m_pResourceDLL); return E_OUTOFMEMORY; }
// set all column update pointers to NULL
memset(m_ppColumnUpdates, 0, ulColumns * sizeof(CVDColumnUpdate*));
return S_OK; }
//=--------------------------------------------------------------------------=
// ResetColumnUpdates - Reset column updates array
//
HRESULT CVDCursorPosition::ResetColumnUpdates() { HRESULT hr = S_OK;
if (m_ppColumnUpdates) { const ULONG ulColumns = m_pCursorMain->GetColumnsCount();
// set all column update pointers to NULL
for (ULONG ulCol = 0; ulCol < ulColumns; ulCol++) SetColumnUpdate(ulCol, NULL); } else { // create array of column update pointers
hr = CreateColumnUpdates(); }
return hr; }
//=--------------------------------------------------------------------------=
// DestroyColumnUpdates - Destroy column updates and array of update pointers
//
void CVDCursorPosition::DestroyColumnUpdates() { if (m_ppColumnUpdates) { // set all column update pointers to NULL
ResetColumnUpdates();
// destroy array of column update pointers
delete [] m_ppColumnUpdates; m_ppColumnUpdates = NULL; } }
//=--------------------------------------------------------------------------=
// GetColumnUpdate - Get column update
//
CVDColumnUpdate * CVDCursorPosition::GetColumnUpdate(ULONG ulColumn) const { CVDColumnUpdate * pColumnUpdate = NULL;
const ULONG ulColumns = m_pCursorMain->GetColumnsCount();
// make sure column index is in range
if (ulColumn < ulColumns) pColumnUpdate = m_ppColumnUpdates[ulColumn];
return pColumnUpdate; }
//=--------------------------------------------------------------------------=
// SetColumnUpdate - Set column update
//
void CVDCursorPosition::SetColumnUpdate(ULONG ulColumn, CVDColumnUpdate * pColumnUpdate) { const ULONG ulColumns = m_pCursorMain->GetColumnsCount();
// make sure column index is in range
if (ulColumn < ulColumns) { // release update if it already exists
if (m_ppColumnUpdates[ulColumn]) m_ppColumnUpdates[ulColumn]->Release();
// store new column update
m_ppColumnUpdates[ulColumn] = pColumnUpdate; } }
//=--------------------------------------------------------------------------=
// PositionToFirstRow
//=--------------------------------------------------------------------------=
// Positions to the first row in the rowset
//
void CVDCursorPosition::PositionToFirstRow() { m_bmCurrent.Reset();
ULONG cRowsObtained = 0; HROW * rghRows = NULL; BYTE bSpecialBM; bSpecialBM = DBBMK_FIRST; HRESULT hr = GetRowsetSource()->GetRowsetLocate()->GetRowsAt(0, 0, sizeof(BYTE), &bSpecialBM, 0, 1, &cRowsObtained, &rghRows);
if (cRowsObtained) { // set current row to first row
SetCurrentHRow(rghRows[0]); // release hRows and associated memory
GetRowsetSource()->GetRowset()->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL); g_pMalloc->Free(rghRows); }
}
//=--------------------------------------------------------------------------=
// ReleaseCurrentRow
//=--------------------------------------------------------------------------=
// Releases old current row
//
void CVDCursorPosition::ReleaseCurrentRow() { if (!GetRowsetSource()->IsRowsetValid() || m_bmCurrent.GetStatus() != VDBOOKMARKSTATUS_CURRENT) return;
if (m_bmCurrent.m_hRow) { GetRowsetSource()->GetRowset()->ReleaseRows(1, &m_bmCurrent.m_hRow, NULL, NULL, NULL); m_bmCurrent.m_hRow = NULL; } }
//=--------------------------------------------------------------------------=
// ReleaseAddRow
//=--------------------------------------------------------------------------=
// Releases temporary add row
//
void CVDCursorPosition::ReleaseAddRow() { if (!GetRowsetSource()->IsRowsetValid()) return;
if (m_bmAddRow.m_hRow) { GetRowsetSource()->GetRowset()->ReleaseRows(1, &m_bmAddRow.m_hRow, NULL, NULL, NULL); m_bmAddRow.m_hRow = NULL; } }
//=--------------------------------------------------------------------------=
// SetCurrentRowStatus
//=--------------------------------------------------------------------------=
// Sets status to beginning or end (releasing current hrow)
//
void CVDCursorPosition::SetCurrentRowStatus(WORD wStatus) { if (VDBOOKMARKSTATUS_BEGINNING == wStatus || VDBOOKMARKSTATUS_END == wStatus) { ReleaseCurrentRow(); m_bmCurrent.SetBookmark(wStatus); } }
//=--------------------------------------------------------------------------=
// SetCurrentHRow
//=--------------------------------------------------------------------------=
// Reads the bookmark from the hrow and sets the m_bmCurrent
//
// Parameters:
// hRowNew - [in] hrow of new current row
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursorPosition::SetCurrentHRow(HROW hRowNew) { if (!GetRowsetSource()->IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
IRowset * pRowset = GetRowsetSource()->GetRowset();
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// get the bookmark data
HRESULT hr = pRowset->GetData(hRowNew, GetCursorMain()->GetBookmarkAccessor(), pBuff); if (S_OK == hr) { ReleaseCurrentRow(); pRowset->AddRefRows(1, &hRowNew, NULL, NULL); ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG); m_bmCurrent.SetBookmark(VDBOOKMARKSTATUS_CURRENT, hRowNew, pbmdata, *pulLen); } else { ASSERT_(FALSE); hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorMove, pRowset, IID_IRowset, m_pResourceDLL); }
delete [] pBuff;
return hr;
}
//=--------------------------------------------------------------------------=
// IsSameRowAsCurrent - Compares current bookmark to supplied hrow
//=--------------------------------------------------------------------------=
//
// Parameters:
// hRow - [in] hrow to check
// fCacheIfNotSame - [in] If TRUE same hrow in cached CVDBookmark
//
// Output:
// HRESULT - S_OK if both hrows correspond to the same logical row
// S_FALSE if not same row
// E_INVALIDARG
// E_UNEXPECTED
// DB_E_BADROWHANDLE
// DB_E_DELETEDROW
// DB_E_NEWLYINSERTED
//
// Notes:
//
HRESULT CVDCursorPosition::IsSameRowAsCurrent(HROW hRow, BOOL fCacheIfNotSame) {
if (!GetRowsetSource()->IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
if (m_bmCurrent.IsSameHRow(hRow)) return S_OK;
HRESULT hrSame = S_FALSE;
IRowsetIdentity * pRowsetIdentity = GetRowsetSource()->GetRowsetIdentity();
if (pRowsetIdentity) { hrSame = pRowsetIdentity->IsSameRow(hRow, m_bmCurrent.GetHRow()); // return if hrow matches or not cache flag set
if (S_OK == hrSame || !fCacheIfNotSame) return hrSame; } else if (fCacheIfNotSame) { // check if hRow matches cache
if (m_bmCache.IsSameHRow(hRow)) { // return TRUE if bookmark matches cache
return m_bmCurrent.IsSameBookmark(&m_bmCache) ? S_OK : S_FALSE; } }
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// get the bookmark data
HRESULT hrWork = GetRowsetSource()->GetRowset()->GetData(hRow, GetCursorMain()->GetBookmarkAccessor(), pBuff);
if (S_OK == hrWork) { ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG); // if IRowsetIdentity isn't supported, compare bookmarks
if (!pRowsetIdentity) { DBCOMPARE dbcompare; hrWork = GetRowsetSource()->GetRowsetLocate()->Compare(0, m_bmCurrent.GetBookmarkLen(), m_bmCurrent.GetBookmark(), *pulLen, pbmdata, &dbcompare); if (SUCCEEDED(hrWork)) { if (DBCOMPARE_EQ == dbcompare) hrSame = S_OK; else hrSame = S_FALSE; } } if (fCacheIfNotSame && S_OK != hrSame) m_bmCache.SetBookmark(VDBOOKMARKSTATUS_CURRENT, hRow, pbmdata, *pulLen); } else hrSame = hrWork;
delete [] pBuff;
return hrSame;
}
//=--------------------------------------------------------------------------=
// IsSameRowAsAddRow - Compares addrow bookmark to supplied hrow
//=--------------------------------------------------------------------------=
//
// Parameters:
// hRow - [in] hrow to check
//
// Output:
// HRESULT - S_OK if both hrows correspond to the same logical row
// S_FALSE if not same row
// E_INVALIDARG
// E_UNEXPECTED
// DB_E_BADROWHANDLE
// DB_E_DELETEDROW
// DB_E_NEWLYINSERTED
//
// Notes:
//
HRESULT CVDCursorPosition::IsSameRowAsNew(HROW hRow) {
if (!GetRowsetSource()->IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
if (m_bmAddRow.IsSameHRow(hRow)) return S_OK;
if (m_bmAddRow.m_hRow == NULL) return S_FALSE;
HRESULT hrSame = S_FALSE;
IRowsetIdentity * pRowsetIdentity = GetRowsetSource()->GetRowsetIdentity();
if (pRowsetIdentity) { hrSame = pRowsetIdentity->IsSameRow(hRow, m_bmAddRow.GetHRow()); // return result
return hrSame; }
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// get the bookmark data
HRESULT hrWork = GetRowsetSource()->GetRowset()->GetData(hRow, GetCursorMain()->GetBookmarkAccessor(), pBuff);
if (S_OK == hrWork) { ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG);
// since IRowsetIdentity isn't supported, compare bookmarks
DBCOMPARE dbcompare; hrWork = GetRowsetSource()->GetRowsetLocate()->Compare(0, m_bmAddRow.GetBookmarkLen(), m_bmAddRow.GetBookmark(), *pulLen, pbmdata, &dbcompare); if (SUCCEEDED(hrWork)) { if (DBCOMPARE_EQ == dbcompare) hrSame = S_OK; else hrSame = S_FALSE; } } else hrSame = hrWork;
delete [] pBuff;
return hrSame;
}
//=--------------------------------------------------------------------------=
// SetAddHRow
//=--------------------------------------------------------------------------=
// Reads the bookmark from the hrow and sets the m_bmAddRow
//
// Parameters:
// hRowNew - [in] hrow of new add row
//
// Output:
// HRESULT - S_OK if successful
//
// Notes:
//
HRESULT CVDCursorPosition::SetAddHRow(HROW hRowNew) { if (!GetRowsetSource()->IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_ICursor, m_pResourceDLL); return E_FAIL; }
IRowset * pRowset = GetRowsetSource()->GetRowset();
// allocate buffer for bookmark plus length indicator
BYTE * pBuff = new BYTE[GetCursorMain()->GetMaxBookmarkLen() + sizeof(ULONG)];
if (!pBuff) { VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_ICursor, m_pResourceDLL); return E_OUTOFMEMORY; }
// get the bookmark data
HRESULT hr = pRowset->GetData(hRowNew, GetCursorMain()->GetBookmarkAccessor(), pBuff); if (S_OK == hr) { ReleaseAddRow(); pRowset->AddRefRows(1, &hRowNew, NULL, NULL); ULONG * pulLen = (ULONG*)pBuff; BYTE * pbmdata = pBuff + sizeof(ULONG); m_bmAddRow.SetBookmark(VDBOOKMARKSTATUS_CURRENT, hRowNew, pbmdata, *pulLen); } else { ASSERT_(FALSE); hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_GETDATAFAILED, IID_ICursorMove, pRowset, IID_IRowset, m_pResourceDLL); }
delete [] pBuff;
return hr;
}
//=--------------------------------------------------------------------------=
// GetEditRow - Get hRow of the row currently being edited
//
HROW CVDCursorPosition::GetEditRow() const { HROW hRow = NULL;
switch (m_dwEditMode) { case CURSOR_DBEDITMODE_UPDATE: hRow = m_bmCurrent.m_hRow; break;
case CURSOR_DBEDITMODE_ADD: hRow = m_bmAddRow.m_hRow; break; }
return hRow; }
//=--------------------------------------------------------------------------=
// SetRowPosition - Set new current hRow
//
HRESULT CVDCursorPosition::SetRowPosition(HROW hRow) { if (!m_pRowPosition) return S_OK;
// set new current row (set/clear internal set row flag)
m_fInternalSetRow = TRUE;
HRESULT hr = m_pRowPosition->ClearRowPosition();
if (SUCCEEDED(hr)) hr = m_pRowPosition->SetRowPosition(NULL, hRow, DBPOSITION_OK);
m_fInternalSetRow = FALSE;
return hr; }
#ifndef VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// UpdateEntryIDStream - Update entry identifier from stream
//=--------------------------------------------------------------------------=
// This function updates the entry identifier's data from stream
//
// Parameters:
// pColumn - [in] rowset column pointer
// hRow - [in] the row handle
// pStream - [in] stream pointer
//
// Output:
// HRESULT - S_OK if successful
// E_INVALIDARG bad parameter
// E_OUTOFMEMORY not enough memory
//
HRESULT CVDCursorPosition::UpdateEntryIDStream(CVDRowsetColumn * pColumn, HROW hRow, IStream * pStream) { ASSERT_POINTER(pStream, IStream)
IAccessor * pAccessor = GetCursorMain()->GetAccessor(); IRowsetChange * pRowsetChange = GetCursorMain()->GetRowsetChange();
// make sure we have valid accessor and change pointers
if (!pAccessor || !pRowsetChange || !GetCursorMain()->IsRowsetValid()) { VDSetErrorInfo(IDS_ERR_ROWSETRELEASED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// make sure we have all necessary pointers
if (!pColumn || !pStream) { VDSetErrorInfo(IDS_ERR_INVALIDARG, IID_IEntryID, m_pResourceDLL); return E_INVALIDARG; }
STATSTG statstg;
// retrieve status structure
HRESULT hr = pStream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) { VDSetErrorInfo(IDS_ERR_STATFAILED, IID_IEntryID, m_pResourceDLL); return E_FAIL; }
// determine length of data
ULONG cbData = statstg.cbSize.LowPart;
HGLOBAL hData;
// get handle to data
hr = GetHGlobalFromStream(pStream, &hData);
if (FAILED(hr)) return hr;
// get pointer to data
BYTE * pData = (BYTE*)GlobalLock(hData);
DBBINDING binding;
// clear out binding
memset(&binding, 0, sizeof(DBBINDING));
// create value binding
binding.iOrdinal = pColumn->GetOrdinal(); binding.obValue = sizeof(DBSTATUS) + sizeof(ULONG); binding.obLength = sizeof(DBSTATUS); binding.obStatus = 0; binding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.cbMaxLen = cbData; binding.wType = DBTYPE_BYREF | DBTYPE_BYTES;
HACCESSOR hAccessor;
// create update accessor
hr = pAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &binding, 0, &hAccessor, NULL);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_CREATEACCESSORFAILED, IID_IEntryID, pAccessor, IID_IAccessor, m_pResourceDLL);
if (FAILED(hr)) { // release pointer to data
GlobalUnlock(hData); return hr; }
// create update buffer
BYTE * pBuffer = new BYTE[sizeof(DBSTATUS) + sizeof(ULONG) + sizeof(LPBYTE)];
if (!pBuffer) { // release pointer to data
GlobalUnlock(hData);
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
VDSetErrorInfo(IDS_ERR_OUTOFMEMORY, IID_IEntryID, m_pResourceDLL); return E_OUTOFMEMORY; }
// set status, length and value
*(DBSTATUS*)pBuffer = DBSTATUS_S_OK; *(ULONG*)(pBuffer + sizeof(DBSTATUS)) = cbData; *(LPBYTE*)(pBuffer + sizeof(DBSTATUS) + sizeof(ULONG)) = pData;
// modify column
hr = pRowsetChange->SetData(hRow, hAccessor, pBuffer);
hr = VDMapRowsetHRtoCursorHR(hr, IDS_ERR_SETDATAFAILED, IID_IEntryID, pRowsetChange, IID_IRowsetChange, m_pResourceDLL);
// release pointer to data
GlobalUnlock(hData);
// release update accessor
pAccessor->ReleaseAccessor(hAccessor, NULL);
// destroy update buffer
delete [] pBuffer;
return hr; }
#endif //VD_DONT_IMPLEMENT_ISTREAM
//=--------------------------------------------------------------------------=
// ReleaseSameRowClone - Release same-row clone, if we still have one
//
void CVDCursorPosition::ReleaseSameRowClone() { if (m_pSameRowClone) { // must be set to NULL before release
ICursor * pSameRowClone = m_pSameRowClone; m_pSameRowClone = NULL;
pSameRowClone->Release(); } }
//=--------------------------------------------------------------------------=
// IRowsetNotify Methods
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IRowsetNotify OnFieldChange
//=--------------------------------------------------------------------------=
// This function is called on any change to the value of a field
//
// Parameters:
// pRowset - [in] the IRowset that is generating the notification
// (we can ignore this since we are only ever dealing
// with a single rowset).
// hRow - [in] the HROW of the row in which the field value has
// changed
// cColumns - [in] the count of columns in rgColumns
// rgColumns - [in] an array of column (ordinal positions) in the row
// for which the value has changed
// eReason - [in] the kind of action which caused this change
// ePhase - [in] the phase of this notification
// fCantDeny - [in] when this flag is set to TRUE, the consumer cannot
// veto the event (by returning S_FALSE)
//
// Output:
// HRESULT - S_OK if successful
// S_FALSE the event/phase is vetoed
// DB_S_UNWANTEDPHASE
// DB_S_UNWANTEDREASON
//
// Notes:
//
HRESULT CVDCursorPosition::OnFieldChange(IUnknown *pRowset, HROW hRow, ULONG cColumns, ULONG rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { // make sure rowset is valid
if (!GetRowsetSource()->IsRowsetValid()) return S_OK;
// check for columns
if (0 == cColumns) return S_OK;
// check for known reasons
if (eReason != DBREASON_COLUMN_SET && eReason != DBREASON_COLUMN_RECALCULATED) return S_OK;
HRESULT hr = S_OK;
// send edit mode notification if needed
if (ePhase == DBEVENTPHASE_OKTODO && m_dwEditMode == CURSOR_DBEDITMODE_NONE) { // setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_EDIT;
// notify other interested parties of action
hr = NotifyBefore(dwEventWhat, 1, rgReasons);
if (hr == S_OK) { // notify other interested parties of success
NotifyAfter(dwEventWhat, 1, rgReasons);
// temporarily place cursor into edit mode
m_fTempEditMode = TRUE; } else { // notify other interested parties of failure
NotifyFail(dwEventWhat, 1, rgReasons); } } // sent set column notifications
if (hr == S_OK && (ePhase == DBEVENTPHASE_OKTODO || ePhase == DBEVENTPHASE_DIDEVENT || ePhase == DBEVENTPHASE_FAILEDTODO)) { // setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
switch (eReason) { case DBREASON_COLUMN_SET: rgReasons[0].dwReason = CURSOR_DBREASON_SETCOLUMN; break;
case DBREASON_COLUMN_RECALCULATED: rgReasons[0].dwReason = CURSOR_DBREASON_RECALC; break; }
// get internal column pointers
ULONG ulColumns = m_pCursorMain->GetColumnsCount(); CVDRowsetColumn * pColumn = m_pCursorMain->InternalGetColumns();
for (ULONG ulCol = 0; ulCol < cColumns; ulCol++) { // determine which column is changing
for (ULONG ulRSCol = 0; ulRSCol < ulColumns; ulRSCol++) { if (pColumn[ulRSCol].GetOrdinal() == rgColumns[ulCol]) { rgReasons[0].arg1.vt = VT_I4; rgReasons[0].arg1.lVal = ulRSCol; } }
HRESULT hrNotify = S_OK;
// notify other interested parties
switch (ePhase) { case DBEVENTPHASE_OKTODO: hrNotify = NotifyBefore(dwEventWhat, 1, rgReasons); break;
case DBEVENTPHASE_DIDEVENT: NotifyAfter(dwEventWhat, 1, rgReasons); break;
case DBEVENTPHASE_FAILEDTODO: NotifyFail(dwEventWhat, 1, rgReasons); break; }
if (hrNotify != S_OK) hr = S_FALSE; } }
// take cursor out of edit mode if we placed it into that mode (success)
if (ePhase == DBEVENTPHASE_DIDEVENT && m_fTempEditMode) { // setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_NONCURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_MODIFIED; // notify other interested parties of action
NotifyBefore(dwEventWhat, 1, rgReasons); NotifyAfter(dwEventWhat, 1, rgReasons);
// take out of edit mode
m_fTempEditMode = FALSE; }
// take cursor out of edit mode if we placed it into that mode (failure)
if (ePhase == DBEVENTPHASE_FAILEDTODO && m_fTempEditMode) { // setup notification structures
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED;
CURSOR_DBNOTIFYREASON rgReasons[1]; VariantInit((VARIANT*)&rgReasons[0].arg1); VariantInit((VARIANT*)&rgReasons[0].arg2);
rgReasons[0].dwReason = CURSOR_DBREASON_CANCELUPDATE;
// notify other interested parties of action
NotifyBefore(dwEventWhat, 1, rgReasons); NotifyAfter(dwEventWhat, 1, rgReasons);
// take out of edit mode
m_fTempEditMode = FALSE; }
// reset cache on ending phase
if (DBEVENTPHASE_FAILEDTODO == ePhase || DBEVENTPHASE_DIDEVENT == ePhase) m_bmCache.Reset();
return hr; }
//=--------------------------------------------------------------------------=
// IRowsetNotify OnRowChange
//=--------------------------------------------------------------------------=
// This function is called on the first change to a row, or any whole-row change
//
// Parameters:
// pRowset - [in] the IRowset that is generating the notification
// (we can ignore this since we are only ever dealing
// with a single rowset).
// cRows - [in] the count of HROWs in rghRows
// rghRows - [in] an array of HROWs which are changing
// eReason - [in] the kind of action which caused this change
// ePhase - [in] the phase of this notification
// fCantDeny - [in] when this flag is set to TRUE, the consumer cannot
// veto the event (by returning S_FALSE)
//
// Output:
// HRESULT - S_OK if successful
// S_FALSE the event/phase is vetoed
// DB_S_UNWANTEDPHASE
// DB_S_UNWANTEDREASON
//
// Notes:
//
HRESULT CVDCursorPosition::OnRowChange(IUnknown *pRowset, ULONG cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { // make sure we still have a valid rowset
if (!(GetRowsetSource()->IsRowsetValid())) return S_OK;
// check for rows
if (0 == cRows) return S_OK;
// filter notifications
switch (eReason) { case DBREASON_ROW_DELETE: case DBREASON_ROW_INSERT: case DBREASON_ROW_RESYNCH: case DBREASON_ROW_UPDATE: case DBREASON_ROW_UNDOCHANGE: case DBREASON_ROW_UNDOINSERT: break;
// the following do not generate notifications
//
// case DBREASON_ROW_ACTIVATE:
// case DBREASON_ROW_RELEASE:
// case DBREASON_ROW_FIRSTCHANGE:
// case DBREASON_ROW_UNDODELETE:
default: return S_OK; }
// create variables
DWORD dwEventWhat = 0; CURSOR_DBNOTIFYREASON * pReasons = (CURSOR_DBNOTIFYREASON *)g_pMalloc->Alloc(cRows * sizeof(CURSOR_DBNOTIFYREASON));
if (!pReasons) return S_OK;
memset(pReasons, 0, cRows * sizeof(CURSOR_DBNOTIFYREASON));
HRESULT hr; BOOL fCurrentRow;
// iterate through supplied rows
for (ULONG ul = 0; ul < cRows; ul++) { if (eReason != DBREASON_ROW_UNDOINSERT) { // check to see if this row is current
hr = IsSameRowAsCurrent(rghRows[ul], TRUE);
switch (hr) { case S_OK: fCurrentRow = TRUE; pReasons[ul].arg1 = m_bmCurrent.GetBookmarkVariant(); break; case S_FALSE: fCurrentRow = FALSE; pReasons[ul].arg1 = m_bmCache.GetBookmarkVariant(); break; default: hr = S_OK; goto cleanup; } } else { // check to see of this row is current add-row
if (m_dwEditMode == CURSOR_DBEDITMODE_ADD) hr = IsSameRowAsNew(rghRows[ul]); else hr = E_FAIL;
switch (hr) { case S_OK: fCurrentRow = TRUE; pReasons[ul].arg1 = m_bmAddRow.GetBookmarkVariant(); break; default: hr = S_OK; goto cleanup; } }
// setup variables
switch (eReason) { case DBREASON_ROW_DELETE: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_CHANGED | CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; else dwEventWhat |= CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_DELETED; break; case DBREASON_ROW_INSERT: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; else dwEventWhat |= CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_INSERTED; break;
case DBREASON_ROW_RESYNCH: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED; else dwEventWhat |= CURSOR_DBEVENT_NONCURRENT_ROW_DATA_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_REFRESH; break;
case DBREASON_ROW_UPDATE: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_CHANGED | CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; else dwEventWhat |= CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_MODIFIED; break;
case DBREASON_ROW_UNDOCHANGE: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; else dwEventWhat |= CURSOR_DBEVENT_NONCURRENT_ROW_DATA_CHANGED | CURSOR_DBEVENT_SET_OF_ROWS_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_MODIFIED; break;
case DBREASON_ROW_UNDOINSERT: if (fCurrentRow) dwEventWhat |= CURSOR_DBEVENT_CURRENT_ROW_DATA_CHANGED; pReasons[ul].dwReason = CURSOR_DBREASON_CANCELUPDATE; break; } }
// notify interested cursor listeners
hr = SendNotification(ePhase, dwEventWhat, cRows, pReasons);
// take cursor out of add-mode if we received UNDOINSERT on current add-row
if (eReason == DBREASON_ROW_UNDOINSERT && ePhase == DBEVENTPHASE_DIDEVENT && hr == S_OK) { // if acquired, release same-row clone
if (GetSameRowClone()) ReleaseSameRowClone();
// also, release add row if we have one
if (m_bmAddRow.GetHRow()) ReleaseAddRow();
// reset edit mode
SetEditMode(CURSOR_DBEDITMODE_NONE);
// reset column updates
ResetColumnUpdates(); }
cleanup: g_pMalloc->Free(pReasons);
// reset cache on ending phase
if (DBEVENTPHASE_FAILEDTODO == ePhase || DBEVENTPHASE_DIDEVENT == ePhase) m_bmCache.Reset();
return hr; }
//=--------------------------------------------------------------------------=
// IRowsetNotify OnRowsetChange
//=--------------------------------------------------------------------------=
// This function is called on any change affecting the entire rowset
//
// Parameters:
// pRowset - [in] the IRowset that is generating the notification
// (we can ignore this since we are only ever dealing
// with a single rowset).
// eReason - [in] the kind of action which caused this change
// ePhase - [in] the phase of this notification
// fCantDeny - [in] when this flag is set to TRUE, the consumer cannot
// veto the event (by returning S_FALSE)
//
// Output:
// HRESULT - S_OK if successful
// S_FALSE the event/phase is vetoed
// DB_S_UNWANTEDPHASE
// DB_S_UNWANTEDREASON
//
// Notes:
//
HRESULT CVDCursorPosition::OnRowsetChange(IUnknown *pRowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { if (!(GetRowsetSource()->IsRowsetValid())) return S_OK;
switch (eReason) { case DBREASON_ROWSET_RELEASE: GetRowsetSource()->SetRowsetReleasedFlag(); break; case DBREASON_ROWSET_FETCHPOSITIONCHANGE: { /*
What do we do here
DWORD dwEventWhat = CURSOR_DBEVENT_CURRENT_ROW_CHANGED; CURSOR_DBNOTIFYREASON reason; memset(&reason, 0, sizeof(CURSOR_DBNOTIFYREASON)); reason.dwReason = CURSOR_DBREASON_MOVE; reason.arg1 = m_bmCurrent.GetBookmarkVariant(); VariantInit((VARIANT*)&reason.arg2); reason.arg2.vt = VT_I4; // the ICursor spec states that this is the value of dlOffset in
// iCursorMove::Move. Since we can't get that from the Rowset spec
// we are setting the value to an arbitrary 1
reason.arg2.lVal = 1; return SendNotification(ePhase, CURSOR_DBEVENT_CURRENT_ROW_CHANGED, 1, &reason); */ break; } }
return S_OK; }
//=--------------------------------------------------------------------------=
// ConnectIRowPositionChange - Connect IRowPositionChange interface
//
HRESULT CVDCursorPosition::ConnectIRowPositionChange() { IConnectionPointContainer * pConnectionPointContainer;
HRESULT hr = m_pRowPosition->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
if (FAILED(hr)) return VD_E_CANNOTCONNECTIROWPOSITIONCHANGE;
IConnectionPoint * pConnectionPoint;
hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowPositionChange, &pConnectionPoint);
if (FAILED(hr)) { pConnectionPointContainer->Release(); return VD_E_CANNOTCONNECTIROWPOSITIONCHANGE; }
hr = pConnectionPoint->Advise(&m_RowPositionChange, &m_dwAdviseCookie);
pConnectionPointContainer->Release(); pConnectionPoint->Release();
return hr; }
//=--------------------------------------------------------------------------=
// DisconnectIRowPositionChange - Disconnect IRowPositionChange interface
//
void CVDCursorPosition::DisconnectIRowPositionChange() { IConnectionPointContainer * pConnectionPointContainer;
HRESULT hr = m_pRowPosition->QueryInterface(IID_IConnectionPointContainer, (void**)&pConnectionPointContainer);
if (FAILED(hr)) return;
IConnectionPoint * pConnectionPoint;
hr = pConnectionPointContainer->FindConnectionPoint(IID_IRowPositionChange, &pConnectionPoint);
if (FAILED(hr)) { pConnectionPointContainer->Release(); return; }
hr = pConnectionPoint->Unadvise(m_dwAdviseCookie);
if (SUCCEEDED(hr)) m_dwAdviseCookie = 0; // clear connection point identifier
pConnectionPointContainer->Release(); pConnectionPoint->Release(); }
//=--------------------------------------------------------------------------=
// SendNotification maps the event phases to the corresponding INotifyDBEvents
// methods
//
HRESULT CVDCursorPosition::SendNotification(DBEVENTPHASE ePhase, DWORD dwEventWhat, ULONG cReasons, CURSOR_DBNOTIFYREASON rgReasons[]) { HRESULT hr = S_OK; switch (ePhase) { case DBEVENTPHASE_OKTODO: hr = NotifyOKToDo(dwEventWhat, cReasons, rgReasons); break; case DBEVENTPHASE_ABOUTTODO: hr = NotifyAboutToDo(dwEventWhat, cReasons, rgReasons); if (S_OK == hr) hr = NotifySyncBefore(dwEventWhat, cReasons, rgReasons); break; case DBEVENTPHASE_SYNCHAFTER: // SyncAfter fired from DidEvent for reentrant safety
break; case DBEVENTPHASE_FAILEDTODO: NotifyCancel(dwEventWhat, cReasons, rgReasons); NotifyFail(dwEventWhat, cReasons, rgReasons); break; case DBEVENTPHASE_DIDEVENT: hr = NotifySyncAfter(dwEventWhat, cReasons, rgReasons); if (S_OK == hr) hr = NotifyDidEvent(dwEventWhat, cReasons, rgReasons); break; }
if (CURSOR_DB_S_CANCEL == hr) hr = S_FALSE;
return hr; }
//=--------------------------------------------------------------------------=
// IUnknown QueryInterface
//
HRESULT CVDCursorPosition::QueryInterface(REFIID riid, void **ppvObjOut) { ASSERT_POINTER(ppvObjOut, IUnknown*)
if (!ppvObjOut) return E_INVALIDARG;
*ppvObjOut = NULL;
if (DO_GUIDS_MATCH(riid, IID_IUnknown)) { *ppvObjOut = this; AddRef(); return S_OK; }
return E_NOINTERFACE; }
//=--------------------------------------------------------------------------=
// IUnknown AddRef (needed to resolve ambiguity)
//
ULONG CVDCursorPosition::AddRef(void) { return CVDNotifier::AddRef(); }
//=--------------------------------------------------------------------------=
// IUnknown Release (needed to resolve ambiguity)
//
ULONG CVDCursorPosition::Release(void) { if (1 == m_dwRefCount) Passivate(); // unhook everything including notification sink
if (1 > --m_dwRefCount) { if (0 == m_RowPositionChange.GetRefCount()) delete this; return 0; }
return m_dwRefCount; }
//=--------------------------------------------------------------------------=
// IRowPositionChange methods implemented
//=--------------------------------------------------------------------------=
//=--------------------------------------------------------------------------=
// IRowPositionChange OnRowPositionChange
//=--------------------------------------------------------------------------=
// This function is called on any change affecting the current row
//
// Parameters:
// eReason - [in] the kind of action which caused this change
// ePhase - [in] the phase of this notification
// fCantDeny - [in] when this flag is set to TRUE, the consumer cannot
// veto the event (by returning S_FALSE)
//
// Output:
// HRESULT - S_OK if successful
// S_FALSE the event/phase is vetoed
// DB_S_UNWANTEDPHASE
// DB_S_UNWANTEDREASON
//
// Notes:
//
HRESULT CVDCursorPosition::OnRowPositionChange(DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { // return if notification caused by internal set row call
if (m_fInternalSetRow) return S_OK;
// return if reason has anything to do with chapter changes
if (eReason == DBREASON_ROWPOSITION_CHAPTERCHANGED) return S_OK;
IRowset * pRowset = GetRowsetSource()->GetRowset();
// make sure we have valid row position and rowset pointers
if (!m_pRowPosition || !pRowset || !GetRowsetSource()->IsRowsetValid()) return S_OK;
// synchronize hRow after event occurs
if (ePhase == DBEVENTPHASE_SYNCHAFTER) { HROW hRow = NULL; HCHAPTER hChapterDummy = NULL; DBPOSITIONFLAGS dwPositionFlags = NULL;
// get new current hRow and position flags from row position object
HRESULT hr = m_pRowPosition->GetRowPosition(&hChapterDummy, &hRow, &dwPositionFlags);
if (FAILED(hr)) return hr;
if (hRow) { // set new hRow
SetCurrentHRow(hRow); pRowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); } else { // set row status to beginning or end
if (dwPositionFlags == DBPOSITION_BOF) SetCurrentRowStatus(VDBOOKMARKSTATUS_BEGINNING); else if (dwPositionFlags == DBPOSITION_EOF) SetCurrentRowStatus(VDBOOKMARKSTATUS_END); } }
CURSOR_DBNOTIFYREASON rgReasons[1]; rgReasons[0].dwReason = CURSOR_DBREASON_MOVE; rgReasons[0].arg1 = m_bmCurrent.GetBookmarkVariant();
VariantInit((VARIANT*)&rgReasons[0].arg2);
// notify other interested parties
return SendNotification(ePhase, CURSOR_DBEVENT_CURRENT_ROW_CHANGED, 1, rgReasons); }
//=--------------------------------------------------------------------------=
// CVDCursorPosition::CVDRowPositionChange::m_pMainUnknown
//=--------------------------------------------------------------------------=
// this method is used when we're sitting in the private unknown object,
// and we need to get at the pointer for the main unknown. basically, it's
// a little better to do this pointer arithmetic than have to store a pointer
// to the parent, etc.
//
inline CVDCursorPosition *CVDCursorPosition::CVDRowPositionChange::m_pMainUnknown ( void ) { return (CVDCursorPosition *)((LPBYTE)this - offsetof(CVDCursorPosition, m_RowPositionChange)); }
//=--------------------------------------------------------------------------=
// CVDCursorPosition::CVDRowPositionChange::QueryInterface
//=--------------------------------------------------------------------------=
// this is the non-delegating internal QI routine.
//
// Parameters:
// REFIID - [in] interface they want
// void ** - [out] where they want to put the resulting object ptr.
//
// Output:
// HRESULT - S_OK, E_NOINTERFACE
//
// Notes:
//
STDMETHODIMP CVDCursorPosition::CVDRowPositionChange::QueryInterface ( REFIID riid, void **ppvObjOut ) { if (!ppvObjOut) return E_INVALIDARG;
*ppvObjOut = NULL;
if (DO_GUIDS_MATCH(riid, IID_IUnknown)) *ppvObjOut = (IUnknown *)this; else if (DO_GUIDS_MATCH(riid, IID_IRowPositionChange)) *ppvObjOut = (IUnknown *)this;
if (*ppvObjOut) { m_cRef++; return S_OK; }
return E_NOINTERFACE;
}
//=--------------------------------------------------------------------------=
// CVDCursorPosition::CVDRowPositionChange::AddRef
//=--------------------------------------------------------------------------=
// adds a tick to the current reference count.
//
// Output:
// ULONG - the new reference count
//
// Notes:
//
ULONG CVDCursorPosition::CVDRowPositionChange::AddRef ( void ) { return ++m_cRef; }
//=--------------------------------------------------------------------------=
// CVDCursorPosition::CVDRowPositionChange::Release
//=--------------------------------------------------------------------------=
// removes a tick from the count, and delets the object if necessary
//
// Output:
// ULONG - remaining refs
//
// Notes:
//
ULONG CVDCursorPosition::CVDRowPositionChange::Release ( void ) { ULONG cRef = --m_cRef;
if (!m_cRef && !m_pMainUnknown()->m_dwRefCount) delete m_pMainUnknown();
return cRef; }
//=--------------------------------------------------------------------------=
// IRowPositionChange OnRowPositionChange
//=--------------------------------------------------------------------------=
// Forward to all CVDCursor objects in our family
//
HRESULT CVDCursorPosition::CVDRowPositionChange::OnRowPositionChange(DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny) { return m_pMainUnknown()->OnRowPositionChange(eReason, ePhase, fCantDeny); }
|