You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1622 lines
45 KiB
1622 lines
45 KiB
//---------------------------------------------------------------------------
|
|
// 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);
|
|
}
|