9788 lines
231 KiB
C++
9788 lines
231 KiB
C++
#include "precomp.h"
|
|
|
|
|
|
//
|
|
// WB.CPP
|
|
// Whiteboard Services
|
|
//
|
|
// Copyright(c) Microsoft 1997-
|
|
//
|
|
|
|
#include <wb.hpp>
|
|
|
|
#define MLZ_FILE_ZONE ZONE_WB
|
|
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
BOOL WbClient::WbInit(PUT_CLIENT putTask, UTEVENT_PROC eventProc)
|
|
{
|
|
BOOL rc = FALSE;
|
|
|
|
DebugEntry(WbInit);
|
|
|
|
//
|
|
// Fill in the fields
|
|
//
|
|
m_state = STATE_EMPTY;
|
|
m_subState = STATE_EMPTY;
|
|
m_hLoadFile = INVALID_HANDLE_VALUE;
|
|
|
|
wbClientReset();
|
|
|
|
//
|
|
// Set the current state to the start of registration state.
|
|
// Store the UT handle - we will need this in any calls to the UT API.
|
|
// Set the ObMan handle to NULL to show that we have not registered.
|
|
//
|
|
m_state = STATE_STARTING;
|
|
m_subState = STATE_START_START;
|
|
m_putTask = putTask;
|
|
m_pomClient = NULL;
|
|
UT_RegisterEvent(putTask, eventProc, NULL, UT_PRIORITY_NORMAL);
|
|
|
|
|
|
TRACE_OUT(("Initialized state to STATE_STARTING"));
|
|
|
|
//
|
|
// Register an event handler to trap events from ObMan. The third
|
|
// parameter is data that will be passed to the event handler. We give
|
|
// the client data pointer so that we can access the correct data for
|
|
// each message.
|
|
//
|
|
UT_RegisterEvent(putTask, wbCoreEventHandler, this, UT_PRIORITY_NORMAL);
|
|
|
|
//
|
|
// Register as a Call Manager Secondary. This is required to query the
|
|
// Call Manager personID to insert into the WB_PERSON structure.
|
|
//
|
|
if (!CMS_Register(putTask, CMTASK_WB, &(m_pcmClient)))
|
|
{
|
|
ERROR_OUT(("CMS_Register failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the state
|
|
//
|
|
m_subState = STATE_START_REGISTERED_EVENT;
|
|
TRACE_OUT(("Moved to substate STATE_START_REGISTERED_EVENT"));
|
|
|
|
//
|
|
// Register with ObMan as a client
|
|
//
|
|
if (OM_Register(putTask, OMCLI_WB, &(m_pomClient)) != 0)
|
|
{
|
|
ERROR_OUT(("OM_Register failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the state
|
|
//
|
|
m_subState = STATE_START_REGISTERED_OM;
|
|
TRACE_OUT(("Moved to substate STATE_START_REGISTERED_OM"));
|
|
|
|
//
|
|
// Register an exit handler. This has to be done after registering with
|
|
// ObMan so that it gets called before the exit procedure registered by
|
|
// ObMan.
|
|
//
|
|
UT_RegisterExit(putTask, wbCoreExitHandler, this);
|
|
|
|
//
|
|
// Update the state
|
|
//
|
|
m_state = STATE_STARTED;
|
|
m_subState = STATE_STARTED_START;
|
|
|
|
rc = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(WbInit, rc);
|
|
return(rc);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// CreateWBObject()
|
|
//
|
|
BOOL WINAPI CreateWBObject
|
|
(
|
|
UTEVENT_PROC eventProc,
|
|
IWbClient** ppwbClient
|
|
)
|
|
{
|
|
BOOL rc = FALSE;
|
|
WbClient* pwbClient = NULL;
|
|
PUT_CLIENT putTask = NULL;
|
|
|
|
DebugEntry(CreateWBObject);
|
|
|
|
//
|
|
// Initialize the WB task
|
|
//
|
|
if (!UT_InitTask(UTTASK_WB, &putTask))
|
|
{
|
|
ERROR_OUT(("Can't register WB task"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Allocate the WB client object
|
|
//
|
|
pwbClient = new WbClient();
|
|
if (!pwbClient)
|
|
{
|
|
ERROR_OUT(("Couldn't allocate WbClient object"));
|
|
|
|
UT_TermTask(&putTask);
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
rc = pwbClient->WbInit(putTask, eventProc);
|
|
if (!rc)
|
|
{
|
|
pwbClient->WBP_Stop(eventProc);
|
|
pwbClient = NULL;
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
*ppwbClient = (IWbClient *)pwbClient;
|
|
|
|
DebugExitBOOL(CreateWBObject, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_Stop()
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_Stop(UTEVENT_PROC eventProc)
|
|
{
|
|
PUT_CLIENT putTask;
|
|
|
|
DebugEntry(WBP_Stop);
|
|
|
|
//
|
|
// UT_TermTask() will call our exit handler and cause cleanup.
|
|
//
|
|
|
|
putTask = m_putTask;
|
|
UT_DeregisterEvent(putTask, eventProc, NULL);
|
|
|
|
// NOTE:
|
|
// UT_TermTask() will put NULL into the pointer you pass in after it
|
|
// has finished. But part of its job is to call your exit proc. Our
|
|
// exit handler will call 'delete this' to kill us off. So when it
|
|
// winds back to UT_TermTask(), the UT_CLIENT* pointer will be invalid.
|
|
// That's why we use a temp. variable.
|
|
//
|
|
UT_TermTask(&putTask);
|
|
|
|
DebugExitVOID(WBP_Stop);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PostEvent()
|
|
//
|
|
// Post an event back to the WB applet after a delay
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PostEvent
|
|
(
|
|
UINT delay,
|
|
UINT event,
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
DebugEntry(WBP_PostEvent);
|
|
|
|
UT_PostEvent(m_putTask, m_putTask, delay, event, param1, param2);
|
|
|
|
DebugExitVOID(WBP_PostEvent);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_JoinCall
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_JoinCall
|
|
(
|
|
BOOL bContentsKeep,
|
|
UINT callID
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_JoinCall);
|
|
|
|
TRACE_OUT(("Keep contents = %s", (bContentsKeep) ? "TRUE" : "FALSE"));
|
|
TRACE_OUT(("Call ID = %d", callID));
|
|
|
|
//
|
|
// If we are to keep the existing contents, just move our workset group
|
|
// into the specified call.
|
|
//
|
|
if (bContentsKeep)
|
|
{
|
|
result = OM_WSGroupMoveReq(m_pomClient, m_hWSGroup, callID,
|
|
&(m_wsgroupCorrelator));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_WSGroupMoveReq failed"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The move request was successful, change the state to show that we
|
|
// are waiting for a move request to complete.
|
|
//
|
|
m_state = STATE_REGISTERING;
|
|
m_subState = STATE_REG_PENDING_WSGROUP_MOVE;
|
|
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_WSGROUP_MOVE"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Leave the current call. This returns the client state to what it
|
|
// should be after a wbStart call.
|
|
//
|
|
wbLeaveCall();
|
|
|
|
//
|
|
// Register with the workset group
|
|
//
|
|
result = OM_WSGroupRegisterPReq(m_pomClient, callID,
|
|
OMFP_WB, OMWSG_WB, &(m_wsgroupCorrelator));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_WSGroupRegisterReq failed, result = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the state
|
|
//
|
|
m_state = STATE_REGISTERING;
|
|
m_subState = STATE_REG_PENDING_WSGROUP_CON;
|
|
TRACE_OUT(("Moved to state STATE_REGISTERING"));
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_WSGROUP_CON"));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_JoinCall, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsDelete
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_ContentsDelete(void)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_ContentsDelete);
|
|
|
|
//
|
|
// Make sure that we have the Page Order Lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Request the lock
|
|
//
|
|
wbContentsDelete(RESET_CHANGED_FLAG);
|
|
|
|
//
|
|
// Reset the flag indicating that the contents have changed
|
|
//
|
|
m_changed = FALSE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_ContentsDelete, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsLoad
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_ContentsLoad(LPCSTR pFileName)
|
|
{
|
|
UINT result = 0;
|
|
HANDLE hFile;
|
|
|
|
DebugEntry(WBP_ContentsLoad);
|
|
|
|
//
|
|
// Check that we have the lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Check the load state
|
|
//
|
|
if (m_loadState != LOAD_STATE_EMPTY)
|
|
{
|
|
result = WB_RC_ALREADY_LOADING;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the file, and get a handle to it.
|
|
// If there is an error, then no file handle is returned.
|
|
//
|
|
result = WBP_ValidateFile(pFileName, &hFile);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Bad file header"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save the file handle for the rest of the load process
|
|
//
|
|
m_hLoadFile = hFile;
|
|
|
|
//
|
|
// We now need to make sure that the contents are deleted before we start
|
|
// adding the new objects.
|
|
//
|
|
wbContentsDelete(DONT_RESET_CHANGED_FLAG);
|
|
|
|
//
|
|
// Update the load state to show that we are waiting for the contents
|
|
// delete to complete.
|
|
//
|
|
m_loadState = LOAD_STATE_PENDING_CLEAR;
|
|
TRACE_OUT(("Moved load state to LOAD_STATE_PENDING_CLEAR"));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_ContentsLoad, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsSave
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_ContentsSave(LPCSTR pFileName)
|
|
{
|
|
UINT result = 0;
|
|
UINT index;
|
|
HANDLE hFile;
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
WB_FILE_HEADER fileHeader;
|
|
WB_END_OF_FILE endOfFile;
|
|
|
|
DebugEntry(WBP_ContentsSave);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
hFile = CreateFile(pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
result = WB_RC_CREATE_FAILED;
|
|
ERROR_OUT(("Error creating file, win32 err=%d", GetLastError()));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Write the file header. This contains the name of the function profile
|
|
// that wrote the file and allows the file type to be tested when it is
|
|
// loaded.
|
|
//
|
|
ZeroMemory(&fileHeader, sizeof(fileHeader));
|
|
fileHeader.length = sizeof(fileHeader);
|
|
fileHeader.type = TYPE_FILE_HEADER;
|
|
|
|
lstrcpy(fileHeader.functionProfile, WB_FP_NAME);
|
|
|
|
result = wbObjectSave(hFile, (LPBYTE) &fileHeader, sizeof(fileHeader));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error writing end-of-page = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Loop through the pages, saving each as we go
|
|
//
|
|
for (index = 0; index < pPageOrder->countPages; index++)
|
|
{
|
|
//
|
|
// Save the page
|
|
//
|
|
result = wbPageSave((WB_PAGE_HANDLE)pPageOrder->pages[index], hFile);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error saving page = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have successfully written the contents, we write an end-of-page
|
|
// marker to the file.
|
|
//
|
|
ZeroMemory(&endOfFile, sizeof(endOfFile));
|
|
endOfFile.length = sizeof(endOfFile);
|
|
endOfFile.type = TYPE_END_OF_FILE;
|
|
|
|
//
|
|
// Write the end-of-file object
|
|
//
|
|
result = wbObjectSave(hFile, (LPBYTE) &endOfFile, sizeof(endOfFile));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error writing end-of-page = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
TRACE_OUT(("Resetting changed flag"));
|
|
m_changed = FALSE;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// Close the file
|
|
//
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
//
|
|
// If an error occurred in saving the contents to file, and the file was
|
|
// opened, then delete it.
|
|
//
|
|
if (result != 0)
|
|
{
|
|
//
|
|
// If the file was opened successfully, close it
|
|
//
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
DeleteFile(pFileName);
|
|
}
|
|
}
|
|
|
|
|
|
DebugExitDWORD(WBP_ContentsSave, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsChanged
|
|
//
|
|
STDMETHODIMP_(BOOL) WbClient::WBP_ContentsChanged(void)
|
|
{
|
|
BOOL changed = FALSE;
|
|
UINT result;
|
|
WB_PAGE_HANDLE hPage;
|
|
POM_OBJECT pObj;
|
|
|
|
DebugEntry(WBP_ContentsChanged);
|
|
|
|
TRACE_OUT(("changed %d", m_changed));
|
|
|
|
if (m_changed)
|
|
{
|
|
//
|
|
// The whiteboard may have been changed, but if the change was to
|
|
// empty it then don't bother. This is because we cannot detect that
|
|
// the New operation was a local New or a remote clear and so we would
|
|
// always prompt after New. Assuming that the user never needs to
|
|
// always prompt after New. Assuming that the user will manually save
|
|
// an workset he really wants to empty solves this problem.
|
|
//
|
|
|
|
//
|
|
// Scan all objects looking to see what is there - get handle to first
|
|
// page
|
|
//
|
|
result = wbPageHandle(WB_PAGE_HANDLE_NULL, PAGE_FIRST, &hPage);
|
|
while (result == 0)
|
|
{
|
|
//
|
|
// Get the handle of the first object in the page workset.
|
|
//
|
|
result = OM_ObjectH(m_pomClient, m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage, 0, &pObj, FIRST);
|
|
if (result != OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
changed = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Try the next page for an object
|
|
//
|
|
result = wbPageHandle(hPage, PAGE_AFTER, &hPage);
|
|
}
|
|
}
|
|
|
|
DebugExitBOOL(WBP_ContentsChanged, changed);
|
|
return(changed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsLock
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_ContentsLock(void)
|
|
{
|
|
UINT result;
|
|
|
|
DebugEntry(WBP_ContentsLock);
|
|
|
|
//
|
|
// Check that there is no lock currently
|
|
//
|
|
QUIT_LOCKED(result);
|
|
QUIT_IF_CANCELLING_LOCK(result, WB_RC_BUSY);
|
|
|
|
//
|
|
// Request the lock
|
|
//
|
|
result = wbLock(WB_LOCK_TYPE_CONTENTS);
|
|
if (result != 0)
|
|
{
|
|
WBP_PostEvent(0, WBP_EVENT_LOCK_FAILED, result, 0);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(WBP_ContentsLock);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageOrderLock
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PageOrderLock(void)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_PageOrderLock);
|
|
|
|
//
|
|
// Check that there is no lock currently
|
|
//
|
|
QUIT_LOCKED(result);
|
|
|
|
//
|
|
// Check that we are not in the process of cancelling a lock request
|
|
//
|
|
QUIT_IF_CANCELLING_LOCK(result, WB_RC_BUSY);
|
|
|
|
//
|
|
// Request the lock
|
|
//
|
|
result = wbLock(WB_LOCK_TYPE_PAGE_ORDER);
|
|
if (result != 0)
|
|
{
|
|
WBP_PostEvent(0, WBP_EVENT_LOCK_FAILED, result, 0);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(WBP_PageOrderLock);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_Unlock
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_Unlock(void)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_Unlock);
|
|
|
|
//
|
|
// If we are currently procesing a lock cancel request, leave the
|
|
// function - it should unlock soon anyway.
|
|
//
|
|
QUIT_IF_CANCELLING_LOCK(result, 0);
|
|
|
|
//
|
|
// Check that we are currently processing a lock - the lock need not
|
|
// necessarily have completed as we allow an application to call
|
|
// WBP_Unlock any time after it has called WBP_Lock, effectively
|
|
// cancelling a lock request.
|
|
//
|
|
QUIT_NOT_PROCESSING_LOCK(result);
|
|
|
|
//
|
|
// If we have completed the last lock request, simply do the unlock:
|
|
//
|
|
// The lock is not yet released, but will be when the
|
|
// OM_OBJECT_DELETE_IND is received.
|
|
//
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
TRACE_OUT(("Unlock"));
|
|
wbUnlock();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise we are part way through processing the last lock and need
|
|
// to cancel the lock on the next OM/lock event. e.g. when we receive
|
|
// the OM_WS_LOCK indication, we should abandon lock processing and
|
|
// unlock the WS.
|
|
//
|
|
TRACE_OUT((
|
|
"Part way through last lock set state to LOCK_STATE_CANCEL_LOCK"));
|
|
m_lockState = LOCK_STATE_CANCEL_LOCK;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(WBP_Unlock);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_LockStatus
|
|
//
|
|
STDMETHODIMP_(WB_LOCK_TYPE) WbClient::WBP_LockStatus(POM_OBJECT *ppObjPersonLock)
|
|
{
|
|
DebugEntry(WBP_LockStatus);
|
|
|
|
*ppObjPersonLock = m_pObjPersonLock;
|
|
|
|
DebugExitDWORD(WBP_LockStatus, m_lockType);
|
|
return(m_lockType);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_ContentsCountPages
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_ContentsCountPages(void)
|
|
{
|
|
UINT countPages;
|
|
|
|
DebugEntry(WBP_ContentsCountPages);
|
|
|
|
countPages = (m_pageOrder).countPages;
|
|
|
|
DebugExitDWORD(WBP_ContentsCountPages, countPages);
|
|
return(countPages);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageClear
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageClear
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_PageClear);
|
|
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
|
|
result = wbPageClear(hPage, RESET_CHANGED_FLAG);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageClear, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageClearConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PageClearConfirm
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
DebugEntry(WBP_PageClearConfirm);
|
|
|
|
wbPageClearConfirm(hPage);
|
|
|
|
DebugExitVOID(WBP_PageClearConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageAddBefore - See wb.h
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageAddBefore
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_PageAddBefore);
|
|
|
|
//
|
|
// Make sure that we have the page order lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Add a new page before the specified page
|
|
//
|
|
result = wbPageAdd(hRefPage, PAGE_BEFORE, phPage,
|
|
RESET_CHANGED_FLAG);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageAddBefore, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageAddAfter - See wb.h
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageAddAfter
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_PageAddAfter);
|
|
|
|
//
|
|
// Make sure that we have the Page Order Lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Add a new page before the specified page
|
|
//
|
|
result = wbPageAdd(hRefPage, PAGE_AFTER, phPage,
|
|
RESET_CHANGED_FLAG);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageAddAfter, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageHandle - See wb.h
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageHandle
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
UINT where,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result;
|
|
|
|
DebugEntry(WBP_PageHandle);
|
|
|
|
result = wbPageHandle(hRefPage, where, phPage);
|
|
|
|
DebugExitDWORD(WBP_PageHandle, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageHandleFromNumber
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageHandleFromNumber
|
|
(
|
|
UINT pageNumber,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result;
|
|
|
|
DebugEntry(WBP_PageHandleFromNumber);
|
|
|
|
result = wbPageHandleFromNumber(pageNumber, phPage);
|
|
|
|
DebugExitDWORD(WBP_PageHandleFromNumber, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageNumberFromHandle
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageNumberFromHandle
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT pageNumber = 0;
|
|
|
|
DebugEntry(WBP_PageNumberFromHandle);
|
|
|
|
if ((hPage < FIRST_PAGE_WORKSET) || (hPage > FIRST_PAGE_WORKSET + WB_MAX_PAGES - 1))
|
|
{
|
|
WARNING_OUT(("WB: Invalid hPage=%u", (UINT) hPage));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the page handle given
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
pageNumber = wbPageOrderPageNumber(&(m_pageOrder), hPage);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageNumberFromHandle, pageNumber);
|
|
return(pageNumber);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageDelete - See wb.h
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageDelete
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_PageDelete);
|
|
|
|
//
|
|
// Make sure that we have the Page Order Lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Delete the page
|
|
//
|
|
|
|
//
|
|
// Check whether the page is already being deleted
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->state == PAGE_IN_USE)
|
|
&& (pPageState->subState == PAGE_STATE_EMPTY))
|
|
{
|
|
//
|
|
// Set the page state to show that it is now in the delete process
|
|
//
|
|
pPageState->subState = PAGE_STATE_LOCAL_DELETE;
|
|
TRACE_OUT(("Moved page %d substate to PAGE_STATE_LOCAL_DELETE",
|
|
hPage));
|
|
|
|
//
|
|
// Update the page control object
|
|
//
|
|
if (wbWritePageControl(FALSE) != 0)
|
|
{
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageDelete, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageDeleteConfirm - See wb.h
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PageDeleteConfirm
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
PWB_PAGE_ORDER pPageOrder;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_PageDeleteConfirm);
|
|
|
|
//
|
|
// Validate the specified page
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Delete the page
|
|
//
|
|
|
|
//
|
|
// Check that the page specified is waiting for a delete confirm
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
ASSERT(((pPageState->subState == PAGE_STATE_LOCAL_DELETE_CONFIRM) ||
|
|
(pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM)));
|
|
|
|
//
|
|
// Delete the page from the page order
|
|
//
|
|
pPageOrder = &(m_pageOrder);
|
|
|
|
wbPageOrderPageDelete(pPageOrder, hPage);
|
|
|
|
//
|
|
// Clear the page (to free memory)
|
|
//
|
|
if (pPageState->subState == PAGE_STATE_LOCAL_DELETE_CONFIRM)
|
|
{
|
|
TRACE_OUT(("Local delete - clearing the page"));
|
|
if (wbPageClear(hPage, RESET_CHANGED_FLAG) != 0)
|
|
{
|
|
ERROR_OUT(("Unable to clear page"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the page state to "not in use", with a substate of "ready" (we
|
|
// do not close the associated workset.
|
|
//
|
|
pPageState->state = PAGE_NOT_IN_USE;
|
|
pPageState->subState = PAGE_STATE_READY;
|
|
TRACE_OUT(("Moved page %d state to PAGE_NOT_IN_USE", hPage));
|
|
|
|
//
|
|
// Continue updating the Page Order
|
|
//
|
|
wbProcessPageControlChanges();
|
|
|
|
//
|
|
// Check the load state to see whether we are waiting to load the
|
|
// contents
|
|
//
|
|
if (m_loadState == LOAD_STATE_PENDING_DELETE)
|
|
{
|
|
//
|
|
// We are waiting to load. If there is now only one page available, we
|
|
// are ready to load, otherwise we wait for the further page deletes to
|
|
// happen.
|
|
//
|
|
if (m_pageOrder.countPages == 1)
|
|
{
|
|
//
|
|
// Start the load proper
|
|
//
|
|
wbStartContentsLoad();
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(WBP_PageDeleteConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageMoveAfter
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageMove
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT where
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_PageMove);
|
|
|
|
//
|
|
// Make sure that we have the Page Order Lock
|
|
//
|
|
QUIT_NOT_GOT_PAGE_ORDER_LOCK(result);
|
|
|
|
//
|
|
// Validate the specified page handles
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Move the page
|
|
//
|
|
result = wbPageMove(hRefPage, hPage, where);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_PageMove, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PageCountGraphics
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PageCountGraphics
|
|
(
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT count;
|
|
|
|
DebugEntry(WBP_PageCountGraphics);
|
|
|
|
//
|
|
// Count the number of graphics on the page
|
|
//
|
|
OM_WorksetCountObjects(m_pomClient, m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage, &count);
|
|
|
|
DebugExitDWORD(WBP_PageCountGraphics, count);
|
|
return(count);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicAllocate
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicAllocate
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT length,
|
|
PPWB_GRAPHIC ppGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pData;
|
|
|
|
DebugEntry(WBP_GraphicAllocate);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Allocate a graphic object
|
|
//
|
|
result = OM_ObjectAlloc(m_pomClient, m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage, length, &pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectAlloc = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Set the length of the object
|
|
//
|
|
pData->length = length;
|
|
|
|
//
|
|
// Convert the ObMan pointer to a core pointer
|
|
//
|
|
*ppGraphic = GraphicPtrFromObjectData(pData);
|
|
|
|
//
|
|
// Initialize the graphic header
|
|
//
|
|
ZeroMemory(*ppGraphic, sizeof(WB_GRAPHIC));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicAllocate, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicAddLast
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicAddLast
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
PWB_GRAPHIC pGraphic,
|
|
PWB_GRAPHIC_HANDLE phGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pData;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_GraphicAddLast);
|
|
|
|
//
|
|
// Check whether another person has an active contents lock
|
|
//
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// If the Client is asking for the lock, copy the local person ID into
|
|
// the graphic object.
|
|
//
|
|
if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL)
|
|
{
|
|
pGraphic->lockPersonID = m_personID;
|
|
}
|
|
|
|
//
|
|
// Check whether the page has been deleted but not yet confirmed: in this
|
|
// case return OK but do not add the object to the workset.
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE)
|
|
|| (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))
|
|
{
|
|
TRACE_OUT(("Object add requested in externally deleted page - ignored"));
|
|
*phGraphic = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Add the graphic object to the page
|
|
//
|
|
pData = ObjectDataPtrFromGraphic(pGraphic);
|
|
|
|
result = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
&pData,
|
|
sizeof(WB_GRAPHIC),
|
|
phGraphic,
|
|
LAST);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectAdd = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicAddLast, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicUpdateRequest
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicUpdateRequest
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
PWB_GRAPHIC pGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pData;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_GraphicUpdateRequest);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Check whether another person has an active contents lock
|
|
//
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
|
|
//
|
|
// Check whether another person has the graphic locked
|
|
//
|
|
QUIT_GRAPHIC_LOCKED(hPage, hGraphic, result);
|
|
|
|
//
|
|
// If the Client is asking for the lock, copy the local person ID into
|
|
// the graphic object.
|
|
//
|
|
if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL)
|
|
{
|
|
pGraphic->lockPersonID = m_personID;
|
|
}
|
|
|
|
//
|
|
// Check whether the page has been deleted but not yet confirmed
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE)
|
|
|| (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))
|
|
{
|
|
TRACE_OUT(("Object update requested in externally deleted page - ignored"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the object
|
|
//
|
|
pData = ObjectDataPtrFromGraphic(pGraphic);
|
|
|
|
result = OM_ObjectUpdate(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
&pData);
|
|
|
|
//
|
|
// Dont worry too much if the update fails because the object has been
|
|
// deleted, just trace an alert and return OK - the front end will be
|
|
// told that the object has gone later.
|
|
//
|
|
if (result != 0)
|
|
{
|
|
if (result == OM_RC_OBJECT_DELETED)
|
|
{
|
|
TRACE_OUT(("Update failed because object has been deleted"));
|
|
result = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
ERROR_OUT(("OM_ObjectUpdate = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Note that the object has not yet been updated. An
|
|
// OM_OBJECT_UPDATE_IND event will be generated.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicUpdateRequest, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicUpdateConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_GraphicUpdateConfirm
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
DebugEntry(WBP_GraphicUpdateConfirm);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Confirm the update to ObMan
|
|
//
|
|
OM_ObjectUpdateConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic);
|
|
|
|
|
|
DebugExitVOID(WBP_GraphicUpdateConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicReplaceRequest
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicReplaceRequest
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
PWB_GRAPHIC pGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pData;
|
|
POM_OBJECT pObjPersonLock;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_GraphicReplaceRequest);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// We allow the replace to go ahead if:
|
|
// - The object is locked by the local user
|
|
// - The object is not locked and the contents are not locked by
|
|
// a remote user
|
|
//
|
|
// Note that this allow the replace if the contents are locked by another
|
|
// user, but the local user has the object locked.
|
|
//
|
|
if (wbGraphicLocked(hPage, hGraphic, &pObjPersonLock))
|
|
{
|
|
if (pObjPersonLock != m_pObjLocal)
|
|
{
|
|
TRACE_OUT(("Graphic is locked by remote client"));
|
|
result = WB_RC_GRAPHIC_LOCKED;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
}
|
|
|
|
//
|
|
// If the Client is asking for the lock, copy the local person ID into
|
|
// the graphic object.
|
|
//
|
|
if (pGraphic->locked == WB_GRAPHIC_LOCK_LOCAL)
|
|
{
|
|
pGraphic->lockPersonID = m_personID;
|
|
}
|
|
|
|
//
|
|
// Check whether the page has been deleted but not yet confirmed
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE)
|
|
|| (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))
|
|
{
|
|
TRACE_OUT(("Object replace requested in externally deleted page - ignored"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Replace the object
|
|
//
|
|
pData = ObjectDataPtrFromGraphic(pGraphic);
|
|
|
|
result = OM_ObjectReplace(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectReplace = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Note that the object has not yet been updated. An
|
|
// OM_OBJECT_REPLACE_IND event will be generated.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicReplaceRequest, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicUpdateConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_GraphicReplaceConfirm
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
DebugEntry(WBP_GraphicReplaceConfirm);
|
|
|
|
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Confirm the update to ObMan
|
|
//
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic);
|
|
|
|
DebugExitVOID(WBP_GraphicReplaceConfirm);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicDeleteRequest
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicDeleteRequest
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_GraphicDeleteRequest);
|
|
|
|
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Check whether another person has an active contents lock
|
|
//
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
|
|
//
|
|
// Check whether another person has the graphic locked
|
|
//
|
|
QUIT_GRAPHIC_LOCKED(hPage, hGraphic, result);
|
|
|
|
//
|
|
// Delete the object
|
|
//
|
|
result = OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectDelete = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Note that at this point the object is not yet deleted. An
|
|
// OM_OBJECT_DELETE_IND event is raised and processed by the Whiteboard
|
|
// Core event handler. A WB_EVENT_GRAPHIC_DELETED is then posted to the
|
|
// client. The client then calls WBP_GraphicDeleteConfirm to complete
|
|
// the deletion.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicDeleteRequest, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicDeleteConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_GraphicDeleteConfirm
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
DebugEntry(WBP_GraphicDeleteConfirm);
|
|
|
|
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Confirm the delete
|
|
//
|
|
OM_ObjectDeleteConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic);
|
|
|
|
|
|
DebugExitVOID(WBP_GraphicDeleteConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicUnlock
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_GraphicUnlock
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic
|
|
)
|
|
{
|
|
UINT rc;
|
|
POM_OBJECTDATA pData;
|
|
PWB_PAGE_STATE pPageState;
|
|
PWB_GRAPHIC pGraphic = NULL;
|
|
PWB_GRAPHIC pNewGraphic = NULL;
|
|
|
|
DebugEntry(WBP_GraphicUnlock);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Check whether the page has been deleted but not yet confirmed
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE)
|
|
|| (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))
|
|
{
|
|
TRACE_OUT(("Object update requested in externally deleted page - ignored"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Read the object from ObMan
|
|
//
|
|
if (WBP_GraphicGet(hPage, hGraphic, &pGraphic) != 0)
|
|
{
|
|
TRACE_OUT(("Could not get graphic - leaving function"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check the local client has the graphic locked
|
|
//
|
|
QUIT_GRAPHIC_NOT_LOCKED(pGraphic, rc);
|
|
|
|
//
|
|
// Allocate a new graphic, copied from the existing one, and clear the
|
|
// lock field.
|
|
//
|
|
if (WBP_GraphicAllocate(hPage, sizeof(WB_GRAPHIC),
|
|
&pNewGraphic) != 0)
|
|
{
|
|
ERROR_OUT(("Could not allocate memory for update object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
memcpy(pNewGraphic, pGraphic, sizeof(WB_GRAPHIC));
|
|
pNewGraphic->locked = WB_GRAPHIC_LOCK_NONE;
|
|
|
|
//
|
|
// Unlock & update the object
|
|
//
|
|
pData = ObjectDataPtrFromGraphic(pNewGraphic);
|
|
pData->length = sizeof(WB_GRAPHIC);
|
|
|
|
rc = OM_ObjectUpdate(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
&pData);
|
|
|
|
//
|
|
// Dont worry too much if the update fails because the object has been
|
|
// deleted, just trace an alert and return OK - the front end will be
|
|
// told that the object has gone later.
|
|
//
|
|
if (rc != 0)
|
|
{
|
|
if (rc == OM_RC_OBJECT_DELETED)
|
|
{
|
|
TRACE_OUT(("Update failed because object has been deleted"));
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("OM_ObjectUpdate = %d", rc));
|
|
}
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Note that the object has not yet been updated. An
|
|
// OM_OBJECT_UPDATE_IND event will be generated.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// If we read the graphic successfully, release it now
|
|
//
|
|
if (pGraphic != NULL)
|
|
{
|
|
WBP_GraphicRelease(hPage, hGraphic, pGraphic);
|
|
}
|
|
|
|
DebugExitVOID(WBP_GraphicUnlock);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicMove
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicMove
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
UINT where
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(WBP_GraphicMove);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Check whether another person has an active contents lock
|
|
//
|
|
QUIT_CONTENTS_LOCKED(result);
|
|
|
|
//
|
|
// Check whether the page has been deleted but not yet confirmed
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE)
|
|
|| (pPageState->subState == PAGE_STATE_EXTERNAL_DELETE_CONFIRM))
|
|
{
|
|
TRACE_OUT(("Object moved in externally deleted page - ignored"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Do the move
|
|
//
|
|
result = OM_ObjectMove(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
(OM_POSITION)where);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectMove = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicMove, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicSelectLast
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicSelect
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
POINT point,
|
|
WB_GRAPHIC_HANDLE hRefGraphic,
|
|
UINT where,
|
|
PWB_GRAPHIC_HANDLE phGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_GraphicSelect);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Get the handle of the last object in the workset
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hRefGraphic,
|
|
&hRefGraphic,
|
|
(OM_POSITION)where);
|
|
|
|
if (result == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
result = WB_RC_NO_SUCH_GRAPHIC;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectH = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the previous matching graphic - this function starts from the
|
|
// object in hRefGraphic and works back.
|
|
//
|
|
result = wbGraphicSelectPrevious(hPage,
|
|
&point,
|
|
hRefGraphic,
|
|
phGraphic);
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicSelect, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicGet
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicGet
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
PPWB_GRAPHIC ppGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
UINT rc;
|
|
POM_OBJECTDATA pData;
|
|
PWB_GRAPHIC pGraphic;
|
|
POM_OBJECT pObjPersonLock;
|
|
|
|
DebugEntry(WBP_GraphicGet);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Read the object.
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectRead = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Convert the ObMan pointer to a core pointer
|
|
//
|
|
pGraphic = GraphicPtrFromObjectData(pData);
|
|
|
|
//
|
|
// If the graphic object indicates that it is locked - verify that the
|
|
// locking person is still in the call.
|
|
//
|
|
if (pGraphic->locked != WB_GRAPHIC_LOCK_NONE)
|
|
{
|
|
TRACE_OUT(("Graphic has lock flag set"));
|
|
|
|
//
|
|
// Convert the lock person ID in the graphic to a person handle
|
|
//
|
|
rc = OM_ObjectIDToPtr(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pGraphic->lockPersonID,
|
|
&pObjPersonLock);
|
|
|
|
if (rc == OM_RC_BAD_OBJECT_ID)
|
|
{
|
|
//
|
|
// The locking person is no longer in the call - reset the lock flag
|
|
// in the graphic. This tells the client that the graphic can be
|
|
// changed.
|
|
//
|
|
TRACE_OUT(("Lock person has left call - resetting lock flag"));
|
|
pGraphic->locked = WB_GRAPHIC_LOCK_NONE;
|
|
}
|
|
else
|
|
{
|
|
if (rc == 0)
|
|
{
|
|
//
|
|
// The object is locked - check whether the lock belongs to the
|
|
// local person or a remote person.
|
|
//
|
|
if (pObjPersonLock == m_pObjLocal)
|
|
{
|
|
//
|
|
// Change the lock type to local to tell the client that they
|
|
// have the lock on this object.
|
|
//
|
|
TRACE_OUT(("Lock belongs to local person"));
|
|
pGraphic->locked = WB_GRAPHIC_LOCK_LOCAL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Change the lock type to remote to tell the client that another
|
|
// person has the lock on this object.
|
|
//
|
|
TRACE_OUT(("Lock belongs to remote person"));
|
|
pGraphic->locked = WB_GRAPHIC_LOCK_REMOTE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the pointer to the graphic data
|
|
//
|
|
*ppGraphic = pGraphic;
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_GraphicGet, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicRelease
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_GraphicRelease
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
PWB_GRAPHIC pGraphic
|
|
)
|
|
{
|
|
POM_OBJECTDATA pData;
|
|
|
|
DebugEntry(WBP_GraphicRelease);
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Release the object.
|
|
//
|
|
pData = ObjectDataPtrFromGraphic(pGraphic);
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hGraphic,
|
|
&pData);
|
|
|
|
|
|
DebugExitVOID(WBP_GraphicRelease);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_GraphicHandle
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GraphicHandle
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hRefGraphic,
|
|
UINT where,
|
|
PWB_GRAPHIC_HANDLE phGraphic
|
|
)
|
|
{
|
|
UINT result;
|
|
|
|
DebugEntry(WBP_GraphicHandle);
|
|
|
|
|
|
|
|
//
|
|
// Check that the page handle is valid
|
|
//
|
|
ASSERT(GetPageState(hPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Get the handle of the first object in the page workset.
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
hRefGraphic,
|
|
phGraphic,
|
|
(OM_POSITION)where);
|
|
if (result == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
TRACE_OUT(("No objects there"));
|
|
result = WB_RC_NO_SUCH_GRAPHIC;
|
|
}
|
|
|
|
|
|
DebugExitDWORD(WBP_GraphicHandle, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonHandleFirst
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PersonHandleFirst
|
|
(
|
|
POM_OBJECT * ppObjPerson
|
|
)
|
|
{
|
|
DebugEntry(WBP_PersonHandleFirst);
|
|
|
|
OM_ObjectH(m_pomClient, m_hWSGroup,
|
|
USER_INFORMATION_WORKSET, 0, ppObjPerson, FIRST);
|
|
|
|
DebugExitVOID(WBP_PersonHandleFirst);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonHandleNext
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PersonHandleNext
|
|
(
|
|
POM_OBJECT pObjPerson,
|
|
POM_OBJECT * ppObjPersonNext
|
|
)
|
|
{
|
|
UINT rc;
|
|
|
|
DebugEntry(WBP_PersonHandleNext);
|
|
|
|
//
|
|
// Get the handle of the next object in the user information workset.
|
|
//
|
|
rc = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjPerson,
|
|
ppObjPersonNext,
|
|
AFTER);
|
|
|
|
if (rc == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
rc = WB_RC_NO_SUCH_PERSON;
|
|
}
|
|
else if (rc != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectNextH = %d", rc));
|
|
}
|
|
|
|
|
|
DebugExitDWORD(WBP_PersonHandleNext, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonHandleLocal
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PersonHandleLocal
|
|
(
|
|
POM_OBJECT * ppObjPerson
|
|
)
|
|
{
|
|
DebugEntry(WBP_PersonHandleLocal);
|
|
|
|
*ppObjPerson = m_pObjLocal;
|
|
|
|
DebugExitVOID(WBP_PersonHandleLocal);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonCountInCall
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_PersonCountInCall(void)
|
|
{
|
|
UINT count;
|
|
POM_OBJECT pObj;
|
|
|
|
DebugEntry(WBP_PersonCountInCall);
|
|
|
|
//
|
|
// Get the count:
|
|
//
|
|
pObj = NULL;
|
|
WBP_PersonHandleFirst(&pObj);
|
|
for (count = 1; ; count++)
|
|
{
|
|
if (WBP_PersonHandleNext(pObj, &pObj) == WB_RC_NO_SUCH_PERSON)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
DebugExitDWORD(WBP_PersonCountInCall, count);
|
|
return(count);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_GetPersonData
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_GetPersonData
|
|
(
|
|
POM_OBJECT pObjPerson,
|
|
PWB_PERSON pPerson
|
|
)
|
|
{
|
|
UINT rc;
|
|
|
|
DebugEntry(WBP_GetPersonData);
|
|
|
|
ASSERT(!IsBadWritePtr(pPerson, sizeof(WB_PERSON)));
|
|
|
|
//
|
|
// Get the object.
|
|
//
|
|
rc = wbPersonGet(pObjPerson, pPerson);
|
|
|
|
DebugExitDWORD(WBP_GetPersonData, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_SetLocalPersonData
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_SetLocalPersonData(PWB_PERSON pPerson)
|
|
{
|
|
UINT rc;
|
|
POM_OBJECTDATA pUserObject;
|
|
|
|
DebugEntry(WBP_SetPersonData);
|
|
|
|
ASSERT(!IsBadReadPtr(pPerson, sizeof(WB_PERSON)));
|
|
|
|
//
|
|
// Allocate a user object
|
|
//
|
|
rc = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
sizeof(WB_PERSON),
|
|
&pUserObject);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectAlloc = %d", rc));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Set the length of the object
|
|
//
|
|
pUserObject->length = sizeof(WB_PERSON);
|
|
|
|
//
|
|
// Copy the user information into the ObMan object
|
|
//
|
|
memcpy(pUserObject->data, pPerson, sizeof(WB_PERSON));
|
|
|
|
//
|
|
// Replace the user object
|
|
//
|
|
rc = OM_ObjectReplace(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
m_pObjLocal,
|
|
&pUserObject);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectReplace"));
|
|
|
|
//
|
|
// Discard the object
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&pUserObject);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// Note that the object has not yet been updated. An
|
|
// OM_OBJECT_UPDATE_IND event will be generated.
|
|
//
|
|
DebugExitDWORD(WBP_SetPersonData, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonUpdateConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PersonUpdateConfirm
|
|
(
|
|
POM_OBJECT pObjPerson
|
|
)
|
|
{
|
|
DebugEntry(WBP_PersonUpdateConfirm);
|
|
|
|
//
|
|
// Confirm the update to ObMan
|
|
//
|
|
OM_ObjectUpdateConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjPerson);
|
|
|
|
DebugExitVOID(WBP_PersonUpdateConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonReplaceConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PersonReplaceConfirm
|
|
(
|
|
POM_OBJECT pObjPerson
|
|
)
|
|
{
|
|
DebugEntry(WBP_PersonReplaceConfirm);
|
|
|
|
//
|
|
// Confirm the replace to ObMan
|
|
//
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjPerson);
|
|
|
|
DebugExitVOID(WBP_PersonReplaceConfirm);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_PersonLeftConfirm
|
|
//
|
|
STDMETHODIMP_(void) WbClient::WBP_PersonLeftConfirm
|
|
(
|
|
POM_OBJECT pObjPerson
|
|
)
|
|
{
|
|
DebugEntry(WBP_PersonLeftConfirm);
|
|
|
|
//
|
|
// Confirm the update to ObMan
|
|
//
|
|
OM_ObjectDeleteConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjPerson);
|
|
|
|
DebugExitVOID(WBP_PersonLeftConfirm);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_SyncPositionGet
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_SyncPositionGet
|
|
(
|
|
PWB_SYNC pSync
|
|
)
|
|
{
|
|
UINT result;
|
|
POM_OBJECTDATA pSyncObject = NULL;
|
|
PWB_SYNC_CONTROL pSyncControl = NULL;
|
|
|
|
DebugEntry(WBP_SyncPositionGet);
|
|
|
|
|
|
ASSERT(!IsBadWritePtr(pSync, sizeof(WB_SYNC)));
|
|
|
|
//
|
|
// Read the Sync Control object
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
m_pObjSyncControl,
|
|
&pSyncObject);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error reading Sync Control Object = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSyncControl = (PWB_SYNC_CONTROL) pSyncObject->data;
|
|
|
|
//
|
|
// Copy the Sync Person details to the result field
|
|
// NOTE:
|
|
// LiveLan sends a larger object, we need to ignore the stuff past the
|
|
// end.
|
|
//
|
|
if (pSyncControl->sync.length != sizeof(WB_SYNC))
|
|
{
|
|
WARNING_OUT(("WBP_SyncPositionGet (interop): Remote created WB_SYNC of size %d, we expected size %d",
|
|
pSyncControl->sync.length, sizeof(WB_SYNC)));
|
|
}
|
|
memcpy(pSync, &pSyncControl->sync, min(sizeof(WB_SYNC), pSyncControl->sync.length));
|
|
|
|
//
|
|
// Release the Sync Control Object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
m_pObjSyncControl,
|
|
&pSyncObject);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_SyncPositionGet, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_SyncPositionUpdate
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_SyncPositionUpdate
|
|
(
|
|
PWB_SYNC pSync
|
|
)
|
|
{
|
|
UINT result;
|
|
POM_OBJECTDATA pSyncObject = NULL;
|
|
PWB_SYNC_CONTROL pSyncControl = NULL;
|
|
|
|
DebugEntry(WBP_SyncPositionUpdate);
|
|
|
|
ASSERT(!IsBadReadPtr(pSync, sizeof(WB_SYNC)));
|
|
|
|
//
|
|
// Write the new sync control object (do not create it)
|
|
//
|
|
result = wbWriteSyncControl(pSync, FALSE);
|
|
|
|
DebugExitDWORD(WBP_SyncPositionUpdate, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WBP_CancelLoad
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_CancelLoad(void)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(WBP_CancelLoad);
|
|
|
|
//
|
|
// Check a load is in progress
|
|
//
|
|
if (m_loadState == LOAD_STATE_EMPTY)
|
|
{
|
|
TRACE_OUT(("request to cancel load, but there is no load in progress"));
|
|
result = WB_RC_NOT_LOADING;
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRACE_OUT(("Cancelling load in progress"));
|
|
|
|
//
|
|
// Close the file
|
|
//
|
|
if (m_hLoadFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(m_hLoadFile);
|
|
m_hLoadFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// reset the load state to show we're no longer loading.
|
|
//
|
|
m_loadState = LOAD_STATE_EMPTY;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(WBP_CancelLoad, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: WBP_ValidateFile
|
|
//
|
|
// Purpose: Validate that a passed filename holds a valid whiteboard file
|
|
//
|
|
// Returns: 0 if successful
|
|
// !0 if an error
|
|
//
|
|
//
|
|
STDMETHODIMP_(UINT) WbClient::WBP_ValidateFile
|
|
(
|
|
LPCSTR pFileName,
|
|
HANDLE * phFile
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
HANDLE hFile;
|
|
WB_FILE_HEADER fileHeader;
|
|
UINT length;
|
|
ULONG cbSizeRead;
|
|
BOOL fileOpen = FALSE;
|
|
|
|
DebugEntry(WBP_ValidateFile);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
hFile = CreateFile(pFileName, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
WARNING_OUT(("Error opening file, win32 err=%d", GetLastError()));
|
|
result = WB_RC_CREATE_FAILED;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that we have opened the file successfully
|
|
//
|
|
fileOpen = TRUE;
|
|
|
|
//
|
|
// Read the file header length
|
|
//
|
|
if (! ReadFile(hFile, (void *) &length, sizeof(length), &cbSizeRead, NULL))
|
|
{
|
|
WARNING_OUT(("Error reading file header length, win32 err=%d", GetLastError()));
|
|
result = WB_RC_READ_FAILED;
|
|
DC_QUIT;
|
|
}
|
|
ASSERT(cbSizeRead == sizeof(length));
|
|
|
|
if (length != sizeof(fileHeader))
|
|
{
|
|
WARNING_OUT(("Bad file header"));
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Read the file header
|
|
//
|
|
if (! ReadFile(hFile, (void *) &fileHeader, sizeof(fileHeader), &cbSizeRead, NULL))
|
|
{
|
|
WARNING_OUT(("Error reading file header, win32 err=%d", GetLastError()));
|
|
result = WB_RC_READ_FAILED;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (cbSizeRead != sizeof(fileHeader))
|
|
{
|
|
WARNING_OUT(("Could not read file header"));
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the file header
|
|
//
|
|
if ( (fileHeader.type != TYPE_FILE_HEADER)
|
|
|| lstrcmp(fileHeader.functionProfile, WB_FP_NAME))
|
|
{
|
|
WARNING_OUT(("Bad function profile in file header"));
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// Return the handle, if the user needs it.
|
|
//
|
|
if ( (result == 0) && (phFile != NULL))
|
|
{
|
|
TRACE_OUT(("return file handle"));
|
|
*phFile = hFile;
|
|
}
|
|
|
|
//
|
|
// Close the file if there has been an error or the caller simply
|
|
// doesnt want the file handle.
|
|
//
|
|
if ( (fileOpen) &&
|
|
((phFile == NULL) || (result != 0)) )
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
DebugExitDWORD(WBP_ValidateFile, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGraphicLocked
|
|
//
|
|
// Purpose: Test whether a client has a lock on the specified graphic, and
|
|
// if so, return the person handle of the client holding the lock.
|
|
//
|
|
// Returns: TRUE if a client has a lock
|
|
// FALSE otherwise
|
|
//
|
|
//
|
|
BOOL WbClient::wbGraphicLocked
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
POM_OBJECT * ppObjLock
|
|
)
|
|
{
|
|
BOOL result = FALSE;
|
|
UINT rc;
|
|
PWB_GRAPHIC pGraphic = NULL;
|
|
|
|
DebugEntry(wbGraphicLocked);
|
|
|
|
//
|
|
// Read the object
|
|
//
|
|
if (WBP_GraphicGet(hPage, hGraphic, &pGraphic) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Look at its lock details
|
|
//
|
|
if (pGraphic->locked != WB_GRAPHIC_LOCK_NONE)
|
|
{
|
|
//
|
|
// The lock flag in the graphic is set
|
|
//
|
|
|
|
//
|
|
// Convert the lock user ID in the graphic to a handle
|
|
//
|
|
rc = OM_ObjectIDToPtr(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pGraphic->lockPersonID,
|
|
ppObjLock);
|
|
|
|
if (rc == 0)
|
|
{
|
|
|
|
TRACE_OUT(("Graphic is locked"));
|
|
result = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (rc != OM_RC_BAD_OBJECT_ID)
|
|
{
|
|
//
|
|
// An error occurred in converting the objectID
|
|
//
|
|
TRACE_OUT(("Error converting object ID to handle"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The object is not locked (or the lock user has left the call)
|
|
//
|
|
TRACE_OUT(("Graphic is not locked"));
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// If the graphic is still held by us, release it
|
|
//
|
|
if (pGraphic != NULL)
|
|
{
|
|
WBP_GraphicRelease(hPage, hGraphic, pGraphic);
|
|
}
|
|
|
|
DebugExitBOOL(wbGraphicLocked, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbAddLocalUserObject
|
|
//
|
|
// Purpose: Add an object to the user information workset for the local
|
|
// user.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbAddLocalUserObject(void)
|
|
{
|
|
UINT rc;
|
|
POM_OBJECTDATA pData;
|
|
PWB_PERSON pUser;
|
|
CM_STATUS cmStatus;
|
|
|
|
DebugEntry(wbAddLocalUserObject);
|
|
|
|
TRACE_OUT(("Adding the necessary control objects"));
|
|
|
|
//
|
|
// Build a user object for this user and write it to the User Information
|
|
// Workset.
|
|
//
|
|
rc = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
sizeof(WB_PERSON),
|
|
&pData);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error allocating object = %d", rc));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData->length = sizeof(WB_PERSON);
|
|
pUser = (PWB_PERSON) (pData->data);
|
|
|
|
//
|
|
// Initialize the contents of the user object for this user
|
|
//
|
|
TRACE_OUT(("Initialising user contents"));
|
|
|
|
ZeroMemory(pUser, sizeof(WB_PERSON));
|
|
|
|
pUser->currentPage = FIRST_PAGE_WORKSET; // lonchanc: it was 1.
|
|
pUser->synced = FALSE;
|
|
pUser->pointerActive = FALSE;
|
|
pUser->pointerPage = FIRST_PAGE_WORKSET; // lonchanc: it was 1.
|
|
pUser->colorId = (TSHR_UINT16)wbSelectPersonColor();
|
|
|
|
//
|
|
// Fill in the Call Manager personID if we are in a call.
|
|
//
|
|
if (CMS_GetStatus(&cmStatus))
|
|
{
|
|
TRACE_OUT(("CMG personID %u", cmStatus.localHandle));
|
|
pUser->cmgPersonID = cmStatus.localHandle;
|
|
}
|
|
else
|
|
{
|
|
pUser->cmgPersonID = 0;
|
|
}
|
|
|
|
//
|
|
// Copy the user name into the object:
|
|
//
|
|
lstrcpy(pUser->personName, cmStatus.localName);
|
|
|
|
//
|
|
// Copy the person's color into the client's data
|
|
//
|
|
m_colorId = pUser->colorId;
|
|
|
|
//
|
|
// Add the object to the User Information Workset, saving the handle of
|
|
// the user object in the client details.
|
|
//
|
|
rc = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&pData,
|
|
WB_PERSON_OBJECT_UPDATE_SIZE,
|
|
&m_pObjLocal,
|
|
LAST);
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// The add failed, we must discard the object
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&pData);
|
|
|
|
ERROR_OUT(("Error adding user object = %d", rc));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save the ID of this user in the client details (for later use in the
|
|
// lock information).
|
|
//
|
|
OM_ObjectPtrToID(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
m_pObjLocal,
|
|
&(m_personID));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbAddLocalUserObject, rc);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGetEmptyPageHandle
|
|
//
|
|
// Purpose: Return a handle for a page that does not have its workset open.
|
|
//
|
|
// Returns: Handle of free page (or 0 if none exists)
|
|
//
|
|
//
|
|
WB_PAGE_HANDLE WbClient::wbGetEmptyPageHandle(void)
|
|
{
|
|
UINT index;
|
|
WB_PAGE_HANDLE hPage = WB_PAGE_HANDLE_NULL;
|
|
PWB_PAGE_STATE pPageState = m_pageStates;
|
|
|
|
//
|
|
// Search the page list for an empty entry
|
|
//
|
|
for (index = 0; index < WB_MAX_PAGES; index++, pPageState++)
|
|
{
|
|
if ( (pPageState->state == PAGE_NOT_IN_USE)
|
|
&& (pPageState->subState == PAGE_STATE_EMPTY))
|
|
{
|
|
hPage = PAGE_INDEX_TO_HANDLE(index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return(hPage);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGetReadyPageHandle
|
|
//
|
|
// Purpose: Return a handle for a page that has its workset open but is not
|
|
// currently in use.
|
|
//
|
|
// Returns: Handle of free page (or 0 if none exists)
|
|
//
|
|
//
|
|
WB_PAGE_HANDLE WbClient::wbGetReadyPageHandle(void)
|
|
{
|
|
UINT index;
|
|
WB_PAGE_HANDLE hPage = WB_PAGE_HANDLE_NULL;
|
|
PWB_PAGE_STATE pPageState = m_pageStates;
|
|
|
|
//
|
|
// Search the page list for a ready entry
|
|
//
|
|
for (index = 0; index < WB_MAX_PAGES; index++, pPageState++)
|
|
{
|
|
if ( (pPageState->state == PAGE_NOT_IN_USE)
|
|
&& (pPageState->subState == PAGE_STATE_READY))
|
|
{
|
|
hPage = PAGE_INDEX_TO_HANDLE(index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(hPage);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageOrderPageNumber
|
|
//
|
|
// Purpose: Return the number of the specified page.
|
|
// This function performs no validation on its parameters.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
UINT WbClient::wbPageOrderPageNumber
|
|
(
|
|
PWB_PAGE_ORDER pPageOrder,
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT index;
|
|
POM_WORKSET_ID pPage = pPageOrder->pages;
|
|
|
|
DebugEntry(wbPageOrderPageNumber);
|
|
|
|
//
|
|
// Search the page order list for the page handle (workset ID)
|
|
//
|
|
for (index = 0; index <= pPageOrder->countPages; index++)
|
|
{
|
|
if (pPage[index] == (OM_WORKSET_ID)hPage)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The page was not found - this is an internal error
|
|
//
|
|
ERROR_OUT(("Page handle not found"));
|
|
|
|
//
|
|
// Return the page number starting from 1.
|
|
//
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPageOrderPageNumber, index + 1);
|
|
return(index + 1);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageOrderPageAdd
|
|
//
|
|
// Purpose: Add a new page to a page order structure. This function expects
|
|
// the parameters to be valid - they must be checked before
|
|
// calling it. It also assumes that there is space in the page
|
|
// list for the new page.
|
|
//
|
|
// Params: pPageOrder - pointer to page list
|
|
// hRefPage - page used as a reference point for the new page
|
|
// hPage - handle of the page to be added
|
|
// where - relative position - before or after hRefPage
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbPageOrderPageAdd
|
|
(
|
|
PWB_PAGE_ORDER pPageOrder,
|
|
WB_PAGE_HANDLE hRefPage,
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT where
|
|
)
|
|
{
|
|
UINT index;
|
|
POM_WORKSET_ID pPage = pPageOrder->pages;
|
|
|
|
DebugEntry(wbPageOrderPageAdd);
|
|
|
|
//
|
|
// Process according to the add position
|
|
//
|
|
switch(where)
|
|
{
|
|
case PAGE_FIRST:
|
|
index = 0;
|
|
if (pPageOrder->countPages != 0)
|
|
{
|
|
UT_MoveMemory(&pPage[1], &pPage[0], pPageOrder->countPages*sizeof(pPage[0]));
|
|
}
|
|
break;
|
|
|
|
case PAGE_LAST:
|
|
index = pPageOrder->countPages;
|
|
break;
|
|
|
|
case PAGE_AFTER:
|
|
case PAGE_BEFORE:
|
|
//
|
|
// Make an empty space in the page order list
|
|
//
|
|
index = wbPageOrderPageNumber(pPageOrder, hRefPage);
|
|
if (where == PAGE_BEFORE)
|
|
{
|
|
index--;
|
|
}
|
|
|
|
UT_MoveMemory(&pPage[index + 1], &pPage[index],
|
|
(pPageOrder->countPages - index)*sizeof(pPage[0]));
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad where parameter"));
|
|
}
|
|
|
|
//
|
|
// Save the new page handle in the list
|
|
//
|
|
pPage[index] = hPage;
|
|
|
|
//
|
|
// Show that the extra page is now present
|
|
//
|
|
pPageOrder->countPages += 1;
|
|
|
|
DebugExitVOID(wbPageOrderPageAdd);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageOrderPageDelete
|
|
//
|
|
// Purpose: Remove the specified page from a page order structure. This
|
|
// function expects its the parameters to be valid - they must be
|
|
// checked before calling it.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbPageOrderPageDelete
|
|
(
|
|
PWB_PAGE_ORDER pPageOrder,
|
|
WB_PAGE_HANDLE hPage
|
|
)
|
|
{
|
|
UINT index;
|
|
POM_WORKSET_ID pPage = pPageOrder->pages;
|
|
|
|
DebugEntry(wbPageOrderPageDelete);
|
|
|
|
index = wbPageOrderPageNumber(pPageOrder, hPage);
|
|
UT_MoveMemory(&pPage[index - 1],
|
|
&pPage[index],
|
|
(pPageOrder->countPages - index)*sizeof(pPage[0]));
|
|
|
|
pPageOrder->countPages -= 1;
|
|
|
|
DebugExitVOID(wbPageOrderPageDelete);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPagesPageAdd
|
|
//
|
|
// Purpose: Add a new page to the internal page list. This function expects
|
|
// the parameters to be valid - they must be checked before
|
|
// calling it. It also assumes that there is space in the page
|
|
// list for the new page.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbPagesPageAdd
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT where
|
|
)
|
|
{
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(wbPagesPageAdd);
|
|
|
|
//
|
|
// Add the page to the page order structure
|
|
//
|
|
wbPageOrderPageAdd(&(m_pageOrder), hRefPage, hPage, where);
|
|
|
|
//
|
|
// Update the page state information
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
pPageState->state = PAGE_IN_USE;
|
|
pPageState->subState = PAGE_STATE_EMPTY;
|
|
|
|
DebugExitVOID(wbPagesPageAdd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbClientReset
|
|
//
|
|
// Purpose: Reset the client data to a state where the client is not in a
|
|
// call, but is registered with ObMan and has event and exit
|
|
// handlers registered with utilities.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbClientReset(void)
|
|
{
|
|
UINT index;
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
PWB_PAGE_STATE pPageState = m_pageStates;
|
|
|
|
DebugEntry(wbClientReset);
|
|
|
|
//
|
|
// Initialize object handles
|
|
//
|
|
m_hWSGroup = (OM_WSGROUP_HANDLE) NULL;
|
|
|
|
m_pObjPageControl = NULL;
|
|
m_pObjSyncControl = NULL;
|
|
m_pObjLocal = NULL;
|
|
|
|
m_pObjLock = NULL;
|
|
m_pObjPersonLock = NULL;
|
|
|
|
//
|
|
// Initialize the status variables
|
|
//
|
|
m_errorState = ERROR_STATE_EMPTY;
|
|
m_changed = FALSE;
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
m_lockType = WB_LOCK_TYPE_NONE;
|
|
m_lockRequestType = WB_LOCK_TYPE_NONE;
|
|
|
|
m_loadState = LOAD_STATE_EMPTY;
|
|
if (m_hLoadFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(m_hLoadFile);
|
|
m_hLoadFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
m_countReadyPages = 0;
|
|
|
|
|
|
//
|
|
// Zero the whole structure
|
|
//
|
|
ZeroMemory(pPageOrder, sizeof(*pPageOrder));
|
|
|
|
//
|
|
// Set the object type
|
|
//
|
|
pPageOrder->objectType = TYPE_CONTROL_PAGE_ORDER;
|
|
|
|
//
|
|
// Set up the page control elements
|
|
//
|
|
pPageOrder->generationLo = 1;
|
|
pPageOrder->generationHi = 0;
|
|
pPageOrder->countPages = 0;
|
|
|
|
//
|
|
// Initialize the page state structures
|
|
//
|
|
for (index = 0; index < WB_MAX_PAGES; index++, pPageState++)
|
|
{
|
|
pPageState->state = PAGE_NOT_IN_USE;
|
|
pPageState->subState = PAGE_STATE_EMPTY;
|
|
}
|
|
|
|
DebugExitVOID(wbClientReset);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWsGroupRegisterCon
|
|
//
|
|
// Purpose: Routine processing OM_WSGROUP_REGISTER_CON events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWsGroupRegisterCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed;
|
|
|
|
DebugEntry(wbOnWsGroupRegisterCon);
|
|
|
|
//
|
|
// Check that this is the event we are expecting
|
|
//
|
|
if (pEvent32->correlator != m_wsgroupCorrelator)
|
|
{
|
|
//
|
|
// We are not expecting this event, this means that it must be for a
|
|
// workset group which we wanted to deregister from (but had not yet
|
|
// received confirmation). So deregister immediately.
|
|
//
|
|
|
|
//
|
|
// Check that the return code for the registration is OK
|
|
//
|
|
if (pEvent32->result == 0)
|
|
{
|
|
OM_WSGroupDeregister(m_pomClient, &(pEvent16->hWSGroup));
|
|
}
|
|
|
|
processed = FALSE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that we have processed the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Test for the correct state
|
|
//
|
|
if (m_subState != STATE_REG_PENDING_WSGROUP_CON)
|
|
{
|
|
//
|
|
// We are not in the correct state for this event - this is an internal
|
|
// error.
|
|
//
|
|
ERROR_OUT(("Not in correct state for WSGroupRegisterCon"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that the return code for the registration is OK
|
|
//
|
|
if (pEvent32->result != 0)
|
|
{
|
|
//
|
|
// Registration with the workset group failed - tidy up
|
|
//
|
|
wbError();
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Registration with the workset group succeeded
|
|
//
|
|
m_hWSGroup = pEvent16->hWSGroup;
|
|
|
|
//
|
|
// Get the clients network ID, used in graphic objects to determine where
|
|
// they are loaded.
|
|
//
|
|
if (!wbGetNetUserID())
|
|
{
|
|
//
|
|
// Tidy up (and post an error event to the client)
|
|
//
|
|
ERROR_OUT(("Failed to get user ID"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Start opening the worksets. We open them one at a time and wait for
|
|
// the response to avoid flooding the message queue.
|
|
// The user information workset is given high priority. This allows
|
|
// remote pointer movements to travel quickly.
|
|
//
|
|
if (OM_WorksetOpenPReq(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
NET_HIGH_PRIORITY,
|
|
TRUE,
|
|
&(m_worksetOpenCorrelator)) != 0)
|
|
{
|
|
ERROR_OUT(("User Information Workset Open Failed"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Move to the next state
|
|
//
|
|
m_subState = STATE_REG_PENDING_USER_WORKSET;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWsGroupRegisterCon, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWorksetOpenCon
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_OPEN_CON events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWorksetOpenCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
OM_WORKSET_ID eventWorksetID;
|
|
|
|
DebugEntry(wbOnWorksetOpenCon);
|
|
|
|
//
|
|
// Process according to the workset ID
|
|
//
|
|
eventWorksetID = pEvent16->worksetID;
|
|
|
|
//
|
|
// If the event is for a page workset
|
|
//
|
|
if (eventWorksetID >= FIRST_PAGE_WORKSET)
|
|
{
|
|
//
|
|
// We are opening a page workset
|
|
//
|
|
processed = wbOnPageWorksetOpenCon(param1, param2);
|
|
if (!processed)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are done if this is a page workset other than the 1st page workset
|
|
//
|
|
if (eventWorksetID > FIRST_PAGE_WORKSET)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Now check if it is one of the control worksets (the first page workset
|
|
// is both a control workset and a page workset).
|
|
//
|
|
if (eventWorksetID != FIRST_PAGE_WORKSET)
|
|
{
|
|
//
|
|
// Check the message correlator
|
|
//
|
|
if (pEvent32->correlator != m_worksetOpenCorrelator)
|
|
{
|
|
TRACE_OUT(("Correlators do not match - quitting"));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are opening a control workset - process the event
|
|
//
|
|
wbOnControlWorksetOpenCon(param1, param2);
|
|
processed = TRUE;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWorksetOpenCon, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnControlWorksetOpenCon
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_OPEN_CON events for control
|
|
// worksets.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnControlWorksetOpenCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
UINT rc;
|
|
OM_WORKSET_ID eventId;
|
|
|
|
DebugEntry(wbOnControlWorksetOpenCon);
|
|
|
|
//
|
|
// Check the return code in the open
|
|
//
|
|
if (pEvent32->result != 0)
|
|
{
|
|
ERROR_OUT(("Error reported on workset open = %d", pEvent32->result));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If we are in registration, we are opening the required worksets -
|
|
// continue the process.
|
|
//
|
|
if (m_state > STATE_REGISTERING)
|
|
{
|
|
ERROR_OUT(("Control workset open con after registration"));
|
|
}
|
|
|
|
//
|
|
// Set up for opening the next workset
|
|
//
|
|
eventId = pEvent16->worksetID;
|
|
switch(eventId)
|
|
{
|
|
case USER_INFORMATION_WORKSET:
|
|
//
|
|
// The user information workset is given high priority. This allows
|
|
// remote pointer movements to travel quickly.
|
|
//
|
|
TRACE_OUT(("Opening Page Control workset"));
|
|
rc = OM_WorksetOpenPReq(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
NET_HIGH_PRIORITY,
|
|
FALSE,
|
|
&(m_worksetOpenCorrelator));
|
|
|
|
m_subState = STATE_REG_PENDING_WORKSET_OPEN;
|
|
break;
|
|
|
|
case PAGE_CONTROL_WORKSET:
|
|
//
|
|
// The sync control workset is given high priority to allow sync
|
|
// updates to travel quickly.
|
|
//
|
|
TRACE_OUT(("Opening Sync Control workset"));
|
|
rc = OM_WorksetOpenPReq(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
NET_HIGH_PRIORITY,
|
|
FALSE,
|
|
&(m_worksetOpenCorrelator));
|
|
break;
|
|
|
|
case SYNC_CONTROL_WORKSET:
|
|
//
|
|
// Open the first of the page worksets - we must do this to allow us
|
|
// to use it as the only page available if we are the first person in
|
|
// the call.
|
|
//
|
|
TRACE_OUT(("Opening first page workset"));
|
|
rc = wbPageWorksetOpen((WB_PAGE_HANDLE)FIRST_PAGE_WORKSET,
|
|
OPEN_LOCAL);
|
|
break;
|
|
|
|
case FIRST_PAGE_WORKSET:
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad workset ID"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check whether we have just opened another workset
|
|
//
|
|
if (eventId != FIRST_PAGE_WORKSET)
|
|
{
|
|
//
|
|
// Test the return code from the open
|
|
//
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Workset open failed = %d", rc));
|
|
wbError();
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We have now opened all the control worksets. We now add the required
|
|
// control objects.
|
|
//
|
|
rc = wbAddLocalUserObject();
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// Stop the join call process, tidy up and send an error message to the
|
|
// client.
|
|
//
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
m_subState = STATE_REG_USER_OBJECT_ADDED;
|
|
TRACE_OUT(("Moved to substate STATE_REG_USER_OBJECT_ADDED"));
|
|
|
|
//
|
|
// Check whether the Page Control objects are available yet (they could
|
|
// have been added by another user in the call).
|
|
//
|
|
TRACE_OUT(("%x PAGE WS object, %x SYNC WS object",
|
|
m_pObjPageControl,
|
|
m_pObjSyncControl));
|
|
if ( (m_pObjPageControl == 0) &&
|
|
(m_pObjSyncControl == 0))
|
|
{
|
|
TRACE_OUT(("No control objects - WE MIGHT BE FIRST IN CALL - get lock"));
|
|
|
|
//
|
|
// We may be the first user to register - request the lock on the Page
|
|
// Control Workset.
|
|
//
|
|
rc = wbLock(WB_LOCK_TYPE_PAGE_ORDER);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error from wbLock = %d", rc));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Set the new registration state
|
|
//
|
|
m_subState = STATE_REG_PENDING_LOCK;
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_LOCK"));
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
if (m_pObjSyncControl == 0)
|
|
{
|
|
TRACE_OUT(("Waiting for sync control"));
|
|
m_subState = STATE_REG_PENDING_SYNC_CONTROL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (m_pObjPageControl == 0)
|
|
{
|
|
TRACE_OUT(("Waiting for page control"));
|
|
m_subState = STATE_REG_PENDING_PAGE_CONTROL;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Complete registration
|
|
//
|
|
TRACE_OUT(("Page Control and Sync Control objects both there."));
|
|
TRACE_OUT(("Registration can be completed"));
|
|
|
|
wbOnControlWorksetsReady();
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnControlWorksetOpenCon);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageWorksetOpen
|
|
//
|
|
// Purpose: Open a page workset
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageWorksetOpen
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT localOrExternal
|
|
)
|
|
{
|
|
UINT result;
|
|
PWB_PAGE_STATE pPageState;
|
|
|
|
DebugEntry(wbPageWorksetOpen);
|
|
|
|
//
|
|
// Get the page state
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
ASSERT((pPageState->state == PAGE_NOT_IN_USE));
|
|
ASSERT((pPageState->subState == PAGE_STATE_EMPTY));
|
|
|
|
//
|
|
// Open the workset. We allow ObMan to choose the priority, this means
|
|
// that ObMan uses a variable priority scheme allowing small objects to
|
|
// overtake large ones.
|
|
//
|
|
result = OM_WorksetOpenPReq(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
OM_OBMAN_CHOOSES_PRIORITY,
|
|
FALSE,
|
|
&(pPageState->worksetOpenCorrelator));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("WorksetOpen failed = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the page state
|
|
//
|
|
if (localOrExternal == OPEN_LOCAL)
|
|
{
|
|
pPageState->subState = PAGE_STATE_LOCAL_OPEN_CONFIRM;
|
|
TRACE_OUT(("Moved page %d state to PAGE_STATE_PENDING_OPEN_CONFIRM",
|
|
(UINT) hPage));
|
|
}
|
|
else
|
|
{
|
|
pPageState->subState = PAGE_STATE_EXTERNAL_OPEN_CONFIRM;
|
|
TRACE_OUT(("Moved page %d state to PAGE_STATE_PENDING_OPEN_CONFIRM",
|
|
(UINT) hPage));
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPageWorksetOpen, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageWorksetOpenCon
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_OPEN_CON events for page worksets
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnPageWorksetOpenCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
OM_WORKSET_ID eventId;
|
|
PWB_PAGE_STATE pPageState;
|
|
WB_PAGE_HANDLE hPage;
|
|
UINT oldState;
|
|
|
|
DebugEntry(wbOnPageWorksetOpenCon);
|
|
|
|
//
|
|
// Get the page state pointer
|
|
//
|
|
eventId = pEvent16->worksetID;
|
|
hPage = (WB_PAGE_HANDLE)eventId;
|
|
pPageState = GetPageState(hPage);
|
|
|
|
//
|
|
// Check the message correlator
|
|
//
|
|
if (pEvent32->correlator != pPageState->worksetOpenCorrelator)
|
|
{
|
|
TRACE_OUT(("Correlators do not match - quitting"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that we have processed this event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Check the return code in the open
|
|
//
|
|
if (pEvent32->result != 0)
|
|
{
|
|
ERROR_OUT(("Error reported on page workset open = %d",
|
|
pEvent32->result));
|
|
|
|
pPageState->subState = PAGE_STATE_EMPTY;
|
|
TRACE_OUT(("Moved page %d substate to PAGE_STATE_EMPTY",
|
|
(UINT)hPage));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the page state to indicate that the page is now ready for use
|
|
//
|
|
oldState = pPageState->subState;
|
|
pPageState->subState = PAGE_STATE_READY;
|
|
TRACE_OUT(("Moved page %d to substate to PAGE_STATE_READY",
|
|
(UINT)hPage));
|
|
|
|
switch (oldState)
|
|
{
|
|
case PAGE_STATE_LOCAL_OPEN_CONFIRM:
|
|
//
|
|
// This workset was opened locally, therefore it is being opened as
|
|
// part of the workset cache. Nothing more to do.
|
|
//
|
|
break;
|
|
|
|
case PAGE_STATE_EXTERNAL_OPEN_CONFIRM:
|
|
//
|
|
// This workset was opened as a result of external updates to the
|
|
// Page Control object. We therefore need to add the page to the
|
|
// page list now that the workset is open. We no longer know where
|
|
// the page is to be added - so call the main Page Control update
|
|
// routine again to get all the information.
|
|
//
|
|
wbProcessPageControlChanges();
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad page state %d", pPageState->subState));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Increment the number of pages in ready state. This count is never
|
|
// decremented - once a workset is open it stays open.
|
|
//
|
|
m_countReadyPages += 1;
|
|
|
|
//
|
|
// If we are in registration and are waiting for the cache of ready
|
|
// pages, we must complete registration now.
|
|
//
|
|
if ( (m_state == STATE_REGISTERING) &&
|
|
(m_subState == STATE_REG_PENDING_READY_PAGES) )
|
|
{
|
|
//
|
|
// If there are enough pages in the cache
|
|
//
|
|
if (wbCheckReadyPages())
|
|
{
|
|
//
|
|
// We have enough ready pages - complete registration
|
|
//
|
|
wbCompleteRegistration();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// There are not yet enough pages in the cache. CheckReadyPages will
|
|
// have made a new workset open request, so we will receive another
|
|
// workset open confirm soon.
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnPageWorksetOpenCon, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWorksetLockCon
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_LOCK_CON events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWorksetLockCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
UINT rc;
|
|
|
|
DebugEntry(wbOnWorksetLockCon);
|
|
|
|
//
|
|
// Check the message correlator
|
|
//
|
|
if (pEvent32->correlator != m_lockCorrelator)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The message is for us - set the result to "processed"
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Check that the event is for the Page Control Workset (this is the
|
|
// only expected workset).
|
|
//
|
|
if (pEvent16->worksetID != PAGE_CONTROL_WORKSET)
|
|
{
|
|
ERROR_OUT(("Unexpected workset in LockCon = %d",
|
|
pEvent16->worksetID));
|
|
}
|
|
|
|
//
|
|
// Process according to the current lock state
|
|
//
|
|
switch (m_lockState)
|
|
{
|
|
//
|
|
// We were waiting for lock confirmation
|
|
//
|
|
case LOCK_STATE_PENDING_LOCK:
|
|
//
|
|
// Check the return code in the event
|
|
//
|
|
if (pEvent32->result != 0)
|
|
{
|
|
TRACE_OUT(("Posting WBP_EVENT_LOCK_FAILED, rc %d",
|
|
pEvent32->result));
|
|
WBP_PostEvent(0, // No delay
|
|
WBP_EVENT_LOCK_FAILED, // Lock request failed
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// The lock failed - update the state. This means that
|
|
// another user has acquired the lock. We expect to get a
|
|
// lock object add indication soon.
|
|
//
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
TRACE_OUT(("Lock request failed - lock state is now EMPTY"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Write the lock details to the Page Control Workset
|
|
//
|
|
rc = wbWriteLock();
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Unable to write lock details = %d", rc));
|
|
|
|
//
|
|
// Tidy up by unlocking the Page Control Workset
|
|
//
|
|
OM_WorksetUnlock(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET);
|
|
|
|
//
|
|
// Tell the client of the failure
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_LOCK_FAILED"));
|
|
WBP_PostEvent(0, // No delay
|
|
WBP_EVENT_LOCK_FAILED, // Lock request failed
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Update the lock state
|
|
//
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Once we get here the write of the lock object above will
|
|
// trigger an object add event that completes the lock
|
|
// processing.
|
|
//
|
|
m_lockState = LOCK_STATE_PENDING_ADD;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_PENDING_ADD"));
|
|
break;
|
|
|
|
//
|
|
// The application has cancelled the lock request before it has had
|
|
// time to complete - tidy up.
|
|
//
|
|
case LOCK_STATE_CANCEL_LOCK:
|
|
TRACE_OUT(("LOCK_STATE_CANCEL_LOCK"));
|
|
|
|
//
|
|
// If the request failed, just reset the state.
|
|
//
|
|
//
|
|
// The lock was cancelled - unlock the workset if necessary,
|
|
// and notify the front-end of the unlock.
|
|
//
|
|
if (pEvent32->result == 0)
|
|
{
|
|
//
|
|
// We have locked the workset successfully, but in the
|
|
// meantime the front-end has cancelled the lock, so unlock
|
|
// the workset now.
|
|
//
|
|
TRACE_OUT((
|
|
"Lock cancelled before workset locked, so unlock now"));
|
|
OM_WorksetUnlock(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET);
|
|
}
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
|
|
//
|
|
// Tell the app that we have cancelled the lock.
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_UNLOCKED"));
|
|
WBP_PostEvent(0,
|
|
WBP_EVENT_UNLOCKED,
|
|
0,
|
|
0);
|
|
break;
|
|
|
|
//
|
|
// Another has got in before us
|
|
//
|
|
case LOCK_STATE_LOCKED_OUT:
|
|
|
|
//
|
|
// We have received a lock confirmation and should have been
|
|
// expecting the lock. But we are locked out. This means that
|
|
// another user has got in just before us, acquired the lock
|
|
// and added the lock object. We have processed the add and
|
|
// changed the lock state accordingly. This lock confirmation
|
|
// will therefore normally be a failure. If by some fluke it
|
|
// isn't, then we treat it as a failure for safety.
|
|
//
|
|
if (pEvent32->result == 0)
|
|
{
|
|
ERROR_OUT(("Lock violation"));
|
|
|
|
//
|
|
// Tidy up by unlocking the Page Control Workset - leave
|
|
// the state as LOCKED_OUT; we'll clear it on receipt of
|
|
// the unlock (either local, or from the locking user).
|
|
//
|
|
OM_WorksetUnlock(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad lock state %d", m_lockState));
|
|
break;
|
|
} // Switch on lock state
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWorksetLockCon, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWorksetUnlockInd
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_UNLOCK_IND events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWorksetUnlockInd
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = TRUE;
|
|
|
|
DebugEntry(wbOnWorksetUnlockInd);
|
|
|
|
//
|
|
// We are only interested if the workset id is that of the Page Control
|
|
// Workset.
|
|
//
|
|
if (pEvent16->worksetID != PAGE_CONTROL_WORKSET)
|
|
{
|
|
TRACE_OUT(("Unexpected workset in unlock = %d", pEvent16->worksetID));
|
|
DC_QUIT;
|
|
}
|
|
|
|
switch (m_lockState)
|
|
{
|
|
//
|
|
// We had the lock and are waiting to unlock or another user had
|
|
// the lock and has now removed it.
|
|
//
|
|
case LOCK_STATE_LOCKED_OUT:
|
|
//
|
|
// We received the unlock of the workset before the removal of
|
|
// the lock object; we just ignore this, since the deletion of
|
|
// the lock object is our indication that the wb lock is removed.
|
|
//
|
|
TRACE_OUT(("Unlock of page control workset while locked out"));
|
|
break;
|
|
|
|
//
|
|
// We are unlocking after an error acquiring the lock or after a
|
|
// user has cancelled alock before we had time to complete it.
|
|
//
|
|
case LOCK_STATE_CANCEL_LOCK:
|
|
|
|
//
|
|
// An error occurred in getting the lock - the client has
|
|
// already been informed so we just record the state change.
|
|
//
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY"));
|
|
break;
|
|
|
|
//
|
|
// We are waiting for the lock - but have got an unlock instead.
|
|
// This could be from another user, or from previous aborted
|
|
// attempts by us to get the lock. We ignore the event and wait
|
|
// for our lock confirmation.
|
|
//
|
|
case LOCK_STATE_PENDING_LOCK:
|
|
TRACE_OUT((
|
|
"Got unlock indication while waiting for lock confirmation"));
|
|
break;
|
|
|
|
//
|
|
// We can get an unlock indication without ever having seen the
|
|
// lock object if the lock object was never added (failure at
|
|
// another user) or if ObMan has spoiled the add and delete.
|
|
//
|
|
case LOCK_STATE_EMPTY:
|
|
TRACE_OUT(("Unlock received in LOCK_STATE_EMPTY - ignoring"));
|
|
break;
|
|
|
|
//
|
|
// Unlock not expected in this state
|
|
//
|
|
default:
|
|
ERROR_OUT(("Bad lock state %d", m_lockState));
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWorksetUnlockInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnControlWorksetsReady
|
|
//
|
|
// Purpose: The control worksets have been opened and set up. Continue the
|
|
// registration process by updating the internal page order to
|
|
// ensure that in matches the external order.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnControlWorksetsReady(void)
|
|
{
|
|
DebugEntry(wbOnControlWorksetsReady);
|
|
|
|
//
|
|
// Read the Page Control object and compare its content to the internal
|
|
// Page Order.
|
|
//
|
|
wbProcessPageControlChanges();
|
|
|
|
//
|
|
// Update the state to show that we are waiting for the Page Order
|
|
// Updated event indicating that the internal page order now matches
|
|
// the external order.
|
|
//
|
|
m_subState = STATE_REG_PENDING_PAGE_ORDER;
|
|
TRACE_OUT(("Moved sub state to STATE_REG_PENDING_PAGE_ORDER"));
|
|
|
|
DebugExitVOID(wbOnControlWorksetsReady);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCompleteRegistration
|
|
//
|
|
// Purpose: Perform the final steps in registering a client. These are:
|
|
// post a WB_EVENT_REGISTERED event to the client; check if
|
|
// another user has a lock on the contents or page order, if so,
|
|
// post a WB_EVENT_CONTENTS_LOCKED or WB_EVENT_PAGE_ORDER_LOCKED
|
|
// to the client.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbCompleteRegistration(void)
|
|
{
|
|
DebugEntry(wbCompleteRegistration);
|
|
|
|
//
|
|
// Inform the client that we are fully registered
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_REGISTER_OK"));
|
|
WBP_PostEvent(0, // No delay
|
|
WBP_EVENT_JOIN_CALL_OK, // Fully registered
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Notify the client of the lock status
|
|
//
|
|
wbSendLockNotification();
|
|
|
|
//
|
|
// Record that we are now fully registered
|
|
//
|
|
m_state = STATE_IDLE;
|
|
m_subState = STATE_EMPTY;
|
|
TRACE_OUT(("Moved to state STATE_IDLE"));
|
|
|
|
DebugExitVOID(wbCompleteRegistration);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbLeaveCall
|
|
//
|
|
// Purpose: Remove a client from a call/workset group
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbLeaveCall(void)
|
|
{
|
|
DebugEntry(wbLeaveCall);
|
|
|
|
//
|
|
// If we have not got far enough to have entered a call - leave now
|
|
// (there is nothing to tidy up).
|
|
//
|
|
if (m_state < STATE_REGISTERING)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If we have the lock - delete the lock object (the workset will be
|
|
// unlocked by ObMan when we deregister).
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
TRACE_OUT(("Still got lock - deleting lock object, handle %d",
|
|
m_pObjLock));
|
|
if (OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjLock) != 0)
|
|
{
|
|
ERROR_OUT(("Error deleting lock object"));
|
|
}
|
|
|
|
//
|
|
// If all is well at this point the unlock process will be
|
|
// completed when the object delete ind is received.
|
|
//
|
|
m_lockState = LOCK_STATE_PENDING_DELETE;
|
|
TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE"));
|
|
}
|
|
|
|
//
|
|
// Fix up the sub state to indicate that all registration actions have
|
|
// been completed (to ensure that they are all undone).
|
|
//
|
|
if (m_state > STATE_REGISTERING)
|
|
{
|
|
m_subState = STATE_REG_END;
|
|
TRACE_OUT(("Moved to substate STATE_REG_END"));
|
|
}
|
|
|
|
//
|
|
// Delete the user object representing the local user from the User
|
|
// Information Workset (if it is present). Note that we are about to
|
|
// deregister from ObMan - this acts as automatic confirmation of the
|
|
// delete request so we do not need to wait for the
|
|
// OM_OBJECT_DELETE_IND event.
|
|
//
|
|
if (m_subState >= STATE_REG_USER_OBJECT_ADDED)
|
|
{
|
|
TRACE_OUT(("Deleting user object"));
|
|
if (OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
m_pObjLocal) != 0)
|
|
{
|
|
//
|
|
// Trace the error but do not quit - we expect everything to be
|
|
// tidied up when we deregister from ObMan.
|
|
//
|
|
ERROR_OUT(("Error deleting local user object"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have already registered with the Workset Group, deregister
|
|
// now. we have not yet received the confirmation, and get it later we
|
|
// will deregister immediately.
|
|
//
|
|
if (m_subState > STATE_REG_PENDING_WSGROUP_CON)
|
|
{
|
|
OM_WSGroupDeregister(m_pomClient, &(m_hWSGroup));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have not yet received the Workset Group Registration
|
|
// confirmation, change the value in the correlator field so that
|
|
// we recognize the fact that we have cancelled registration later.
|
|
//
|
|
m_wsgroupCorrelator--;
|
|
}
|
|
|
|
//
|
|
// Reset the handles of objects added during registration
|
|
//
|
|
TRACE_OUT(("Resetting client data"));
|
|
wbClientReset();
|
|
|
|
//
|
|
// Set the client state to the appropriate value
|
|
//
|
|
m_state = STATE_STARTED;
|
|
m_subState = STATE_STARTED_START;
|
|
TRACE_OUT(("Moved state to STATE_STARTED"));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbLeaveCall);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbContentsDelete
|
|
//
|
|
// Purpose: Remove all the current graphics and pages, leaving a single
|
|
// blank page.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbContentsDelete
|
|
(
|
|
UINT changedFlagAction
|
|
)
|
|
{
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
PWB_PAGE_STATE pPageState;
|
|
UINT index;
|
|
|
|
DebugEntry(wbContentsDelete);
|
|
|
|
//
|
|
// Just clear the first page in the list
|
|
//
|
|
wbPageClear(pPageOrder->pages[0], changedFlagAction);
|
|
|
|
//
|
|
// If there is only one page left in the list - we're done.
|
|
//
|
|
if (pPageOrder->countPages == 1)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// There is more than one page
|
|
//
|
|
|
|
//
|
|
// Mark all of the active pages (except the first) as "delete pending"
|
|
//
|
|
for (index = 1; index < pPageOrder->countPages; index++)
|
|
{
|
|
pPageState = GetPageState((pPageOrder->pages)[index]);
|
|
|
|
if ((pPageState->state == PAGE_IN_USE) &&
|
|
(pPageState->subState == PAGE_STATE_EMPTY))
|
|
{
|
|
pPageState->subState = PAGE_STATE_LOCAL_DELETE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the page control information. The replace event generated by
|
|
// the write will kick off the actual deletion of the pages marked.
|
|
//
|
|
wbWritePageControl(FALSE);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbContentsDelete);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbStartContentsLoad
|
|
//
|
|
// Purpose: Start the loading of a file (after the contents have been
|
|
// cleared).
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbStartContentsLoad(void)
|
|
{
|
|
DebugEntry(wbStartContentsLoad);
|
|
|
|
//
|
|
// Specify the first (and only) page handle as the page to load to
|
|
//
|
|
wbPageHandleFromNumber(1, &m_loadPageHandle);
|
|
|
|
//
|
|
// Update the load state to show that we are now loading
|
|
//
|
|
m_loadState = LOAD_STATE_LOADING;
|
|
TRACE_OUT(("Moved load state to LOAD_STATE_LOADING"));
|
|
|
|
//
|
|
// Load the first page - subsequent pages are chained from this first one
|
|
//
|
|
wbPageLoad();
|
|
|
|
DebugExitVOID(wbStartContentsLoad);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbLock
|
|
//
|
|
// Purpose: Request the lock for the Whiteboard contents or the page order
|
|
// generating one of the following events:
|
|
//
|
|
// WB_EVENT_CONTENTS_LOCKED
|
|
// WB_EVENT_CONTENTS_LOCK_FAILED.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbLock(WB_LOCK_TYPE lockType)
|
|
{
|
|
UINT result = 0;
|
|
OM_CORRELATOR correlator;
|
|
|
|
DebugEntry(wbLock);
|
|
|
|
//
|
|
// If we already have the lock we can merely change its status
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
TRACE_OUT(("Already got the lock"));
|
|
|
|
m_lockRequestType = lockType;
|
|
result = wbWriteLock();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Request the lock for the Page Control Workset
|
|
//
|
|
result = OM_WorksetLockReq(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&correlator);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_WorksetLockReq failed, result = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRACE_OUT(("Requested lock for the Page Control Workset"));
|
|
|
|
//
|
|
// Save the lock details
|
|
//
|
|
|
|
m_lockState = LOCK_STATE_PENDING_LOCK;
|
|
m_lockCorrelator = correlator;
|
|
m_lockRequestType = lockType;
|
|
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_PENDING_LOCK"));
|
|
TRACE_OUT(("Lock type requested = %d", lockType));
|
|
|
|
//
|
|
// We return now, further processing is done when the OM_WORKSET_LOCK_CON
|
|
// event is received.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbLock, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbUnlock
|
|
//
|
|
// Purpose: Unlock the Contents or Page Order.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbUnlock(void)
|
|
{
|
|
DebugEntry(wbUnlock);
|
|
|
|
//
|
|
// Check that we have the lock
|
|
//
|
|
if (m_lockState != LOCK_STATE_GOT_LOCK)
|
|
{
|
|
ERROR_OUT(("Local person doesn't have lock"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Delete the lock object
|
|
//
|
|
TRACE_OUT(("Delete Lock handle %x", m_pObjLock));
|
|
if (OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjLock) != 0)
|
|
{
|
|
ERROR_OUT(("Could not delete lock object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If all is well at this point the unlock process will be completed when
|
|
// the object delete ind is received.
|
|
//
|
|
m_lockState = LOCK_STATE_PENDING_DELETE;
|
|
TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE"));
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbUnlock);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbObjectSave
|
|
//
|
|
// Purpose: Save a structure to file
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbObjectSave
|
|
(
|
|
HANDLE hFile,
|
|
LPBYTE pData,
|
|
UINT length
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
ULONG cbSizeWritten;
|
|
|
|
DebugEntry(wbObjectSave);
|
|
|
|
//
|
|
// Save the length
|
|
//
|
|
if (! WriteFile(hFile, (void *) &length, sizeof(length), &cbSizeWritten, NULL))
|
|
{
|
|
result = WB_RC_WRITE_FAILED;
|
|
ERROR_OUT(("Error writing length to file, win32 err=%d", GetLastError()));
|
|
DC_QUIT;
|
|
}
|
|
ASSERT(cbSizeWritten == sizeof(length));
|
|
|
|
//
|
|
// Save the object data
|
|
//
|
|
if (! WriteFile(hFile, pData, length, &cbSizeWritten, NULL))
|
|
{
|
|
result = WB_RC_WRITE_FAILED;
|
|
ERROR_OUT(("Error writing data to file, win32 err=%d", GetLastError()));
|
|
DC_QUIT;
|
|
}
|
|
ASSERT(cbSizeWritten == length);
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbObjectSave, result);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageSave
|
|
//
|
|
// Purpose: Save the contents of a single page to file.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageSave
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
UINT rc;
|
|
OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage;
|
|
POM_OBJECT pObj;
|
|
POM_OBJECTDATA pData;
|
|
WB_END_OF_PAGE endOfPage;
|
|
|
|
DebugEntry(wbPageSave);
|
|
|
|
//
|
|
// Get the first object
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
0,
|
|
&pObj,
|
|
FIRST);
|
|
if (result == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
// This can happen on an empty page, not an error
|
|
TRACE_OUT(("No objects left, quitting with good return"));
|
|
result = 0;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error getting first object in page"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Loop through the objects
|
|
//
|
|
for( ; ; )
|
|
{
|
|
//
|
|
// Get a pointer to the object
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error reading object = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save the object data
|
|
//
|
|
rc = wbObjectSave(hFile,
|
|
(LPBYTE) pData->data,
|
|
pData->length);
|
|
|
|
//
|
|
// The return code is tested after we have released the object because
|
|
// we must always do the release.
|
|
//
|
|
|
|
//
|
|
// Release the object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj,
|
|
&pData);
|
|
|
|
//
|
|
// Now test the write return code
|
|
//
|
|
if (rc != 0)
|
|
{
|
|
result = rc;
|
|
ERROR_OUT(("Error writing object data = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the next object
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj,
|
|
&pObj,
|
|
AFTER);
|
|
if (result == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
TRACE_OUT(("No objects left, quitting with good return"));
|
|
result = 0;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// If we have successfully written the page contents, we write an end-of-
|
|
// page marker to the file.
|
|
//
|
|
if (result == 0)
|
|
{
|
|
//
|
|
// Set the end of page object details
|
|
//
|
|
ZeroMemory(&endOfPage, sizeof(endOfPage));
|
|
|
|
endOfPage.length = sizeof(endOfPage);
|
|
endOfPage.type = TYPE_END_OF_PAGE;
|
|
|
|
//
|
|
// Write the end-of-page object
|
|
//
|
|
result = wbObjectSave(hFile,
|
|
(LPBYTE) &endOfPage,
|
|
sizeof(endOfPage));
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error writing end-of-page = %d", result));
|
|
}
|
|
}
|
|
|
|
DebugExitDWORD(wbPageSave, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageLoad
|
|
//
|
|
// Purpose: Load the contents of a single page from file.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbPageLoad(void)
|
|
{
|
|
UINT result = 0;
|
|
UINT type;
|
|
POM_OBJECT pObj;
|
|
POM_OBJECTDATA pData = NULL;
|
|
PWB_GRAPHIC pGraphic = NULL;
|
|
WB_PAGE_HANDLE hPage = m_loadPageHandle;
|
|
WB_PAGE_HANDLE hNewPage;
|
|
UINT postDelay = 0;
|
|
|
|
DebugEntry(wbPageLoad);
|
|
TRACE_OUT(("Entered wbPageLoad for page %d", (UINT) hPage));
|
|
|
|
//
|
|
// Check the load state - if we're not loading, then quit (can happen if
|
|
// the load is cancelled).
|
|
//
|
|
if (m_loadState == LOAD_STATE_EMPTY)
|
|
{
|
|
TRACE_OUT(("Load has been cancelled - abandoning page load"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that we have a full complement of ready pages before starting
|
|
// the load.
|
|
//
|
|
if (!wbCheckReadyPages())
|
|
{
|
|
//
|
|
// There are not enough pages worksets ready to be used. We exit now
|
|
// to allow the page to be made ready before we continue. We set up a
|
|
// delay on the message that will be used to restart the process to
|
|
// allow the worksets to be opened before we get back in here.
|
|
//
|
|
postDelay = 200;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If we are waiting to add a new page, get the handle of the page we
|
|
// expect to add next here. (We have to do this as ObMan requires that
|
|
// we allocate memory for the object in the correct workset, but we do
|
|
// not want to actually add the page here because we may not need it.)
|
|
//
|
|
if (m_loadState == LOAD_STATE_PENDING_NEW_PAGE)
|
|
{
|
|
hNewPage = wbGetReadyPageHandle();
|
|
|
|
//
|
|
// If we cannot get a ready page - we must have run out of pages (we
|
|
// have already done a check on the availability of ready pages above).
|
|
// If we cannot get a new page we continue using the old.
|
|
//
|
|
if (hNewPage != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
hPage = hNewPage;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the next object
|
|
//
|
|
result = wbObjectLoad(m_hLoadFile,
|
|
(OM_WORKSET_ID)hPage,
|
|
&pGraphic);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error reading object = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData = ObjectDataPtrFromGraphic(pGraphic);
|
|
type = pGraphic->type;
|
|
|
|
//
|
|
// Process the object according to type
|
|
//
|
|
|
|
//
|
|
// End of file marker
|
|
//
|
|
if (type == TYPE_END_OF_FILE)
|
|
{
|
|
//
|
|
// Let the Front End know that the load has completed
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_LOAD_COMPLETE"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_LOAD_COMPLETE, // Load completed
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Leave now - the file will be closed below
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// It is not an end-of file object. So it must be either an end-of page
|
|
// or a graphic object. In either case we may already have flagged the
|
|
// need to add a new page.
|
|
//
|
|
|
|
//
|
|
// Add a new page (if necessary)
|
|
//
|
|
if (m_loadState == LOAD_STATE_PENDING_NEW_PAGE)
|
|
{
|
|
//
|
|
// If we could not get a new page handle above leave with an error
|
|
//
|
|
if (hPage == m_loadPageHandle)
|
|
{
|
|
ERROR_OUT(("Run out of pages for load"));
|
|
result = WB_RC_TOO_MANY_PAGES;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Add a new page after the current page. The new page handle is saved
|
|
// in the client details.
|
|
//
|
|
result = wbPageAdd(m_loadPageHandle,
|
|
PAGE_AFTER,
|
|
&(m_loadPageHandle),
|
|
DONT_RESET_CHANGED_FLAG);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Failed to add page"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that we got the page handle we expected
|
|
//
|
|
ASSERT((hPage == m_loadPageHandle));
|
|
|
|
//
|
|
// Show that we are no longer waiting for a new page
|
|
//
|
|
m_loadState = LOAD_STATE_LOADING;
|
|
}
|
|
|
|
//
|
|
// End of page marker
|
|
//
|
|
if (type == TYPE_END_OF_PAGE)
|
|
{
|
|
TRACE_OUT(("End of page object"));
|
|
|
|
//
|
|
// Discard the object
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
&pData);
|
|
pData = NULL;
|
|
|
|
//
|
|
// Set the load state to "pending new page" and leave the routine
|
|
// immediately. The process continues when we return to this routine.
|
|
//
|
|
m_loadState = LOAD_STATE_PENDING_NEW_PAGE;
|
|
|
|
//
|
|
// Exit (we post ourselves a message below to get us back into this
|
|
// routine later).
|
|
//
|
|
postDelay = 100;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The object is a standard graphic
|
|
//
|
|
TRACE_OUT(("Graphic object"));
|
|
|
|
//
|
|
// Add the object to the page
|
|
//
|
|
result = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
&pData,
|
|
sizeof(WB_GRAPHIC),
|
|
&pObj,
|
|
LAST);
|
|
if (result != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that we have finished with the object
|
|
//
|
|
pGraphic = NULL;
|
|
pData = NULL;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// If we still have the object - discard it
|
|
//
|
|
if (pData != NULL)
|
|
{
|
|
TRACE_OUT(("Discarding object"));
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage,
|
|
&pData);
|
|
}
|
|
|
|
//
|
|
// If an error occurred or we have reached the end-of-file - close the
|
|
// file.
|
|
//
|
|
if ((result != 0) || (type == TYPE_END_OF_FILE))
|
|
{
|
|
CloseHandle(m_hLoadFile);
|
|
m_hLoadFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// If the final result is an error - post an error message to ourselves
|
|
//
|
|
if (result != 0)
|
|
{
|
|
TRACE_OUT(("Posting WBP_EVENT_LOAD_FAILED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_LOAD_FAILED, // Load the next object
|
|
0, // No parameters
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Record that we are no longer in the load process
|
|
//
|
|
m_loadState = LOAD_STATE_EMPTY;
|
|
TRACE_OUT(("Moved load state to LOAD_STATE_EMPTY"));
|
|
}
|
|
|
|
//
|
|
// send a message to load the next page, unless the load has been
|
|
// cancelled
|
|
//
|
|
if (m_loadState != LOAD_STATE_EMPTY)
|
|
{
|
|
//
|
|
// We have not reached the end-of-file and there has been no error.
|
|
// Post a message to ourselves to continue the load process.
|
|
//
|
|
TRACE_OUT(("Posting WBPI_EVENT_LOAD_NEXT"));
|
|
WBP_PostEvent(postDelay, // With delay
|
|
WBPI_EVENT_LOAD_NEXT, // Load the next object
|
|
0, // No parameters
|
|
0);
|
|
}
|
|
|
|
DebugExitVOID(wbPageLoad);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbObjectLoad
|
|
//
|
|
// Purpose: Load a single object from file.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbObjectLoad
|
|
(
|
|
HANDLE hFile,
|
|
WB_PAGE_HANDLE hPage,
|
|
PPWB_GRAPHIC ppGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage;
|
|
UINT length;
|
|
ULONG cbSizeRead;
|
|
POM_OBJECTDATA pData = NULL;
|
|
PWB_GRAPHIC pGraphic = NULL;
|
|
|
|
DebugEntry(wbObjectLoad);
|
|
|
|
TRACE_OUT(("Entered wbObjectLoad for page %d", (UINT) hPage));
|
|
|
|
//
|
|
// Read the next object's length
|
|
//
|
|
if ( (! ReadFile(hFile, (void *) &length, sizeof(length), &cbSizeRead, NULL)) ||
|
|
(cbSizeRead != sizeof(length)) ||
|
|
(length > OM_MAX_OBJECT_SIZE) ||
|
|
(length == 0) )
|
|
{
|
|
//
|
|
// Make sure we return a sensible error.
|
|
//
|
|
ERROR_OUT(("reading object length, win32 err=%d, length=%d", GetLastError(), length));
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for the object
|
|
//
|
|
result = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
length,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error allocating object = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData->length = length;
|
|
pGraphic = GraphicPtrFromObjectData(pData);
|
|
|
|
//
|
|
// Read the object into memory
|
|
//
|
|
if ( (! ReadFile(hFile, (void *) pGraphic, length, &cbSizeRead, NULL)) ||
|
|
(cbSizeRead != length))
|
|
{
|
|
//
|
|
// Make sure we return a sensible error.
|
|
//
|
|
ERROR_OUT((
|
|
"Reading object from file: win32 err=%d, asked for %d got %d bytes",
|
|
GetLastError(),
|
|
length,
|
|
cbSizeRead));
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the object type
|
|
//
|
|
switch (pGraphic->type)
|
|
{
|
|
//
|
|
// Standard type, end-of-page or end-of-file
|
|
//
|
|
case TYPE_END_OF_PAGE:
|
|
case TYPE_END_OF_FILE:
|
|
case TYPE_GRAPHIC_FREEHAND:
|
|
case TYPE_GRAPHIC_LINE:
|
|
case TYPE_GRAPHIC_RECTANGLE:
|
|
case TYPE_GRAPHIC_FILLED_RECTANGLE:
|
|
case TYPE_GRAPHIC_ELLIPSE:
|
|
case TYPE_GRAPHIC_FILLED_ELLIPSE:
|
|
case TYPE_GRAPHIC_TEXT:
|
|
case TYPE_GRAPHIC_DIB:
|
|
break;
|
|
|
|
//
|
|
// Unrecognized object type - probably wrong version
|
|
//
|
|
default:
|
|
result = WB_RC_BAD_FILE_FORMAT;
|
|
DC_QUIT;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// For graphic objects, set the flag in the object header showing that it
|
|
// was loaded from file. Add our user ID so we know where it came from.
|
|
//
|
|
if ( (pGraphic->type != TYPE_END_OF_FILE) &&
|
|
(pGraphic->type != TYPE_END_OF_PAGE))
|
|
{
|
|
pGraphic->loadedFromFile = TRUE;
|
|
pGraphic->loadingClientID = m_clientNetID;
|
|
}
|
|
|
|
*ppGraphic = pGraphic;
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// If an error has occurred - discard the object (if we have it)
|
|
//
|
|
if ((result != 0) && (pData != NULL))
|
|
{
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
&pData);
|
|
}
|
|
|
|
DebugExitDWORD(wbObjectLoad, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageHandleFromNumber
|
|
//
|
|
// Purpose: Return the handle of a page specified by page number
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageHandleFromNumber
|
|
(
|
|
UINT pageNumber,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
WB_PAGE_HANDLE hPage;
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
|
|
DebugEntry(wbPageHandleFromNumber);
|
|
|
|
//
|
|
// Validate the requested page number
|
|
//
|
|
if ((pageNumber < 1)|| (pageNumber > WB_MAX_PAGES))
|
|
{
|
|
result = WB_RC_BAD_PAGE_NUMBER;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (pageNumber > pPageOrder->countPages)
|
|
{
|
|
result = WB_RC_NO_SUCH_PAGE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the page handle
|
|
//
|
|
hPage = (pPageOrder->pages)[pageNumber - 1];
|
|
|
|
//
|
|
// Check that this page is in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
ERROR_OUT(("Page list is bad"));
|
|
}
|
|
|
|
//
|
|
// Return the page handle
|
|
//
|
|
*phPage = hPage;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPageHandleFromNumber, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageClear
|
|
//
|
|
// Purpose: Clear the specified page of all graphic objects
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageClear
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT changedFlagAction
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
|
|
DebugEntry(wbPageClear);
|
|
|
|
//
|
|
// Show that the contents have changed, if required.
|
|
//
|
|
if (changedFlagAction == RESET_CHANGED_FLAG)
|
|
{
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
}
|
|
|
|
//
|
|
// Request that the page be cleared
|
|
//
|
|
result = OM_WorksetClear(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage);
|
|
|
|
|
|
DebugExitDWORD(wbPageClear, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageClearConfirm
|
|
//
|
|
// Purpose: Complete the clearing of a page
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbPageClearConfirm(WB_PAGE_HANDLE hPage)
|
|
{
|
|
DebugEntry(wbPageClearConfirm);
|
|
|
|
//
|
|
// Request that the page be cleared
|
|
//
|
|
OM_WorksetClearConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
(OM_WORKSET_ID)hPage);
|
|
|
|
|
|
//
|
|
// Check the load state to see whether we are waiting to load the
|
|
// contents
|
|
//
|
|
if (m_loadState == LOAD_STATE_PENDING_CLEAR)
|
|
{
|
|
//
|
|
// We are waiting to load. If there is only one page available (ie the
|
|
// one that has just been cleared) we are ready to load, otherwise we
|
|
// wait for the page deletes to happen.
|
|
//
|
|
if ((m_pageOrder).countPages == 1)
|
|
{
|
|
//
|
|
// Start the load proper
|
|
//
|
|
wbStartContentsLoad();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Move the load state to show that we are waiting for all the pages
|
|
// to be deleted.
|
|
//
|
|
m_loadState = LOAD_STATE_PENDING_DELETE;
|
|
TRACE_OUT(("Moved load state to LOAD_STATE_PENDING_DELETE"));
|
|
}
|
|
}
|
|
|
|
DebugExitVOID(wbPageClearConfirm);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCheckReadyPages
|
|
//
|
|
// Purpose: Check that we have enough worksets open for the local user to
|
|
// use immediately (during page adds).
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
BOOL WbClient::wbCheckReadyPages(void)
|
|
{
|
|
BOOL bResult = TRUE;
|
|
WB_PAGE_HANDLE hNewPage;
|
|
UINT countPages = m_pageOrder.countPages;
|
|
UINT countReadyPages = m_countReadyPages;
|
|
|
|
//
|
|
// If we have opened all the worksets
|
|
//
|
|
if (countReadyPages == WB_MAX_PAGES)
|
|
{
|
|
//
|
|
// Quit there are no more worksets that we can open
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If the number of pages in use is getting close to the number of ready
|
|
// pages.
|
|
//
|
|
if ( (countReadyPages >= PREINITIALIZE_PAGES)
|
|
&& (countPages <= (countReadyPages - PREINITIALIZE_PAGES)))
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If the number of pages ready is less than the required cache size,
|
|
// open another one.
|
|
//
|
|
hNewPage = wbGetEmptyPageHandle();
|
|
if (hNewPage != WB_PAGE_HANDLE_NULL)
|
|
{
|
|
//
|
|
// Open the workset associated with the page
|
|
//
|
|
wbPageWorksetOpen(hNewPage, OPEN_LOCAL);
|
|
}
|
|
|
|
bResult = FALSE;
|
|
|
|
DC_EXIT_POINT:
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageAdd
|
|
//
|
|
// Purpose: Add a new (blank) page in a specified position
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageAdd
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
UINT where,
|
|
PWB_PAGE_HANDLE phPage,
|
|
UINT changedFlagAction
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
WB_PAGE_HANDLE hNewPage;
|
|
|
|
DebugEntry(wbPageAdd);
|
|
|
|
//
|
|
// Check that there are not too many pages already
|
|
//
|
|
if (m_pageOrder.countPages == WB_MAX_PAGES)
|
|
{
|
|
result = WB_RC_TOO_MANY_PAGES;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the specified reference page
|
|
//
|
|
ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Get a handle for the new page
|
|
//
|
|
hNewPage = wbGetReadyPageHandle();
|
|
|
|
//
|
|
// If there are no handles ready we attempt to create one and return a
|
|
// busy indication.
|
|
//
|
|
if (hNewPage == WB_PAGE_HANDLE_NULL)
|
|
{
|
|
result = WB_RC_BUSY;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Make the internal update immediately - this allows the client to
|
|
// reference the new page as soon as this function has returned.
|
|
//
|
|
wbPagesPageAdd(hRefPage, hNewPage, where);
|
|
|
|
//
|
|
// Update the Page Control Object
|
|
//
|
|
result = wbWritePageControl(FALSE);
|
|
if (result != 0)
|
|
{
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that the contents have changed (if required).
|
|
//
|
|
if (changedFlagAction == RESET_CHANGED_FLAG)
|
|
{
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
}
|
|
|
|
//
|
|
// Return the handle of the new page
|
|
//
|
|
*phPage = hNewPage;
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// If we successfully added the page, or could not get a spare page
|
|
// handle, attempt to create a spare one for next time.
|
|
//
|
|
if ((result == 0) || (result == WB_RC_BUSY))
|
|
{
|
|
wbCheckReadyPages();
|
|
}
|
|
|
|
DebugExitDWORD(wbPageAdd, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageMove
|
|
//
|
|
// Purpose: Move a page relative to another page
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageMove
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
WB_PAGE_HANDLE hPage,
|
|
UINT where
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
|
|
DebugEntry(wbPageMove);
|
|
|
|
//
|
|
// Extract the page to be moved
|
|
//
|
|
wbPageOrderPageDelete(pPageOrder, hPage);
|
|
|
|
//
|
|
// Add it back at its new position
|
|
//
|
|
wbPageOrderPageAdd(pPageOrder, hRefPage, hPage, where);
|
|
|
|
//
|
|
// Update the page control object
|
|
//
|
|
result = wbWritePageControl(FALSE);
|
|
if (result != 0)
|
|
{
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that the contents have changed
|
|
//
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPageMove, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPageHandle
|
|
//
|
|
// Purpose: Return a page handle. The page for which the handle is
|
|
// required can be specified relative to another page or as the
|
|
// first/last page.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPageHandle
|
|
(
|
|
WB_PAGE_HANDLE hRefPage,
|
|
UINT where,
|
|
PWB_PAGE_HANDLE phPage
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
UINT pageNumber;
|
|
PWB_PAGE_ORDER pPageOrder = &(m_pageOrder);
|
|
POM_WORKSET_ID pPage = pPageOrder->pages;
|
|
WB_PAGE_HANDLE hPage;
|
|
|
|
DebugEntry(wbPageHandle);
|
|
|
|
//
|
|
// Check the relative position
|
|
//
|
|
switch (where)
|
|
{
|
|
case PAGE_FIRST:
|
|
hPage = pPage[0];
|
|
break;
|
|
|
|
case PAGE_LAST:
|
|
hPage = pPage[pPageOrder->countPages - 1];
|
|
break;
|
|
|
|
case PAGE_AFTER:
|
|
case PAGE_BEFORE:
|
|
//
|
|
// Validate the specified reference page
|
|
//
|
|
ASSERT(GetPageState(hRefPage)->state == PAGE_IN_USE);
|
|
|
|
//
|
|
// Get the page number of the reference page
|
|
//
|
|
pageNumber = wbPageOrderPageNumber(pPageOrder, hRefPage);
|
|
TRACE_OUT(("Reference page number is %d", pageNumber));
|
|
|
|
//
|
|
// Get the page number of the required page
|
|
//
|
|
pageNumber = (UINT)(pageNumber + ((where == PAGE_AFTER) ? 1 : -1));
|
|
TRACE_OUT(("New page number is %d", pageNumber));
|
|
|
|
//
|
|
// Check that the new page is valid
|
|
//
|
|
TRACE_OUT(("Number of pages is %d", pPageOrder->countPages));
|
|
if ( (pageNumber < 1)
|
|
|| (pageNumber > pPageOrder->countPages))
|
|
{
|
|
TRACE_OUT(("Returning WB_RC_NO_SUCH_PAGE"));
|
|
result = WB_RC_NO_SUCH_PAGE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the handle of the page
|
|
//
|
|
hPage = pPage[pageNumber - 1];
|
|
TRACE_OUT(("Returning handle %d", (UINT) hPage));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return the page handle
|
|
//
|
|
*phPage = hPage;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPageHandle, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGraphicSelectPrevious
|
|
//
|
|
// Purpose: Return the next graphic object in the specified page whose
|
|
// bounding rectangle contains the specified point. The function
|
|
// starts with the graphic whose handle is given as parameter and
|
|
// will return this graphic if it contains the point.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbGraphicSelectPrevious
|
|
(
|
|
WB_PAGE_HANDLE hPage,
|
|
LPPOINT pPoint,
|
|
WB_GRAPHIC_HANDLE hGraphic,
|
|
PWB_GRAPHIC_HANDLE phGraphic
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
OM_WORKSET_ID worksetID = (OM_WORKSET_ID)hPage;
|
|
PWB_GRAPHIC pGraphic;
|
|
POM_OBJECTDATA pData;
|
|
RECT rect;
|
|
|
|
DebugEntry(wbGraphicSelectPrevious);
|
|
|
|
*phGraphic = (WB_GRAPHIC_HANDLE) NULL;
|
|
|
|
//
|
|
// Loop back through the objects starting at the reference point
|
|
//
|
|
do
|
|
{
|
|
//
|
|
// Get the object from ObMan
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
hGraphic,
|
|
&pData);
|
|
|
|
//
|
|
// Leave the loop if error on read - we do not need to do the release
|
|
//
|
|
if (result != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
pGraphic = GraphicPtrFromObjectData(pData);
|
|
|
|
//
|
|
// Extract the bounding rectangle of the object
|
|
//
|
|
RECT_FROM_TSHR_RECT16(&rect, pGraphic->rectBounds);
|
|
|
|
//
|
|
// Release the object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
hGraphic,
|
|
&pData);
|
|
|
|
//
|
|
// Check whether the point lies in bounds
|
|
//
|
|
if (PtInRect(&rect, *pPoint))
|
|
{
|
|
//
|
|
// Set the result handle
|
|
//
|
|
TRACE_OUT(("Returning graphic handle"));
|
|
*phGraphic = hGraphic;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the next object to test
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
hGraphic,
|
|
&hGraphic,
|
|
BEFORE);
|
|
}
|
|
while (result == 0);
|
|
|
|
//
|
|
// Correct the return code (if necessary)
|
|
//
|
|
if (result == OM_RC_NO_SUCH_OBJECT)
|
|
{
|
|
TRACE_OUT(("Returning WB_RC_NO_SUCH_GRAPHIC"));
|
|
result = WB_RC_NO_SUCH_GRAPHIC;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbGraphicSelectPrevious, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCoreExitHandler
|
|
//
|
|
// Purpose: Exit handler for the Whiteboard Core. This handler is
|
|
// registered with the Utilities by the WBP_Start call. It is
|
|
// deregistered by the client deregistration process, so it is
|
|
// only called when an abnormal termination occurs.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void CALLBACK wbCoreExitHandler(LPVOID clientData)
|
|
{
|
|
WbClient* pwbClient = (WbClient *)clientData;
|
|
|
|
pwbClient->wbExitHandler();
|
|
}
|
|
|
|
|
|
void WbClient::wbExitHandler(void)
|
|
{
|
|
DebugEntry(wbExitHandler);
|
|
|
|
//
|
|
// Leave the current call if there is one, removing any locks etc.
|
|
//
|
|
wbLeaveCall();
|
|
|
|
//
|
|
// Dereg from call manager
|
|
//
|
|
if (m_pcmClient != NULL)
|
|
{
|
|
CMS_Deregister(&(m_pcmClient));
|
|
}
|
|
|
|
//
|
|
// Dereg exit handler
|
|
//
|
|
if (m_subState >= STATE_START_REGISTERED_EXIT)
|
|
{
|
|
UT_DeregisterExit(m_putTask, wbCoreExitHandler, this);
|
|
}
|
|
|
|
//
|
|
// Dereg obman
|
|
//
|
|
if (m_subState >= STATE_START_REGISTERED_OM)
|
|
{
|
|
OM_Deregister(&m_pomClient);
|
|
}
|
|
|
|
//
|
|
// Dereg event handler
|
|
//
|
|
if (m_subState >= STATE_START_REGISTERED_EVENT)
|
|
{
|
|
UT_DeregisterEvent(m_putTask, wbCoreEventHandler, this);
|
|
}
|
|
|
|
//
|
|
// delete ourself!
|
|
//
|
|
delete this;
|
|
|
|
DebugExitVOID(wbExitHandler);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCoreEventHandler
|
|
//
|
|
// Purpose: Event handler for the Whiteboard Core. This handler is
|
|
// registered with the Utilities by the WBP_Start call.
|
|
//
|
|
// Params: clientData - pointer to the data stored for a client
|
|
// event - event identifier
|
|
// param1 - word event parameter (content depends on event)
|
|
// param2 - long event parameter (content depends on event)
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL CALLBACK wbCoreEventHandler
|
|
(
|
|
LPVOID clientData,
|
|
UINT event,
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
WbClient* pwbClient = (WbClient *)clientData;
|
|
|
|
return(pwbClient->wbEventHandler(event, param1, param2));
|
|
}
|
|
|
|
|
|
BOOL WbClient::wbEventHandler
|
|
(
|
|
UINT event,
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbEventHandler);
|
|
|
|
TRACE_OUT(("event %d, param1 %d, param2 %d", event, param1, param2));
|
|
|
|
switch (event)
|
|
{
|
|
//
|
|
// Confirmation that we have registered with a workset group
|
|
//
|
|
case OM_WSGROUP_REGISTER_CON:
|
|
TRACE_OUT(("OM_WSGROUP_REGISTER_CON %x %x",param1,param2));
|
|
processed = wbOnWsGroupRegisterCon(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// Confirmation that we have moved a workset group
|
|
//
|
|
case OM_WSGROUP_MOVE_CON:
|
|
TRACE_OUT(("OM_WSGROUP_MOVE_CON %x %x",param1,param2));
|
|
processed = wbOnWsGroupMoveCon(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// Our workset group has been moved
|
|
//
|
|
case OM_WSGROUP_MOVE_IND:
|
|
TRACE_OUT(("OM_WSGROUP_MOVE_IND %x %x",param1,param2));
|
|
processed = wbOnWsGroupMoveInd(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// A workset has been created - we do nothing
|
|
//
|
|
case OM_WORKSET_NEW_IND:
|
|
TRACE_OUT(("OM_WORKSET_NEW_IND %x %x",param1,param2));
|
|
processed = TRUE;
|
|
break;
|
|
|
|
//
|
|
// A workset has been opened
|
|
//
|
|
case OM_WORKSET_OPEN_CON:
|
|
TRACE_OUT(("OM_WORKSET_OPEN_CON %x %x",param1,param2));
|
|
processed = wbOnWorksetOpenCon(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// A workset has been locked
|
|
//
|
|
case OM_WORKSET_LOCK_CON:
|
|
TRACE_OUT(("OM_WORKSET_LOCK_CON %x %x",param1,param2));
|
|
processed = wbOnWorksetLockCon(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// A workset has been unlocked
|
|
//
|
|
case OM_WORKSET_UNLOCK_IND:
|
|
TRACE_OUT(("OM_WORKSET_UNLOCK_IND %x %x",param1,param2));
|
|
processed = wbOnWorksetUnlockInd(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// ObMan has run out of resources
|
|
//
|
|
case OM_OUT_OF_RESOURCES_IND:
|
|
TRACE_OUT(("OM_OUT_OF_RESOURCES_IND %x %x",param1,param2));
|
|
wbError();
|
|
processed = TRUE;
|
|
break;
|
|
|
|
//
|
|
// A workset has been cleared
|
|
//
|
|
case OM_WORKSET_CLEAR_IND:
|
|
TRACE_OUT(("OM_WORKSET_CLEAR_IND %x %x",param1,param2));
|
|
processed = wbOnWorksetClearInd(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// A new object has been added to a workset
|
|
//
|
|
case OM_OBJECT_ADD_IND:
|
|
TRACE_OUT(("OM_OBJECT_ADD_IND %x %x",param1,param2));
|
|
processed = wbOnObjectAddInd(param1, (POM_OBJECT)param2);
|
|
break;
|
|
|
|
//
|
|
// An object has been moved
|
|
//
|
|
case OM_OBJECT_MOVE_IND:
|
|
TRACE_OUT(("OM_OBJECT_MOVE_IND %x %x",param1,param2));
|
|
processed = wbOnObjectMoveInd(param1, param2);
|
|
break;
|
|
|
|
//
|
|
// An object has been deleted
|
|
//
|
|
case OM_OBJECT_DELETE_IND:
|
|
TRACE_OUT(("OM_OBJECT_DELETE_IND %x %x",param1,param2));
|
|
processed = wbOnObjectDeleteInd(param1, (POM_OBJECT)param2);
|
|
break;
|
|
|
|
//
|
|
// An object has been updated
|
|
//
|
|
case OM_OBJECT_UPDATE_IND:
|
|
TRACE_OUT(("OM_OBJECT_UPDATE_IND %x %x",param1,param2));
|
|
processed = wbOnObjectUpdateInd(param1, (POM_OBJECT)param2);
|
|
break;
|
|
|
|
//
|
|
// An object has been updated
|
|
//
|
|
case OM_OBJECT_REPLACE_IND:
|
|
TRACE_OUT(("OM_OBJECT_REPLACE_IND %x %x",param1,param2));
|
|
processed = wbOnObjectReplaceInd(param1, (POM_OBJECT)param2);
|
|
break;
|
|
|
|
//
|
|
// Load chaining event
|
|
//
|
|
case WBPI_EVENT_LOAD_NEXT:
|
|
TRACE_OUT(("WBPI_EVENT_LOAD_NEXT"));
|
|
wbPageLoad();
|
|
processed = TRUE;
|
|
break;
|
|
|
|
//
|
|
// Whiteboard page clear indication
|
|
//
|
|
case WBP_EVENT_PAGE_CLEAR_IND:
|
|
TRACE_OUT(("WBP_EVENT_PAGE_CLEAR_IND"));
|
|
processed = wbOnWBPPageClearInd((WB_PAGE_HANDLE) param1);
|
|
break;
|
|
|
|
//
|
|
// Whiteboard lock notification
|
|
//
|
|
case WBP_EVENT_PAGE_ORDER_LOCKED:
|
|
case WBP_EVENT_CONTENTS_LOCKED:
|
|
TRACE_OUT(("WBP_EVENT_xxx_LOCKED (%#hx) %#hx %#lx",
|
|
event,
|
|
param1,
|
|
param2));
|
|
processed = wbOnWBPLock();
|
|
break;
|
|
|
|
//
|
|
// Whiteboard lock failure notification
|
|
//
|
|
case WBP_EVENT_LOCK_FAILED:
|
|
TRACE_OUT(("WBP_EVENT_LOCK_FAILED %x %x",param1,param2));
|
|
processed = wbOnWBPLockFailed();
|
|
break;
|
|
|
|
//
|
|
// Whiteboard Unlock notification
|
|
//
|
|
case WBP_EVENT_UNLOCKED:
|
|
TRACE_OUT(("WBP_EVENT_UNLOCKED %x %x",param1,param2));
|
|
processed = wbOnWBPUnlocked();
|
|
break;
|
|
|
|
//
|
|
// Whiteboard Page Order Updated notification
|
|
//
|
|
case WBP_EVENT_PAGE_ORDER_UPDATED:
|
|
TRACE_OUT(("WBP_EVENT_PAGE_ORDER_UPDATED %x %x",
|
|
param1,
|
|
param2));
|
|
processed = wbOnWBPPageOrderUpdated();
|
|
break;
|
|
|
|
//
|
|
// We are not interested in this event - do nothing
|
|
//
|
|
default:
|
|
TRACE_OUT(("Event ignored"));
|
|
break;
|
|
} // Switch on event type
|
|
|
|
|
|
DebugExitBOOL(wbEventHandler, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// wbJoinCallError
|
|
//
|
|
// This function should be called in STATE_REGISTERING only.
|
|
//
|
|
//
|
|
void WbClient::wbJoinCallError(void)
|
|
{
|
|
DebugEntry(wbJoinCallError);
|
|
|
|
ASSERT((m_state == STATE_REGISTERING));
|
|
|
|
//
|
|
// Post a registration failed message to the client
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_REGISTER_FAILED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_JOIN_CALL_FAILED, // Failure
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Tidy up after the attempt to join the call
|
|
//
|
|
wbLeaveCall();
|
|
|
|
DebugExitVOID(wbJoinCallError);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// wbError
|
|
//
|
|
void WbClient::wbError(void)
|
|
{
|
|
DebugEntry(wbError);
|
|
|
|
//
|
|
// An error has occurred during Core processing. We act according to the
|
|
// current state.
|
|
//
|
|
switch (m_state)
|
|
{
|
|
//
|
|
// If the error has occurred during registration, post a registration
|
|
// failure message to the client and cancel registration.
|
|
//
|
|
case STATE_REGISTERING:
|
|
wbJoinCallError();
|
|
break;
|
|
|
|
//
|
|
// If the error occurred during normal running, we tell the client who
|
|
// must deregister.
|
|
//
|
|
case STATE_IDLE:
|
|
//
|
|
// Only take action if we are not already in fatal error state
|
|
//
|
|
if (m_errorState == ERROR_STATE_EMPTY)
|
|
{
|
|
//
|
|
// Post an error message to the client
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_ERROR"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_ERROR, // Error
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Record that an error has occurred
|
|
//
|
|
m_errorState = ERROR_STATE_FATAL;
|
|
TRACE_OUT(("Moved error state to ERROR_STATE_FATAL"));
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Client is in an unknown state
|
|
//
|
|
default:
|
|
ERROR_OUT(("Bad main state for call"));
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(wbError);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWSGroupMoveCon
|
|
//
|
|
// Purpose: Routine processing OM_WSGROUP_MOVE_CON events.
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWsGroupMoveCon
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
UINT rc;
|
|
BOOL failedToJoin = FALSE;
|
|
|
|
DebugEntry(wbOnWsGroupMoveCon);
|
|
|
|
//
|
|
// Check that this is the event we are expecting
|
|
//
|
|
if (pEvent32->correlator != m_wsgroupCorrelator)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Show that we have processed the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Test for the correct state
|
|
//
|
|
if (m_subState != STATE_REG_PENDING_WSGROUP_MOVE)
|
|
{
|
|
//
|
|
// We are not in the correct state for this event - this is an internal
|
|
// error.
|
|
//
|
|
ERROR_OUT(("Wrong state for WSGroupMoveCon"));
|
|
}
|
|
|
|
//
|
|
// Check that the return code for the move is OK
|
|
//
|
|
if (pEvent32->result != 0)
|
|
{
|
|
//
|
|
// Moving the workset group failed - post a "join call failed" message
|
|
// to the front-end.
|
|
//
|
|
TRACE_OUT(("WSGroup move failed, result = %d", pEvent32->result));
|
|
failedToJoin = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The WSGroupMove has completed successfully. Replace our local user
|
|
// object by deleting the current one (we must have one to get to this
|
|
// point) and adding a new one.
|
|
//
|
|
// The reason we do this is that our existing user object has been moved
|
|
// from the local domain into a call, but since it is in a non-persistent
|
|
// workset, the Obman behaviour for this object when the call ends is
|
|
// undefined. So we replace the object to get a defined behaviour.
|
|
//
|
|
TRACE_OUT(("Deleting local user object"));
|
|
rc = OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
m_pObjLocal);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error deleting local user object = %u", rc));
|
|
}
|
|
|
|
TRACE_OUT(("Adding new local user object"));
|
|
rc = wbAddLocalUserObject();
|
|
if (rc != 0)
|
|
{
|
|
TRACE_OUT(("Failed to add local user object"));
|
|
failedToJoin = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Get the clients network ID, used in graphic objects to determine where
|
|
// they are loaded.
|
|
//
|
|
if (!wbGetNetUserID())
|
|
{
|
|
//
|
|
// Tidy up (and post an error event to the client)
|
|
//
|
|
ERROR_OUT(("Failed to get user ID, rc %u", rc));
|
|
failedToJoin = TRUE;
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We added our user object successfully, so now wait for the
|
|
// OBJECT_ADD_IND to arrive.
|
|
//
|
|
m_subState = STATE_REG_PENDING_NEW_USER_OBJECT;
|
|
|
|
DC_EXIT_POINT:
|
|
if (failedToJoin)
|
|
{
|
|
//
|
|
// We have failed to join the call, so clean up.
|
|
//
|
|
wbError();
|
|
}
|
|
|
|
DebugExitBOOL(wbOnWsGroupMoveCon, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWSGroupMoveInd
|
|
//
|
|
// Purpose: Routine processing OM_WSGROUP_MOVE_IND events.
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWsGroupMoveInd
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR callID
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
BOOL processed = TRUE;
|
|
|
|
DebugEntry(wbOnWsGroupMoveInd);
|
|
|
|
if (callID != OM_NO_CALL)
|
|
{
|
|
TRACE_OUT(("Moved into new call"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If we are registering, treat it as a failure to join the call,
|
|
// otherwise let the client know about the network failure.
|
|
//
|
|
if (m_state == STATE_REGISTERING)
|
|
{
|
|
TRACE_OUT(("Call went down while registering"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRACE_OUT(("Posting WBP_EVENT_NETWORK_LOST"));
|
|
WBP_PostEvent(0,
|
|
WBP_EVENT_NETWORK_LOST, // Unlocked
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Tidy up the User Information workset (the local client is now the
|
|
// only user). Note that since the user information workset it
|
|
// non-persistent, Obman will delete the remote user objects for us.
|
|
//
|
|
|
|
//
|
|
// - check we have opened the user workset
|
|
//
|
|
if ( (m_state > STATE_REGISTERING) ||
|
|
(m_subState > STATE_REG_PENDING_USER_WORKSET))
|
|
{
|
|
//
|
|
// Delete the lock object.
|
|
//
|
|
if (m_pObjLock != NULL)
|
|
{
|
|
TRACE_OUT(("Deleting lock object %d", m_pObjLock));
|
|
if (OM_ObjectDelete(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjLock) != 0)
|
|
{
|
|
ERROR_OUT(("Error deleting lock object"));
|
|
}
|
|
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
//
|
|
// If all is well at this point the unlock process will be
|
|
// completed when the object delete ind is received.
|
|
//
|
|
m_lockState = LOCK_STATE_PENDING_DELETE;
|
|
TRACE_OUT(("Moved to state LOCK_STATE_PENDING_DELETE"));
|
|
}
|
|
else
|
|
{
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
TRACE_OUT(("Moved to state LOCK_STATE_EMPTY"));
|
|
}
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWSGroupMoveInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWorksetClearInd
|
|
//
|
|
// Purpose: Routine processing OM_WORKSET_CLEAR_IND events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWorksetClearInd
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnWorksetClearInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent16->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch(pEvent16->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
ERROR_OUT(("Unexpected clear for Page Control Workset"));
|
|
break;
|
|
|
|
//
|
|
// Lock Workset
|
|
//
|
|
case SYNC_CONTROL_WORKSET:
|
|
ERROR_OUT(("Unexpected clear for Sync Control Workset"));
|
|
break;
|
|
|
|
//
|
|
// User Information Workset
|
|
//
|
|
case USER_INFORMATION_WORKSET:
|
|
ERROR_OUT(("Unexpected clear for User Information Workset"));
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
//
|
|
// Tell the client that the page has been cleared - the client must then
|
|
// confirm the clear.
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PAGE_CLEAR_IND"));
|
|
WBP_PostEvent(
|
|
0,
|
|
WBP_EVENT_PAGE_CLEAR_IND,
|
|
pEvent16->worksetID,
|
|
0);
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWorksetClearInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWBPPageClearInd
|
|
//
|
|
// Purpose: Routine processing WBP_PAGE_CLEAR_IND events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWBPPageClearInd(WB_PAGE_HANDLE hPage)
|
|
{
|
|
BOOL processed;
|
|
|
|
DebugEntry(wbOnWBPPageClearInd);
|
|
|
|
//
|
|
// This routine catches WB_PAGE_CLEAR_IND events posted to the client.
|
|
// Because of the asynchronous nature of page order updates these can
|
|
// sometimes have been sent previously for pages that are now no longer
|
|
// in use. We trap these events here, confirm the clear to ObMan and
|
|
// discard the event.
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Page is not in use - confirming workset clear immediately"));
|
|
|
|
//
|
|
// Accept the page clear immediately
|
|
//
|
|
wbPageClearConfirm(hPage);
|
|
processed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we get here the page is in use - so we must pass the event on to
|
|
// the client. Resetting the result code of this routine to "not
|
|
// processed" will ask the utilities to pass it on to the next event
|
|
// handler.
|
|
//
|
|
processed = FALSE;
|
|
}
|
|
|
|
|
|
DebugExitBOOL(wbOnWBPPageClearInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnObjectAddInd
|
|
//
|
|
// Purpose: Routine processing OM_OBJECT_ADD_IND events.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnObjectAddInd
|
|
(
|
|
UINT_PTR param1,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnObjectAddInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent16->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch(pEvent16->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
wbOnPageObjectAddInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Sync Control Workset
|
|
//
|
|
case SYNC_CONTROL_WORKSET:
|
|
wbOnSyncObjectAddInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// User Information Workset
|
|
//
|
|
case USER_INFORMATION_WORKSET:
|
|
wbOnUserObjectAddInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
wbOnGraphicObjectAddInd(pEvent16->worksetID, pObj);
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnObjectAddInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGetPageObjectType
|
|
//
|
|
// Purpose: Get the type of an object in the Page Control Workset
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbGetPageObjectType
|
|
(
|
|
POM_OBJECT pObj,
|
|
UINT * pObjectType
|
|
)
|
|
{
|
|
UINT result;
|
|
POM_OBJECTDATA pData;
|
|
|
|
DebugEntry(wbGetPageObjectType);
|
|
|
|
//
|
|
// Read the object to get its type
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Error reading object = %d", result));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The first two bytes of the object data give its type
|
|
//
|
|
*pObjectType = *((TSHR_UINT16 *)pData->data);
|
|
|
|
//
|
|
// Release the object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj,
|
|
&pData);
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbGetPageObjectType, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageObjectAddInd
|
|
//
|
|
// Purpose: Routine processing OM_OBJECT_ADD_IND events occurring on the
|
|
// Page Control Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnPageObjectAddInd(POM_OBJECT pObj)
|
|
{
|
|
UINT objectType;
|
|
|
|
DebugEntry(wbOnPageObjectAddInd);
|
|
|
|
//
|
|
// Read the object to get its type
|
|
//
|
|
if (wbGetPageObjectType(pObj, &objectType) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Act according to the type of object added
|
|
//
|
|
switch (objectType)
|
|
{
|
|
case TYPE_CONTROL_LOCK:
|
|
TRACE_OUT(("It is a lock object"));
|
|
wbReadLock();
|
|
break;
|
|
|
|
case TYPE_CONTROL_PAGE_ORDER:
|
|
TRACE_OUT(("It is the Page Control object"));
|
|
wbOnPageControlObjectAddInd(pObj);
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Unknown object type added to Page Control Workset"));
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnPageObjectAddInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageControlObjectAddInd
|
|
//
|
|
// Purpose: Routine processing add of page control object
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnPageControlObjectAddInd(POM_OBJECT pObj)
|
|
{
|
|
DebugEntry(wbOnPageControlObjectAddInd);
|
|
|
|
//
|
|
// We only ever expect to get one of these objects
|
|
//
|
|
if (m_pObjPageControl != 0)
|
|
{
|
|
//
|
|
// Check that this is the same object - the add has been triggered by
|
|
// the workset open but we have already read the contents.
|
|
//
|
|
ASSERT((m_pObjPageControl == pObj));
|
|
}
|
|
|
|
//
|
|
// Save the handle of the object
|
|
//
|
|
m_pObjPageControl = pObj;
|
|
TRACE_OUT(("Got Page Control object"));
|
|
|
|
//
|
|
// Continue according to the current state
|
|
//
|
|
switch (m_state)
|
|
{
|
|
case STATE_REGISTERING:
|
|
//
|
|
// We now have a Page Control Object - if we are waiting for the
|
|
// object we can now move to the next stage.
|
|
//
|
|
if (m_subState == STATE_REG_PENDING_PAGE_CONTROL)
|
|
{
|
|
//
|
|
// If we have the lock on the Page Control Workset then we are in
|
|
// control of the registration process. We must add the sync
|
|
// control object to the Sync Workset.
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
//
|
|
// Create the Sync Control Object
|
|
//
|
|
if (wbCreateSyncControl() != 0)
|
|
{
|
|
ERROR_OUT(("Error adding Sync Control Object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we do not have the sync control object then wait for it -
|
|
// otherwise we can complete initialisation.
|
|
//
|
|
if (m_pObjSyncControl == 0)
|
|
{
|
|
m_subState = STATE_REG_PENDING_SYNC_CONTROL;
|
|
TRACE_OUT(("Moved substate to STATE_REG_PENDING_SYNC_CONTROL"));
|
|
DC_QUIT;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If it is us who has the Page Control Workset locked - release
|
|
// the lock.
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
//
|
|
// Unlock the workset
|
|
//
|
|
wbUnlock();
|
|
|
|
//
|
|
// Wait for notification of the lock being released
|
|
//
|
|
TRACE_OUT(("Sub state change %d to %d",
|
|
m_subState, STATE_REG_PENDING_UNLOCK));
|
|
|
|
m_subState = STATE_REG_PENDING_UNLOCK;
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("Page Control and Sync Control objects both there."));
|
|
TRACE_OUT(("Registration can be completed"));
|
|
wbOnControlWorksetsReady();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// In other registration states we are not ready to process the
|
|
// event. It will be dealt with later.
|
|
//
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
//
|
|
// We must already have a Page COntrol Object since we are in idle
|
|
// state. So this is an error. It may have been caused by another
|
|
// client so we just trace it rather than asserting.
|
|
//
|
|
ERROR_OUT(("Unexpected add of Page Control Object in idle state"));
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad main state"));
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnPageControlObjectAddInd);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnSyncObjectAddInd
|
|
//
|
|
// Purpose: Routine processing OM_OBJECT_ADD_IND events occurring on the
|
|
// Sync Control Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnSyncObjectAddInd(POM_OBJECT pObj)
|
|
{
|
|
DebugEntry(wbOnSyncObjectAddInd);
|
|
|
|
//
|
|
// We only expect this during registration
|
|
//
|
|
switch(m_state)
|
|
{
|
|
//
|
|
// We are waiting for registration to continue
|
|
//
|
|
case STATE_REGISTERING:
|
|
switch(m_subState)
|
|
{
|
|
//
|
|
// We are waiting for a Sync Control Object
|
|
//
|
|
case STATE_REG_PENDING_SYNC_CONTROL:
|
|
m_pObjSyncControl = pObj;
|
|
|
|
//
|
|
// The Sync Control object has been added. We do not need to do
|
|
// anything with it yet.
|
|
//
|
|
|
|
//
|
|
// If we already have the page control object then we can
|
|
// complete initilisation, otherwise we have to wait for it.
|
|
//
|
|
if (m_pObjPageControl == 0)
|
|
{
|
|
TRACE_OUT(("Sub state change %d to %d",
|
|
m_subState, STATE_REG_PENDING_PAGE_CONTROL));
|
|
m_subState = STATE_REG_PENDING_PAGE_CONTROL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If it is us who has the Page Control Workset locked -
|
|
// release the lock.
|
|
//
|
|
if (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
{
|
|
//
|
|
// Unlock the workset
|
|
//
|
|
wbUnlock();
|
|
|
|
//
|
|
// Wait for notification of the lock being released
|
|
//
|
|
TRACE_OUT(("Sub state change %d to %d",
|
|
m_subState, STATE_REG_PENDING_UNLOCK));
|
|
m_subState = STATE_REG_PENDING_UNLOCK;
|
|
}
|
|
else
|
|
{
|
|
TRACE_OUT(("Page Control and Sync Control objects both there."));
|
|
TRACE_OUT(("Registration can be completed"));
|
|
wbOnControlWorksetsReady();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Save the handle of the Sync Control Object
|
|
//
|
|
m_pObjSyncControl = pObj;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// We are fully registered and are therefore not expecting an add event
|
|
// on this workset. However, since we are registered we must be
|
|
// satisfied that we have a Sync Control Object - so ignore the error.
|
|
//
|
|
case STATE_IDLE:
|
|
ERROR_OUT(("Sync object add not expected in idle state"));
|
|
break;
|
|
|
|
//
|
|
// The client is in an unknown state
|
|
//
|
|
default:
|
|
ERROR_OUT(("Client in unknown state = %d", m_state));
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(wbOnSyncObjectAddInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnUserObjectAddInd
|
|
//
|
|
// Purpose: A user object has been added to the User Information Workset.
|
|
// Inform the client that a new user has joined the call.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnUserObjectAddInd(POM_OBJECT pObj)
|
|
{
|
|
UINT countUsers;
|
|
|
|
DebugEntry(wbOnUserObjectAddInd);
|
|
|
|
OM_WorksetCountObjects(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&countUsers);
|
|
TRACE_OUT(("Number of users is now %d", countUsers));
|
|
|
|
//
|
|
// Ignore the add indication for our own user.
|
|
//
|
|
if (m_pObjLocal == pObj)
|
|
{
|
|
TRACE_OUT(("Got add of own user object"));
|
|
//
|
|
// If we have the lock (temporarily, with NULL lock owner handle),
|
|
// then we need to update the lock object with our actual handle.
|
|
//
|
|
if ((m_pObjLock != NULL) &&
|
|
(m_lockState == LOCK_STATE_GOT_LOCK))
|
|
{
|
|
TRACE_OUT(("Got the lock - update lock object"));
|
|
wbWriteLock();
|
|
}
|
|
|
|
if ((m_state == STATE_REGISTERING) &&
|
|
(m_subState == STATE_REG_PENDING_NEW_USER_OBJECT))
|
|
{
|
|
//
|
|
// We have successfully joined the call.
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_JOIN_CALL_OK"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_JOIN_CALL_OK, // Unlocked
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Update the state to show that we are ready for work again
|
|
//
|
|
m_state = STATE_IDLE;
|
|
m_subState = STATE_EMPTY;
|
|
TRACE_OUT(("Moved state back to STATE_IDLE"));
|
|
}
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// If we have created our user object we must check to see if the new
|
|
// user has usurped our color. If so we may need to change color.
|
|
//
|
|
if (m_pObjLocal != NULL)
|
|
{
|
|
TRACE_OUT(("We have added our user object - check colors"));
|
|
wbCheckPersonColor(pObj);
|
|
}
|
|
|
|
//
|
|
// Ignore these events unless we are fully registered
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Ignoring user object add - not fully registered"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Tell the client that a new user has joined
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_USER_JOINED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PERSON_JOINED, // Event type
|
|
0, // No short parameter
|
|
(UINT_PTR) pObj); // User object handle
|
|
|
|
//
|
|
// Try to read the lock object - we may not have been able to do this
|
|
// yet.
|
|
//
|
|
wbReadLock();
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnUserObjectAddInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnGraphicObjectAddInd
|
|
//
|
|
// Purpose: A graphic object has been added to a page workset.
|
|
// Inform the client that a new graphic has been added.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnGraphicObjectAddInd
|
|
(
|
|
OM_WORKSET_ID worksetID,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID;
|
|
POM_OBJECTDATA pData;
|
|
PWB_GRAPHIC pGraphic;
|
|
UINT result;
|
|
|
|
DebugEntry(wbOnGraphicObjectAddInd);
|
|
|
|
//
|
|
// NFC, SFR 6450. If this object was loaded from file on this machine,
|
|
// then we dont need to set the "changed flag". Otherwise record that
|
|
// the contents have changed
|
|
//
|
|
//
|
|
// Read the object.
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj,
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
WARNING_OUT(("OM_ObjectRead (%u) failed, set changed flag anyway ", result));
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("changed flag now TRUE"));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Convert the ObMan pointer to a core pointer
|
|
//
|
|
pGraphic = GraphicPtrFromObjectData(pData);
|
|
|
|
if ( ! ((pGraphic->loadedFromFile) &&
|
|
(pGraphic->loadingClientID == m_clientNetID)))
|
|
{
|
|
TRACE_OUT(("Not loaded from file locally - Set changed flag on"));
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
}
|
|
|
|
//
|
|
// Finished with the object, so release it.
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj,
|
|
&pData);
|
|
}
|
|
|
|
//
|
|
// These events are ignored unless we are fully registered (the client
|
|
// can do nothing about them if it is not registered correctly).
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Ignoring add of graphic object - not registered"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that this page is actually in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Ignoring add to page not in use"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Inform the client of the object being added
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_ADDED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_GRAPHIC_ADDED, // Event type
|
|
hPage, //
|
|
(UINT_PTR)pObj); // User object handle
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnGraphicObjectAddInd);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnObjectMoveInd
|
|
//
|
|
// Purpose: This routine is called whenever OM_OBJECT_MOVE_IND events are
|
|
// received.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnObjectMoveInd
|
|
(
|
|
UINT_PTR param1,
|
|
UINT_PTR param2
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
POM_EVENT_DATA32 pEvent32 = (POM_EVENT_DATA32) ¶m2;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnObjectMoveInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent16->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch(pEvent16->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset Lock Workset User Information Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
case SYNC_CONTROL_WORKSET:
|
|
case USER_INFORMATION_WORKSET:
|
|
//
|
|
// Event not expected for these worksets
|
|
//
|
|
ERROR_OUT(("Unexpected for workset %d", (UINT) pEvent16->worksetID));
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
wbOnGraphicObjectMoveInd(pEvent16->worksetID,
|
|
(POM_OBJECT) param2);
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnObjectMoveInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnGraphicObjectMoveInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_MOVE_IND is
|
|
// received for a graphic object.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnGraphicObjectMoveInd
|
|
(
|
|
OM_WORKSET_ID worksetID,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID;
|
|
|
|
DebugEntry(wbOnGraphicObjectMoveInd);
|
|
|
|
//
|
|
// Record that the contents have changed
|
|
//
|
|
m_changed = TRUE;
|
|
|
|
//
|
|
// These events are ignored unless we are fully registered (the client
|
|
// can do nothing about them).
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Ignoring move of graphic object before registration"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that this page is actually in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Ignoring move in page not in use"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Inform the client of the object being added
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_MOVED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_GRAPHIC_MOVED, // Event type
|
|
hPage, // Page handle
|
|
(UINT_PTR)pObj); // Object handle
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnGraphicObjectMoveInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnObjectDeleteInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is
|
|
// received.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnObjectDeleteInd
|
|
(
|
|
UINT_PTR param1,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnObjectDeleteInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent16->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch(pEvent16->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
wbOnPageObjectDeleteInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Sync Workset
|
|
//
|
|
case SYNC_CONTROL_WORKSET:
|
|
ERROR_OUT(("Illegal object delete on sync control workset - ignored"));
|
|
|
|
//
|
|
// We do not confirm the delete since we do not want to lose the Sync
|
|
// Control Object.
|
|
//
|
|
break;
|
|
|
|
//
|
|
// User Information Workset
|
|
//
|
|
case USER_INFORMATION_WORKSET:
|
|
wbOnUserObjectDeleteInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
wbOnGraphicObjectDeleteInd(pEvent16->worksetID, pObj);
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnObjectDeleteInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageObjectDeleteInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is
|
|
// received for an object in the Page Control Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnPageObjectDeleteInd(POM_OBJECT pObj)
|
|
{
|
|
UINT objectType;
|
|
|
|
DebugEntry(wbOnPageObjectDeleteInd);
|
|
|
|
//
|
|
// Get the type of object that is being deleted
|
|
//
|
|
if (wbGetPageObjectType(pObj, &objectType) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
switch(objectType)
|
|
{
|
|
case TYPE_CONTROL_PAGE_ORDER:
|
|
//
|
|
// The object is the Page Control Object - something serious is wrong
|
|
// as this object should never be deleted.
|
|
//
|
|
ERROR_OUT(("Attempt to delete page control object"));
|
|
break;
|
|
|
|
case TYPE_CONTROL_LOCK:
|
|
TRACE_OUT(("Lock object being deleted"));
|
|
wbOnLockControlObjectDeleteInd(pObj);
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad object type"));
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnPageObjectDeleteInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnLockControlObjectDeleteInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is
|
|
// received for a lock object in the Page Control Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnLockControlObjectDeleteInd(POM_OBJECT pObj
|
|
)
|
|
{
|
|
DebugEntry(wbOnLockControlObjectDeleteInd);
|
|
|
|
//
|
|
// Confirm the delete to ObMan
|
|
//
|
|
TRACE_OUT(("Lock handle %x, expecting %x", pObj, m_pObjLock));
|
|
if (pObj != m_pObjLock)
|
|
{
|
|
WARNING_OUT(("Unexpected lock handle %x, expecting %x",
|
|
pObj, m_pObjLock));
|
|
}
|
|
|
|
OM_ObjectDeleteConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj);
|
|
m_pObjLock = NULL;
|
|
|
|
//
|
|
// Process according to the current lock state
|
|
//
|
|
switch(m_lockState)
|
|
{
|
|
case LOCK_STATE_PENDING_DELETE:
|
|
//
|
|
// We are deleting our lock object. We must unlock the
|
|
// workset.
|
|
//
|
|
TRACE_OUT(("Our lock object delete confirmed - unlocking the workset"));
|
|
OM_WorksetUnlock(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET);
|
|
break;
|
|
|
|
case LOCK_STATE_LOCKED_OUT:
|
|
//
|
|
// The user with the lock has deleted the lock object. We treat
|
|
// this as a removal of the whiteboard lock - we reset the
|
|
// state at the end of this function.
|
|
//
|
|
TRACE_OUT(("Remote user's lock object deleted"));
|
|
break;
|
|
|
|
case LOCK_STATE_EMPTY:
|
|
//
|
|
// We have just deleted the object at the end of a call to tidy
|
|
// up. Carry on so we reset lockType / pObjPersonLock etc.
|
|
//
|
|
TRACE_OUT(("LOCK_STATE_EMPTY"));
|
|
break;
|
|
|
|
case LOCK_STATE_PENDING_LOCK:
|
|
WARNING_OUT(("LOCK_STATE_PENDING_LOCK"));
|
|
//
|
|
// We don't expect to get here. If by some chance we do, then
|
|
// just quit, since we should still get the workset lock con.
|
|
//
|
|
DC_QUIT;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad lock state %d", m_lockState));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The lock object has been deleted, so there is no lock active
|
|
//
|
|
m_lockType = WB_LOCK_TYPE_NONE;
|
|
m_pObjPersonLock = NULL;
|
|
|
|
//
|
|
// Record that there is now no lock
|
|
//
|
|
m_lockState = LOCK_STATE_EMPTY;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_EMPTY"));
|
|
|
|
//
|
|
// Notify the client of the lock status change
|
|
//
|
|
wbSendLockNotification();
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnLockControlObjectDeleteInd);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnGraphicObjectDeleteInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is
|
|
// received for an object in a page workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnGraphicObjectDeleteInd
|
|
(
|
|
OM_WORKSET_ID worksetID,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID;
|
|
BOOL bConfirm = FALSE;
|
|
|
|
DebugEntry(wbOnGraphicObjectDeleteInd);
|
|
|
|
//
|
|
// Record that the contents have changed
|
|
//
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
|
|
//
|
|
// These events are handled within the core until the client is ready.
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Delete of graphic object before registration"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check that this page is actually in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Delete in page that is not in use"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check whether we are to pass the event on to the client
|
|
//
|
|
if (bConfirm)
|
|
{
|
|
//
|
|
// Confirm the delete to ObMan
|
|
//
|
|
TRACE_OUT(("Confirming delete immediately"));
|
|
OM_ObjectDeleteConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Inform the client of the object being added
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_DELETE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_GRAPHIC_DELETE_IND, // Event type
|
|
hPage, // Page handle
|
|
(UINT_PTR)pObj); // Object handle
|
|
}
|
|
|
|
DebugExitVOID(wbOnGraphicObjectDeleteInd);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnUserObjectDeleteInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_DELETE_IND is
|
|
// received for an object in the User Information Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnUserObjectDeleteInd
|
|
(
|
|
POM_OBJECT pObjPerson
|
|
)
|
|
{
|
|
DebugEntry(wbOnUserObjectDeleteInd);
|
|
|
|
//
|
|
// If the user which has been removed had a lock then remove its user
|
|
// handle from the client data. The lock is still there, and will be
|
|
// removed when we get the WORKSET_UNLOCK_IND for the lock workset. This
|
|
// arrives after the user-object delete, because the user workset is of
|
|
// higher priority.
|
|
//
|
|
if (m_pObjPersonLock == pObjPerson)
|
|
{
|
|
m_pObjPersonLock = NULL;
|
|
}
|
|
|
|
//
|
|
// These events are ignored unless we are fully registered (the client
|
|
// can do nothing about them).
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Delete of user object before registration - confirming"));
|
|
|
|
//
|
|
// Confirm the delete
|
|
//
|
|
OM_ObjectDeleteConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjPerson);
|
|
|
|
//
|
|
// Nothing more to be done
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Inform the client of the user leaving
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_USER_LEFT_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PERSON_LEFT, // Event type
|
|
0, // No short parameter
|
|
(UINT_PTR) pObjPerson); // User object handle
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnUserObjectDeleteInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnObjectUpdateInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is
|
|
// received for an object in a page workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnObjectUpdateInd
|
|
(
|
|
UINT_PTR param1,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent16 = (POM_EVENT_DATA16) ¶m1;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnObjectUpdateInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent16->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent16->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch(pEvent16->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
ERROR_OUT(("Illegal object update on page control workset - ignored"));
|
|
|
|
//
|
|
// Updates on the Page Control Object are not allowed - do not
|
|
// confirm it.
|
|
//
|
|
break;
|
|
|
|
//
|
|
// Lock Workset
|
|
//
|
|
case SYNC_CONTROL_WORKSET:
|
|
ERROR_OUT(("Illegal object update on sync control workset"));
|
|
|
|
//
|
|
// Updates to the Sync Control Object itself are not allowed - do not
|
|
// confirm it.
|
|
//
|
|
break;
|
|
|
|
//
|
|
// User Information Workset
|
|
//
|
|
case USER_INFORMATION_WORKSET:
|
|
wbOnUserObjectUpdateInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
wbOnGraphicObjectUpdateInd(pEvent16->worksetID, pObj);
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnObjectUpdateInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnUserObjectUpdateInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is
|
|
// received for an object in the User Information Workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnUserObjectUpdateInd(POM_OBJECT pObj)
|
|
{
|
|
DebugEntry(wbOnUserObjectUpdateInd);
|
|
|
|
//
|
|
// if the updated user object is not the local user's, and we have
|
|
// already added the local user's object, then check the color hasn't
|
|
// changed to clash with the local user's color.
|
|
//
|
|
if ( (m_pObjLocal != pObj)
|
|
&& (m_pObjLocal != NULL))
|
|
{
|
|
TRACE_OUT(("Check color of updated user object"));
|
|
wbCheckPersonColor(pObj);
|
|
}
|
|
|
|
//
|
|
// Don't inform the front end if we aren't fully registered
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("User object updated before registration - confirming"));
|
|
|
|
//
|
|
// Confirm the update immediately
|
|
//
|
|
OM_ObjectUpdateConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObj);
|
|
|
|
//
|
|
// Nothing more to be done
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Tell the client that a user has been updated
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PERSON_UPDATE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PERSON_UPDATE, // Event type
|
|
0, // No short parameter
|
|
(UINT_PTR) pObj); // User object handle
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnUserObjectUpdateInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnUserObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is
|
|
// received for an object in the User Information Workset.
|
|
//
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnUserObjectReplaceInd(POM_OBJECT pObj)
|
|
{
|
|
DebugEntry(wbOnUserObjectReplaceInd);
|
|
|
|
//
|
|
// if the updated user object is not the local user's, and we have
|
|
// already added the local user's object, then check the color hasn't
|
|
// changed to clash with the local user's color.
|
|
//
|
|
if ( (m_pObjLocal != pObj)
|
|
&& (m_pObjLocal != NULL))
|
|
{
|
|
TRACE_OUT(("Check color of updated user object"));
|
|
wbCheckPersonColor(pObj);
|
|
}
|
|
|
|
//
|
|
// Don't inform the front end if we aren't fully registered
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("User object replaced before registration - confirming"));
|
|
|
|
//
|
|
// Confirm the replace immediately
|
|
//
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObj);
|
|
|
|
//
|
|
// Nothing more to be done
|
|
//
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Tell the client that a user has been updated
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PERSON_UPDATE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PERSON_REPLACE, // Event type
|
|
0, // No short parameter
|
|
(UINT_PTR) pObj); // User object handle
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnUserObjectReplaceInd);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnGraphicObjectUpdateInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_UPDATE_IND is
|
|
// received for an object in a page workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnGraphicObjectUpdateInd
|
|
(
|
|
OM_WORKSET_ID worksetID,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID;
|
|
BOOL bConfirm = FALSE;
|
|
|
|
DebugEntry(wbOnGraphicObjectUpdateInd);
|
|
|
|
//
|
|
// Record that the contents have changed
|
|
//
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
|
|
//
|
|
// These events are handled within the core until the client is ready.
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Update of graphic object before registration"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check that this page is actually in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Update for page that is not in use"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check whether we are to confirm the update now or ask the client
|
|
//
|
|
if (bConfirm)
|
|
{
|
|
//
|
|
// Confirm the update immediately
|
|
//
|
|
TRACE_OUT(("Confirming update immediately"));
|
|
OM_ObjectUpdateConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Inform the client of the object being added
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_UPDATE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_GRAPHIC_UPDATE_IND, // Event type
|
|
hPage, // Page handle
|
|
(UINT_PTR)pObj); // Object handle
|
|
}
|
|
|
|
DebugExitVOID(wbOnGraphicObjectUpdateInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is
|
|
// received.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnObjectReplaceInd
|
|
(
|
|
UINT_PTR param1,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
POM_EVENT_DATA16 pEvent = (POM_EVENT_DATA16) ¶m1;
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnObjectReplaceInd);
|
|
|
|
//
|
|
// Check that the workset group is ours
|
|
//
|
|
if (pEvent->hWSGroup != m_hWSGroup)
|
|
{
|
|
ERROR_OUT(("Event for unknown workset group = %d", pEvent->hWSGroup));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We will process the event
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// Process the event according to the workset ID
|
|
//
|
|
switch (pEvent->worksetID)
|
|
{
|
|
//
|
|
// Page Control Workset
|
|
//
|
|
case PAGE_CONTROL_WORKSET:
|
|
wbOnPageObjectReplaceInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Lock Workset
|
|
//
|
|
case SYNC_CONTROL_WORKSET:
|
|
wbOnSyncObjectReplaceInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// User Information Workset
|
|
//
|
|
case USER_INFORMATION_WORKSET:
|
|
wbOnUserObjectReplaceInd(pObj);
|
|
break;
|
|
|
|
//
|
|
// Other (should be a Page Workset)
|
|
//
|
|
default:
|
|
wbOnGraphicObjectReplaceInd(pEvent->worksetID, pObj);
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnObjectReplaceInd, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever the Page Control object is
|
|
// replaced.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnPageObjectReplaceInd(POM_OBJECT pObj)
|
|
{
|
|
UINT objectType;
|
|
|
|
DebugEntry(wbOnPageObjectReplaceInd);
|
|
|
|
//
|
|
// Confirm the change to ObMan (cannot fail)
|
|
//
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj);
|
|
|
|
//
|
|
// Read the object to get its type
|
|
//
|
|
if (wbGetPageObjectType(pObj, &objectType) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Act according to the type of object added
|
|
//
|
|
switch (objectType)
|
|
{
|
|
case TYPE_CONTROL_LOCK:
|
|
wbReadLock();
|
|
break;
|
|
|
|
case TYPE_CONTROL_PAGE_ORDER:
|
|
wbOnPageControlObjectReplaceInd();
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Unknown object type added to Page Control Workset"));
|
|
break;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnPageObjectReplaceInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnPageControlObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever the Page Control object is
|
|
// replaced.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnPageControlObjectReplaceInd(void)
|
|
{
|
|
DebugEntry(wbOnPageControlObjectReplaceInd);
|
|
|
|
//
|
|
// Process according to the current state
|
|
//
|
|
switch (m_state)
|
|
{
|
|
case STATE_REGISTERING:
|
|
//
|
|
// During registration we do nothing - the Page Order is updated
|
|
// explicitly as one of the last registration actions.
|
|
//
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
//
|
|
// When we are fully registered we must send events to the front-end
|
|
// indicating what changes have been made to the page list.
|
|
//
|
|
wbProcessPageControlChanges();
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad client major state"));
|
|
break;
|
|
}
|
|
|
|
DebugExitVOID(wbOnPageControlObjectReplaceInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbProcessPageControlChanges
|
|
//
|
|
// Purpose: This routine is called whenever the Page Control object is
|
|
// replaced in idle state. It reads the new Page Control data
|
|
// and starts the process of informing the client of any changes.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbProcessPageControlChanges(void)
|
|
{
|
|
BYTE toBeMarked[WB_MAX_PAGES];
|
|
UINT indexExternal;
|
|
UINT indexInternal;
|
|
UINT lLengthExternal;
|
|
BOOL addOutstanding = TRUE;
|
|
PWB_PAGE_ORDER pPageOrderExternal;
|
|
PWB_PAGE_ORDER pPageOrderInternal = &(m_pageOrder);
|
|
PWB_PAGE_STATE pPageState;
|
|
POM_WORKSET_ID pPageExternal;
|
|
UINT countPagesExternal;
|
|
POM_OBJECTDATA pData = NULL;
|
|
|
|
DebugEntry(wbProcessPageControlChanges);
|
|
|
|
//
|
|
// Read the new Page Control Object
|
|
//
|
|
if (OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjPageControl,
|
|
&pData) != 0)
|
|
{
|
|
ERROR_OUT(("Error reading Page Control Object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Extract details from the external page order
|
|
//
|
|
lLengthExternal = pData->length;
|
|
pPageOrderExternal = (PWB_PAGE_ORDER) pData->data;
|
|
pPageExternal = pPageOrderExternal->pages;
|
|
countPagesExternal = pPageOrderExternal->countPages;
|
|
|
|
//
|
|
// Process existing and newly added pages
|
|
//
|
|
for (indexExternal = 0; indexExternal < countPagesExternal; indexExternal++)
|
|
{
|
|
//
|
|
// Convert the index into the Page Control Object to an index into the
|
|
// internal Page List.
|
|
//
|
|
indexInternal = PAGE_WORKSET_ID_TO_INDEX(pPageExternal[indexExternal]);
|
|
|
|
//
|
|
// Test and update the internal page state as necessary
|
|
//
|
|
pPageState = &((m_pageStates)[indexInternal]);
|
|
|
|
//
|
|
// If the page is in use locally then we do not need to do anything
|
|
// (the external and internal page lists agree already).
|
|
//
|
|
if (pPageState->state != PAGE_IN_USE)
|
|
{
|
|
switch (pPageState->subState)
|
|
{
|
|
case PAGE_STATE_EMPTY:
|
|
//
|
|
// The page does not yet have a workset open for it - open one
|
|
// now. (But only open one per call to this routine to prevent
|
|
// swamping the message queue. The other outstanding opens will
|
|
// be done when this routine is next called).
|
|
//
|
|
wbPageWorksetOpen(PAGE_INDEX_TO_HANDLE(indexInternal),
|
|
OPEN_EXTERNAL);
|
|
|
|
//
|
|
// Leave now - this routine will be called again when the open
|
|
// confirm is received for the workset just opened.
|
|
//
|
|
DC_QUIT;
|
|
break;
|
|
|
|
case PAGE_STATE_LOCAL_OPEN_CONFIRM:
|
|
case PAGE_STATE_EXTERNAL_OPEN_CONFIRM:
|
|
case PAGE_STATE_EXTERNAL_ADD:
|
|
//
|
|
// Do nothing - the page is already in the add process
|
|
//
|
|
TRACE_OUT(("Page %d is already pending local add",
|
|
PAGE_INDEX_TO_HANDLE(indexInternal)));
|
|
break;
|
|
|
|
case PAGE_STATE_READY:
|
|
//
|
|
// The page workset has been opened previously - we can just mark
|
|
// the page as being in use immediately.
|
|
//
|
|
pPageState->state = PAGE_IN_USE;
|
|
pPageState->subState = PAGE_STATE_EMPTY;
|
|
TRACE_OUT(("Moved page %d state to PAGE_IN_USE",
|
|
(UINT) PAGE_INDEX_TO_HANDLE(indexInternal) ));
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad page substate %d", pPageState->subState));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark any pages that no longer appear in the Page Control Object as
|
|
// "delete pending" (unless they are already marked).
|
|
//
|
|
|
|
FillMemory(toBeMarked, sizeof(toBeMarked), TRUE);
|
|
|
|
//
|
|
// Flag which pages should be marked
|
|
//
|
|
for (indexExternal = 0; indexExternal < countPagesExternal; indexExternal++)
|
|
{
|
|
toBeMarked[PAGE_WORKSET_ID_TO_INDEX(pPageExternal[indexExternal])] = 0;
|
|
}
|
|
|
|
//
|
|
// Mark them
|
|
//
|
|
for (indexInternal = 0; indexInternal < WB_MAX_PAGES; indexInternal++)
|
|
{
|
|
pPageState = &((m_pageStates)[indexInternal]);
|
|
|
|
if ( (toBeMarked[indexInternal] == 1)
|
|
&& (pPageState->state == PAGE_IN_USE))
|
|
{
|
|
switch (pPageState->subState)
|
|
{
|
|
case PAGE_STATE_EMPTY:
|
|
//
|
|
// Ask the client for confirmation of the delete
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PAGE_DELETE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PAGE_DELETE_IND, // Page being deleted
|
|
PAGE_INDEX_TO_HANDLE(indexInternal), // Page handle
|
|
0);
|
|
|
|
//
|
|
// Update the page state
|
|
//
|
|
pPageState->subState = PAGE_STATE_EXTERNAL_DELETE_CONFIRM;
|
|
TRACE_OUT(("Moved page %d substate to PAGE_STATE_EXTERNAL_DELETE_CONFIRM",
|
|
(UINT) PAGE_INDEX_TO_HANDLE(indexInternal) ));
|
|
|
|
//
|
|
// Leave now - this routine will be called again when the delete
|
|
// confirm is received for this workset.
|
|
//
|
|
DC_QUIT;
|
|
break;
|
|
|
|
case PAGE_STATE_LOCAL_DELETE:
|
|
//
|
|
// Ask the client for confirmation of the delete
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PAGE_DELETE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PAGE_DELETE_IND, // Page being deleted
|
|
PAGE_INDEX_TO_HANDLE(indexInternal), // Page handle
|
|
0);
|
|
|
|
//
|
|
// Update the page state
|
|
//
|
|
pPageState->subState = PAGE_STATE_LOCAL_DELETE_CONFIRM;
|
|
TRACE_OUT(("Moved page %d substate to PAGE_STATE_LOCAL_DELETE_CONFIRM",
|
|
(UINT) PAGE_INDEX_TO_HANDLE(indexInternal) ));
|
|
|
|
//
|
|
// Leave now - this routine will be called again when the delete
|
|
// confirm is received for this workset.
|
|
//
|
|
DC_QUIT;
|
|
break;
|
|
|
|
case PAGE_STATE_EXTERNAL_DELETE:
|
|
case PAGE_STATE_EXTERNAL_DELETE_CONFIRM:
|
|
case PAGE_STATE_LOCAL_DELETE_CONFIRM:
|
|
//
|
|
// We are already expecting a delete for this page
|
|
//
|
|
TRACE_OUT(("Page %d is already pending local delete",
|
|
PAGE_INDEX_TO_HANDLE(indexInternal)));
|
|
DC_QUIT;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad page substate %d", pPageState->subState));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// There are no deletes or adds outstanding now
|
|
//
|
|
|
|
//
|
|
// Copy the new page order to the internal page list
|
|
//
|
|
memcpy(pPageOrderInternal, pPageOrderExternal, lLengthExternal);
|
|
|
|
//
|
|
// Inform the client of the change
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_PAGE_ORDER_UPDATED"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_PAGE_ORDER_UPDATED, // Event number
|
|
0, // No parameters
|
|
0);
|
|
|
|
//
|
|
// Check the number of pages ready in the cache
|
|
//
|
|
wbCheckReadyPages();
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// Release the Page Control Object
|
|
//
|
|
if (pData != NULL)
|
|
{
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjPageControl,
|
|
&pData);
|
|
}
|
|
|
|
DebugExitVOID(wbProcessPageControlChanges);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnSyncObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever the Sync Control object is
|
|
// replaced.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnSyncObjectReplaceInd(POM_OBJECT pObj)
|
|
{
|
|
POM_OBJECTDATA pSyncObject;
|
|
PWB_SYNC_CONTROL pSyncControl;
|
|
OM_OBJECT_ID syncPersonID;
|
|
|
|
DebugEntry(wbOnSyncControlReplaced);
|
|
|
|
//
|
|
// Confirm the replace of the object
|
|
//
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
pObj);
|
|
|
|
//
|
|
// Read the object and determine whether it was written by this client or
|
|
// another.
|
|
//
|
|
if (OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
m_pObjSyncControl,
|
|
&pSyncObject) != 0)
|
|
{
|
|
ERROR_OUT(("Error reading Sync Control Object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
pSyncControl = (PWB_SYNC_CONTROL) pSyncObject->data;
|
|
|
|
//
|
|
// Get the user ID from the object
|
|
//
|
|
syncPersonID = pSyncControl->personID;
|
|
|
|
//
|
|
// Release the Sync Control Object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
m_pObjSyncControl,
|
|
&pSyncObject);
|
|
pSyncControl = NULL;
|
|
|
|
//
|
|
// If the user ID in the object is not the ID of the current client, we
|
|
// must post a message to the front-end.
|
|
//
|
|
if (memcmp(&syncPersonID,
|
|
&(m_personID),
|
|
sizeof(syncPersonID)) != 0)
|
|
{
|
|
//
|
|
// Post a "sync position updated" event to the front-end
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_SYNC_POSITION_UPDATED"));
|
|
WBP_PostEvent(
|
|
0,
|
|
WBP_EVENT_SYNC_POSITION_UPDATED,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbOnSyncControlReplaced);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnGraphicObjectReplaceInd
|
|
//
|
|
// Purpose: This routine is called whenever an OM_OBJECT_REPLACE_IND is
|
|
// received for an object in a page workset.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbOnGraphicObjectReplaceInd
|
|
(
|
|
OM_WORKSET_ID worksetID,
|
|
POM_OBJECT pObj
|
|
)
|
|
{
|
|
WB_PAGE_HANDLE hPage = (WB_PAGE_HANDLE)worksetID;
|
|
BOOL bConfirm = FALSE;
|
|
|
|
DebugEntry(wbOnGraphicObjectReplaceInd);
|
|
|
|
//
|
|
// Record that the contents have changed
|
|
//
|
|
m_changed = TRUE;
|
|
TRACE_OUT(("Changed flag now TRUE"));
|
|
|
|
//
|
|
// These events are handled within the core until the client is ready.
|
|
//
|
|
if (m_state != STATE_IDLE)
|
|
{
|
|
TRACE_OUT(("Replace of graphic object before registration"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check that this page is actually in use
|
|
//
|
|
if (GetPageState(hPage)->state != PAGE_IN_USE)
|
|
{
|
|
TRACE_OUT(("Replace in page that is not in use"));
|
|
bConfirm = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check whether we are to pass the replace on to the client
|
|
//
|
|
if (bConfirm)
|
|
{
|
|
//
|
|
// Confirm the change to ObMan (cannot fail)
|
|
//
|
|
TRACE_OUT(("Confirming replace immediately"));
|
|
OM_ObjectReplaceConfirm(m_pomClient,
|
|
m_hWSGroup,
|
|
worksetID,
|
|
pObj);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Inform the client of the object being added
|
|
//
|
|
TRACE_OUT(("Posting WBP_EVENT_GRAPHIC_REPLACE_IND"));
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_GRAPHIC_REPLACE_IND, // Event type
|
|
hPage, // Page handle
|
|
(UINT_PTR)pObj); // Object handle
|
|
}
|
|
|
|
|
|
DebugExitVOID(wbOnGraphicObjectReplaceInd);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbWritePageControl
|
|
//
|
|
// Purpose: Write the page control information to the Page Control Workset
|
|
// from the copy held in client data. We write only those pages
|
|
// which are marked as being in use (and are not pending delete).
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbWritePageControl(BOOL create)
|
|
{
|
|
UINT result = 0;
|
|
UINT rc;
|
|
UINT index;
|
|
UINT length;
|
|
PWB_PAGE_ORDER pPageOrderInternal = &(m_pageOrder);
|
|
PWB_PAGE_ORDER pPageOrderExternal;
|
|
WB_PAGE_HANDLE hPage;
|
|
PWB_PAGE_STATE pPageState;
|
|
POM_OBJECT pObj;
|
|
POM_OBJECTDATA pData = NULL;
|
|
UINT generation;
|
|
|
|
DebugEntry(wbWritePageControl);
|
|
|
|
//
|
|
// Allocate memory for the object.
|
|
//
|
|
length = sizeof(WB_PAGE_ORDER)
|
|
- ( (WB_MAX_PAGES - pPageOrderInternal->countPages)
|
|
* sizeof(OM_WORKSET_ID));
|
|
|
|
if (OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
length,
|
|
&pData) != 0)
|
|
{
|
|
ERROR_OUT(("Error allocating object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData->length = length;
|
|
|
|
//
|
|
// Get a pointer to the page control object itself
|
|
//
|
|
pPageOrderExternal = (PWB_PAGE_ORDER) pData->data;
|
|
|
|
//
|
|
// Set the object type
|
|
//
|
|
pPageOrderExternal->objectType = TYPE_CONTROL_PAGE_ORDER;
|
|
|
|
//
|
|
// Increment the page list generation number indicating that we have
|
|
// written a new version of the page list.
|
|
//
|
|
generation = MAKELONG(pPageOrderInternal->generationLo,
|
|
pPageOrderInternal->generationHi);
|
|
generation++;
|
|
pPageOrderInternal->generationLo = LOWORD(generation);
|
|
pPageOrderInternal->generationHi = HIWORD(generation);
|
|
|
|
//
|
|
// Copy the page control data
|
|
//
|
|
pPageOrderExternal->objectType = TYPE_CONTROL_PAGE_ORDER;
|
|
pPageOrderExternal->generationLo = pPageOrderInternal->generationLo;
|
|
pPageOrderExternal->generationHi = pPageOrderInternal->generationHi;
|
|
pPageOrderExternal->countPages = 0;
|
|
|
|
//
|
|
// Loop through the internal page order finding the pages that are in
|
|
// use.
|
|
//
|
|
for (index = 0; index < pPageOrderInternal->countPages; index++)
|
|
{
|
|
//
|
|
// Get the handle of the next page
|
|
//
|
|
hPage = (pPageOrderInternal->pages)[index];
|
|
|
|
//
|
|
// Check the page state
|
|
//
|
|
pPageState = GetPageState(hPage);
|
|
if ( (pPageState->state == PAGE_IN_USE)
|
|
&& (pPageState->subState == PAGE_STATE_EMPTY))
|
|
{
|
|
//
|
|
// Add the page to the external list
|
|
//
|
|
(pPageOrderExternal->pages)[pPageOrderExternal->countPages] = hPage;
|
|
pPageOrderExternal->countPages++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We expect always to copy at least one page
|
|
//
|
|
ASSERT((pPageOrderExternal->countPages >= 1));
|
|
|
|
//
|
|
// Check whether we are creating or replacing the object
|
|
//
|
|
if (create)
|
|
{
|
|
//
|
|
// Add the object to the workset (we never update these objects, so the
|
|
// update length is set to 0).
|
|
//
|
|
rc = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&pData,
|
|
0,
|
|
&pObj,
|
|
LAST);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Replace the existing object
|
|
//
|
|
TRACE_OUT(("Replacing Page Control Object"));
|
|
rc = OM_ObjectReplace(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjPageControl,
|
|
&pData);
|
|
}
|
|
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// Discard the object - it was not used to replace the existing one
|
|
//
|
|
TRACE_OUT(("Adding Page Control Object"));
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&pData);
|
|
|
|
ERROR_OUT(("Error adding/replacing page control object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbWritePageControl, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCreateSyncControl
|
|
//
|
|
// Purpose: Create the sync control object
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
UINT WbClient::wbCreateSyncControl(void)
|
|
{
|
|
UINT result;
|
|
WB_SYNC sync;
|
|
|
|
DebugEntry(wbCreateSyncControl);
|
|
|
|
//
|
|
// Set the sync information to no page, empty rectangle
|
|
//
|
|
ZeroMemory(&sync, sizeof(WB_SYNC));
|
|
sync.length = WB_SYNC_SIZE;
|
|
sync.currentPage = WB_PAGE_HANDLE_NULL;
|
|
|
|
//
|
|
// Write the object
|
|
//
|
|
result = wbWriteSyncControl(&sync, TRUE);
|
|
|
|
DebugExitDWORD(wbCreateSyncControl, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbWriteSyncControl
|
|
//
|
|
// Purpose: Write the Sync Control object to the Page Control Workset
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbWriteSyncControl
|
|
(
|
|
PWB_SYNC pSync,
|
|
BOOL create
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
UINT rc;
|
|
POM_OBJECT pObj;
|
|
POM_OBJECTDATA pData = NULL;
|
|
PWB_SYNC_CONTROL pSyncControl;
|
|
|
|
DebugEntry(wbWriteSyncControl);
|
|
|
|
//
|
|
// Allocate memory for the object.
|
|
//
|
|
rc = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
WB_SYNC_CONTROL_SIZE,
|
|
&pData);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error allocating object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData->length = WB_SYNC_CONTROL_SIZE;
|
|
|
|
//
|
|
// Copy the sync control data from the client information
|
|
//
|
|
pSyncControl = (PWB_SYNC_CONTROL) pData->data;
|
|
pSyncControl->personID = m_personID;
|
|
memcpy(&(pSyncControl->sync), pSync, WB_SYNC_SIZE);
|
|
|
|
//
|
|
// Check whether we are creating or replacing the object
|
|
//
|
|
if (create)
|
|
{
|
|
//
|
|
// Add the object to the workset
|
|
//
|
|
rc = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
&pData,
|
|
WB_SYNC_CONTROL_SIZE,
|
|
&pObj,
|
|
LAST);
|
|
|
|
//
|
|
// If successful
|
|
//
|
|
if (rc == 0)
|
|
{
|
|
//
|
|
// Save the handle of the sync control object
|
|
//
|
|
m_pObjSyncControl = pObj;
|
|
|
|
//
|
|
// Make sure we do not discard the object below
|
|
//
|
|
pData = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Replace the existing object
|
|
//
|
|
rc = OM_ObjectReplace(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
m_pObjSyncControl,
|
|
&pData);
|
|
|
|
//
|
|
// Make sure we do not discard the object below
|
|
//
|
|
pData = NULL;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
//
|
|
// If we still have the Sync Control object - discard it
|
|
//
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Discard the object - it was not used to replace the existing one
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
SYNC_CONTROL_WORKSET,
|
|
&pData);
|
|
}
|
|
|
|
//
|
|
// If an error occurred during processing - report it
|
|
//
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error adding/replacing sync control object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
DebugExitDWORD(wbWriteSyncControl, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbSelectPersonColor
|
|
//
|
|
// Purpose: Select a color identifier for the local user
|
|
//
|
|
// Returns: Selected color
|
|
//
|
|
//
|
|
UINT WbClient::wbSelectPersonColor(void)
|
|
{
|
|
UINT count = 0;
|
|
UINT result;
|
|
POM_OBJECT pObjUser;
|
|
|
|
DebugEntry(wbSelectPersonColor);
|
|
|
|
//
|
|
// Select the color according to the order in the workset. See comments
|
|
// in wbCheckPersonColor for further details.
|
|
//
|
|
|
|
//
|
|
// start at the first object, search for the position of the local user's
|
|
// user object.
|
|
//
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
0,
|
|
&pObjUser,
|
|
FIRST);
|
|
while ((result == 0) && (pObjUser != m_pObjLocal))
|
|
{
|
|
count++;
|
|
result = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjUser,
|
|
&pObjUser,
|
|
AFTER);
|
|
|
|
}
|
|
|
|
if ((result != 0) && (result != OM_RC_NO_SUCH_OBJECT))
|
|
{
|
|
ERROR_OUT(("Unexpected return code from ObMan"));
|
|
}
|
|
|
|
DebugExitDWORD(wbSelectPersonColor, count);
|
|
return (count);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbCheckPersonColor
|
|
//
|
|
// Purpose: Check whether a new user has usurped our color. If so we must
|
|
// update our own color.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//
|
|
void WbClient::wbCheckPersonColor
|
|
(
|
|
POM_OBJECT hCheckObject
|
|
)
|
|
{
|
|
POM_OBJECTDATA pCheckObject = NULL;
|
|
PWB_PERSON pUser;
|
|
WB_PERSON user;
|
|
|
|
DebugEntry(wbCheckPersonColor);
|
|
|
|
//
|
|
// Read the new user information
|
|
//
|
|
if (OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
hCheckObject,
|
|
&pCheckObject) != 0)
|
|
{
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
pUser = (PWB_PERSON) pCheckObject->data;
|
|
|
|
//
|
|
// Compare the color identifier in the new user with that of the local
|
|
// user, if they are different there is nothing to do.
|
|
//
|
|
if (pUser->colorId == m_colorId)
|
|
{
|
|
TRACE_OUT(("New user has same color as local user = %d", pUser->colorId));
|
|
|
|
//
|
|
// The user color is determined by the order in the workset group of
|
|
// the user objects. The first user has color 0, the second color 1
|
|
// etc.
|
|
//
|
|
// When a user leaves the workset, however, the colors do not change.
|
|
//
|
|
// When a new user joins, it sets its color to its new position, and
|
|
// the others will then be forced to change accordingly. Whenever an
|
|
// object add or update is received where the new remote user color
|
|
// clashes with the local one, it is always the local user's job to
|
|
// change color, since the remote user has selected his new color
|
|
// according to his current position in the workset. The local user
|
|
// can't have the same position (since two users have two distinct user
|
|
// objects, so therefore must have the wrong color.
|
|
//
|
|
|
|
//
|
|
// Get the user object for the local user
|
|
//
|
|
if (wbPersonGet(m_pObjLocal, &user) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the color
|
|
//
|
|
TRACE_OUT(("Old color ID for local user is %d", user.colorId));
|
|
user.colorId = (TSHR_UINT16)wbSelectPersonColor();
|
|
TRACE_OUT(("New color ID for local user is %d", user.colorId));
|
|
|
|
//
|
|
// Copy the person's color into the client's data
|
|
//
|
|
m_colorId = user.colorId;
|
|
|
|
//
|
|
// Write the new user information back
|
|
//
|
|
if (wbPersonUpdate(&user) != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
|
|
//
|
|
// If an object has been read, release it now
|
|
//
|
|
if (pCheckObject != NULL)
|
|
{
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
hCheckObject,
|
|
&pCheckObject);
|
|
}
|
|
|
|
DebugExitVOID(wbCheckPersonColor);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbWriteLock
|
|
//
|
|
// Purpose: Add a lock object to the Page Control Workset
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbWriteLock(void)
|
|
{
|
|
UINT result;
|
|
POM_OBJECTDATA pData;
|
|
PWB_LOCK pLock;
|
|
POM_OBJECT pObj;
|
|
|
|
DebugEntry(wbWriteLock);
|
|
|
|
//
|
|
// Create a lock object
|
|
//
|
|
result = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
sizeof(WB_LOCK),
|
|
&pData);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("Unable to allocate lock object = %d", result));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
pData->length = sizeof(WB_LOCK);
|
|
|
|
//
|
|
// Set the lock object fields
|
|
//
|
|
pLock = (PWB_LOCK) pData->data;
|
|
pLock->objectType = TYPE_CONTROL_LOCK;
|
|
pLock->personID = m_personID;
|
|
pLock->lockType = m_lockRequestType;
|
|
|
|
//
|
|
// If we already have the lock, then we can just replace the object
|
|
//
|
|
if (m_pObjLock == NULL)
|
|
{
|
|
//
|
|
// Add the lock object to the Workset. The Add indication received
|
|
// by the remote users signals the presence of the lock to them.
|
|
//
|
|
result = OM_ObjectAdd(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&pData,
|
|
sizeof(WB_LOCK),
|
|
&pObj,
|
|
LAST);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Replace the existing object
|
|
//
|
|
result = OM_ObjectReplace(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
m_pObjLock,
|
|
&pData);
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
//
|
|
// The add or replace failed, we must discard the object
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&pData);
|
|
|
|
ERROR_OUT(("Error adding user object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save the handle of the lock object
|
|
//
|
|
TRACE_OUT(("Lock handle was %x, now %x", m_pObjLock, pObj));
|
|
m_pObjLock = pObj;
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbWriteLock, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbReadLock
|
|
//
|
|
// Purpose: Update the lock information stored in the client data after a
|
|
// change in the Lock Object.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbReadLock(void)
|
|
{
|
|
UINT count = 0;
|
|
|
|
DebugEntry(wbReadLock);
|
|
|
|
//
|
|
// Before we read the lock information we need to ensure that the
|
|
// PAGE_CONTROL_WORKSET and the USER_INFORMATION_WORKSET both contain
|
|
// the objects we need. If either of the objects are missing, quit and
|
|
// wait until we are called again - this function will be called
|
|
// whenever new objects are added to these worksets.
|
|
//
|
|
OM_WorksetCountObjects(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&count);
|
|
TRACE_OUT(("%d objects in USER_INFORMATION_WORKSET", count));
|
|
if (count == 0)
|
|
{
|
|
TRACE_OUT(("Need to wait for USER_INFO object"));
|
|
DC_QUIT;
|
|
}
|
|
OM_WorksetCountObjects(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
&count);
|
|
TRACE_OUT(("%d objects in PAGE_CONTROL_WORKSET", count));
|
|
if (count == 0)
|
|
{
|
|
TRACE_OUT(("Need to wait for PAGE_CONTROL object"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
TRACE_OUT(("Process lock"));
|
|
wbProcessLockNotification();
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbReadLock);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbProcessLockNotification
|
|
//
|
|
// Purpose:
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbProcessLockNotification(void)
|
|
{
|
|
UINT rc = 0;
|
|
POM_OBJECTDATA pData;
|
|
PWB_LOCK pLock;
|
|
WB_LOCK_TYPE lockType;
|
|
POM_OBJECT pObjPersonLock;
|
|
POM_OBJECT pObj;
|
|
POM_OBJECT pObjLock;
|
|
UINT objectType = 0;
|
|
|
|
DebugEntry(wbProcessLockNotification);
|
|
|
|
//
|
|
// Get the handle of the lock object. We use the last object in the
|
|
// workset to protect against lock objects being left lying around.
|
|
//
|
|
rc = OM_ObjectH(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
0,
|
|
&pObj,
|
|
LAST);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error getting lock object handle = %d", rc));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Check that this is the CONTROL_LOCK object. Quit if it isnt - we
|
|
// will be called again later when the object arrices.
|
|
//
|
|
rc = wbGetPageObjectType(pObj, &objectType);
|
|
if (rc != 0)
|
|
{
|
|
DC_QUIT;
|
|
}
|
|
if (objectType != TYPE_CONTROL_LOCK)
|
|
{
|
|
TRACE_OUT(("not LOCK control object - quit"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Save the handle of the lock object
|
|
//
|
|
pObjLock = pObj;
|
|
|
|
//
|
|
// Read the object
|
|
//
|
|
rc = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj,
|
|
&pData);
|
|
if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error reading lock object %d", rc));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
pLock = (PWB_LOCK) &(pData->data);
|
|
|
|
//
|
|
// Save the lock details
|
|
//
|
|
lockType = (WB_LOCK_TYPE)pLock->lockType;
|
|
TRACE_OUT(("Lock type %d", lockType));
|
|
|
|
//
|
|
// Convert the object ID held in the PAGE_CONTROL workset to an object
|
|
// handle.
|
|
//
|
|
rc = OM_ObjectIDToPtr(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pLock->personID,
|
|
&pObjPersonLock);
|
|
|
|
//
|
|
// The return code is checked after the object release to ensure that
|
|
// the object is not held and read again.
|
|
//
|
|
|
|
//
|
|
// Release the lock object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
PAGE_CONTROL_WORKSET,
|
|
pObj,
|
|
&pData);
|
|
|
|
//
|
|
// Check the return code from the ID to Handle call
|
|
//
|
|
if (rc == OM_RC_BAD_OBJECT_ID)
|
|
{
|
|
WARNING_OUT(("Unknown ID - wait for next add of user object"));
|
|
DC_QUIT;
|
|
}
|
|
else if (rc != 0)
|
|
{
|
|
ERROR_OUT(("Error (%d) converting lock user ID to handle", rc));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Validate the lock state and details
|
|
//
|
|
switch (m_lockState)
|
|
{
|
|
//
|
|
// In this state we do not actually have the lock, but are waiting
|
|
// for confirmation of an earlier workset-lock request. In this
|
|
// case, we let the front end know that the lock request has failed
|
|
// before sending indication of the lock by the other user.
|
|
//
|
|
case LOCK_STATE_PENDING_LOCK:
|
|
ASSERT((pObjPersonLock != m_pObjLocal));
|
|
|
|
m_lockState = LOCK_STATE_LOCKED_OUT;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_LOCKED_OUT"));
|
|
|
|
WBP_PostEvent(
|
|
0, // No delay
|
|
WBP_EVENT_LOCK_FAILED, // Lock request failed
|
|
0, // No parameters
|
|
0);
|
|
break;
|
|
|
|
//
|
|
// In these states we do not have a lock - this must be a new lock
|
|
// from remote user or an update to an old lock.
|
|
//
|
|
case LOCK_STATE_EMPTY:
|
|
case LOCK_STATE_LOCKED_OUT:
|
|
ASSERT((pObjPersonLock != m_pObjLocal));
|
|
|
|
//
|
|
// Update the lock state to show that we are now locked out
|
|
//
|
|
m_lockState = LOCK_STATE_LOCKED_OUT;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_LOCKED_OUT"));
|
|
break;
|
|
|
|
//
|
|
// In these states we have the lock (or are expecting to get it)
|
|
//
|
|
case LOCK_STATE_GOT_LOCK:
|
|
case LOCK_STATE_PENDING_ADD:
|
|
ASSERT((pObjPersonLock == m_pObjLocal));
|
|
|
|
//
|
|
// Update the lock state to show that we are now locked out
|
|
//
|
|
m_lockState = LOCK_STATE_GOT_LOCK;
|
|
TRACE_OUT(("Moved lock state to LOCK_STATE_GOT_LOCK"));
|
|
break;
|
|
|
|
//
|
|
// The lock request has been cancelled - unlock the WS.
|
|
//
|
|
case LOCK_STATE_CANCEL_LOCK:
|
|
break;
|
|
|
|
//
|
|
// In any other state we are not expecting any lock
|
|
//
|
|
default:
|
|
ERROR_OUT(("Not expecting lock object add"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Save the lock details
|
|
//
|
|
TRACE_OUT(("Lock handle was %x, now %x",
|
|
m_pObjLock, pObjLock));
|
|
m_pObjLock = pObjLock;
|
|
m_lockType = lockType;
|
|
m_pObjPersonLock = pObjPersonLock;
|
|
|
|
//
|
|
// If the lock has subsequently been cancelled, unlock the WS.
|
|
//
|
|
if (m_lockState == LOCK_STATE_CANCEL_LOCK)
|
|
{
|
|
TRACE_OUT(("Cancel lock"));
|
|
m_lockState = LOCK_STATE_GOT_LOCK;
|
|
wbUnlock();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Inform the client of the lock. The notification will be trapped
|
|
// by the core if the client is not fully registered.
|
|
//
|
|
wbSendLockNotification();
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitVOID(wbProcessLockNotification);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbSendLockNotification
|
|
//
|
|
// Purpose: Post a lock notification to the client. The lock information
|
|
// held in the client memory must be up to date when this function
|
|
// is called.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
void WbClient::wbSendLockNotification(void)
|
|
{
|
|
UINT result = 0;
|
|
UINT lockEvent;
|
|
|
|
DebugEntry(wbSendLockNotification);
|
|
|
|
//
|
|
// Check that we are in a valid state for sending a lock notification
|
|
//
|
|
if ( (m_lockState == LOCK_STATE_GOT_LOCK)
|
|
|| (m_lockState == LOCK_STATE_LOCKED_OUT)
|
|
|| (m_lockState == LOCK_STATE_EMPTY))
|
|
{
|
|
//
|
|
// Verify the lock type
|
|
//
|
|
switch (m_lockType)
|
|
{
|
|
case WB_LOCK_TYPE_CONTENTS:
|
|
TRACE_OUT(("Posting WBP_EVENT_CONTENTS_LOCKED"));
|
|
lockEvent = WBP_EVENT_CONTENTS_LOCKED;
|
|
break;
|
|
|
|
case WB_LOCK_TYPE_PAGE_ORDER:
|
|
TRACE_OUT(("Posting WBP_EVENT_PAGE_ORDER_LOCKED"));
|
|
lockEvent = WBP_EVENT_PAGE_ORDER_LOCKED;
|
|
break;
|
|
|
|
case WB_LOCK_TYPE_NONE:
|
|
TRACE_OUT(("Posting WBP_EVENT_UNLOCKED"));
|
|
lockEvent = WBP_EVENT_UNLOCKED;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad lock type %d", (UINT) m_lockType));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Tell the client that the lock has been acquired or released
|
|
//
|
|
WBP_PostEvent(
|
|
0,
|
|
lockEvent,
|
|
0,
|
|
(UINT_PTR)m_pObjPersonLock);
|
|
|
|
TRACE_OUT(("Sent lock notification"));
|
|
}
|
|
|
|
DebugExitVOID(wbSendLockNotification);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWBPLock
|
|
//
|
|
// Purpose: Process a successful lock acquisitoin
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWBPLock(void)
|
|
{
|
|
BOOL processed = TRUE;
|
|
|
|
DebugEntry(wbOnWBPLock);
|
|
|
|
//
|
|
// If we are registering and have just acquired the lock - we can now
|
|
// continue the registration process.
|
|
//
|
|
|
|
//
|
|
// Test the current state
|
|
//
|
|
switch (m_state)
|
|
{
|
|
//
|
|
// We are waiting for registration to continue
|
|
//
|
|
case STATE_REGISTERING:
|
|
//
|
|
// Act on the registration substate
|
|
//
|
|
if (m_subState == STATE_REG_PENDING_LOCK)
|
|
{
|
|
//
|
|
// Check that it is us who now has the lock
|
|
//
|
|
if (m_lockState != LOCK_STATE_GOT_LOCK)
|
|
{
|
|
TRACE_OUT(("It is not us who has the lock"));
|
|
|
|
//
|
|
// Another client has acquired the lock - we must wait for them
|
|
// to add the Page Control Object.
|
|
//
|
|
m_subState = STATE_REG_PENDING_PAGE_CONTROL;
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_PAGE_CONTROL"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We now have the Page Control Workset locked - check for the
|
|
// existence of the Page Control and Sync Control objects. (We
|
|
// have to do this because another client could have locked the
|
|
// workset, added the objects and unlocked the workset just before
|
|
// we requested the lock. The Page Control Object may not have
|
|
// reached us before we requested the lock. Now that we have the
|
|
// lock we are guaranteed to have all objects in the workset so the
|
|
// object add events may have arrived just before the lock
|
|
// confirmation.
|
|
//
|
|
if ( (m_pObjPageControl != 0)
|
|
&& (m_pObjSyncControl != 0))
|
|
{
|
|
//
|
|
// Unlock the workset
|
|
//
|
|
wbUnlock();
|
|
|
|
//
|
|
// Wait for the unlock to complete
|
|
//
|
|
m_subState = STATE_REG_PENDING_UNLOCK;
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_UNLOCK"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We are the first in the call - we must add the Page Control
|
|
// Object. (It is possible that another client added the Page
|
|
// Control object and then failed. To cover this we check
|
|
// separately for the Page Control and Sync objects.)
|
|
//
|
|
if (m_pObjPageControl == 0)
|
|
{
|
|
//
|
|
// Add a single page to the page control object using the first
|
|
// page workset (which we always open).
|
|
//
|
|
wbPagesPageAdd(0, FIRST_PAGE_WORKSET,
|
|
PAGE_FIRST);
|
|
|
|
//
|
|
// Write the Page Control information
|
|
//
|
|
if (wbWritePageControl(TRUE) != 0)
|
|
{
|
|
ERROR_OUT(("Error adding Page Control Object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Update the state to "waiting for Page Control"
|
|
//
|
|
m_subState = STATE_REG_PENDING_PAGE_CONTROL;
|
|
TRACE_OUT(("Moved to substate STATE_REG_PENDING_PAGE_CONTROL"));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// The Page Control object is there, so the Sync Control object
|
|
// must not be (we checked above for both being there and would
|
|
// have exited by now if they were).
|
|
//
|
|
ASSERT((m_pObjSyncControl == 0));
|
|
|
|
//
|
|
// Create the Sync Control Object.
|
|
//
|
|
if (wbCreateSyncControl() != 0)
|
|
{
|
|
ERROR_OUT(("Error adding Sync Control Object"));
|
|
wbError();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Wait for the Sync Control object to be added
|
|
//
|
|
m_subState = STATE_REG_PENDING_SYNC_CONTROL;
|
|
TRACE_OUT(("Moved substate to STATE_REG_PENDING_SYNC_CONTROL"));
|
|
DC_QUIT;
|
|
}
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
//
|
|
// We are fully registered. The event must be passed on to the
|
|
// front-end
|
|
//
|
|
processed = FALSE;
|
|
break;
|
|
|
|
//
|
|
// We are in an unknown state
|
|
//
|
|
default:
|
|
ERROR_OUT(("Bad client major state"));
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWBPLock, processed);
|
|
return(processed);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWBPLockFailed
|
|
//
|
|
// Purpose: Process a failed lock acquisition
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWBPLockFailed(void)
|
|
{
|
|
BOOL processed = TRUE;
|
|
|
|
DebugEntry(wbOnWBPLockFailed);
|
|
|
|
//
|
|
// Check the current state
|
|
//
|
|
switch (m_state)
|
|
{
|
|
case STATE_REGISTERING:
|
|
//
|
|
// If we are registering and have just failed to acquire the lock -
|
|
// this is because another user has the lock. If both the page and
|
|
// sync objects have been added, finish registration, otherwise wait
|
|
// for them to be added.
|
|
//
|
|
if ( (m_pObjPageControl != 0) &&
|
|
(m_pObjSyncControl != 0))
|
|
{
|
|
TRACE_OUT(("Page Control and Sync Control objects both there."));
|
|
TRACE_OUT(("Registration can be completed"));
|
|
wbOnControlWorksetsReady();
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (m_pObjPageControl == 0)
|
|
{
|
|
TRACE_OUT(("Waiting for page control"));
|
|
m_subState = STATE_REG_PENDING_PAGE_CONTROL;
|
|
DC_QUIT;
|
|
}
|
|
|
|
if (m_pObjSyncControl == 0)
|
|
{
|
|
TRACE_OUT(("Waiting for sync control"));
|
|
m_subState = STATE_REG_PENDING_SYNC_CONTROL;
|
|
DC_QUIT;
|
|
}
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
//
|
|
// We are fully registered. The event must be passed on to the
|
|
// front-end
|
|
//
|
|
processed = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad main state"));
|
|
break;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWBPLockFailed, processed);
|
|
return(processed);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWBPUnlocked
|
|
//
|
|
// Purpose: Process an unlock notification
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWBPUnlocked(void)
|
|
{
|
|
BOOL processed = TRUE;
|
|
|
|
DebugEntry(wbOnWBPUnlocked);
|
|
|
|
//
|
|
// If we are registering and waiting to unlock the Page Control Workset
|
|
// we must complete registration here.
|
|
//
|
|
|
|
//
|
|
// Check the current state
|
|
//
|
|
switch (m_state)
|
|
{
|
|
case STATE_REGISTERING:
|
|
//
|
|
// Check whether we are expecting his event
|
|
//
|
|
if(m_subState == STATE_REG_PENDING_UNLOCK)
|
|
{
|
|
//
|
|
// Continue the registration process
|
|
//
|
|
wbOnControlWorksetsReady();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We were not expecting the unlock event
|
|
//
|
|
WARNING_OUT(("Unexpected unlock event"));
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
//
|
|
// We are fully registered. The event must be passed on to the
|
|
// front-end
|
|
//
|
|
processed = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT(("Bad main state"));
|
|
break;
|
|
} // Switch on client state
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWBPUnlocked, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbOnWBPPageOrderUpdated
|
|
//
|
|
// Purpose: Process a page order updated notification
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
BOOL WbClient::wbOnWBPPageOrderUpdated(void)
|
|
{
|
|
BOOL processed = FALSE;
|
|
|
|
DebugEntry(wbOnWBPPageOrderUpdated);
|
|
|
|
//
|
|
// If we are registering and waiting for the Page Order to be brought
|
|
// up-to-date we can now continue registration.
|
|
//
|
|
if (m_state == STATE_REGISTERING)
|
|
{
|
|
//
|
|
// Show that we have processed the event (we do not want to pass it on
|
|
// to the client, they are not yet fully registered and will not be
|
|
// expecting it).
|
|
//
|
|
processed = TRUE;
|
|
|
|
//
|
|
// If we have enough pages ready in the cache, we have completed
|
|
// registration. (Otherwise the call to CheckReadyPages will open
|
|
// another page and will complete registration later.)
|
|
//
|
|
if (wbCheckReadyPages())
|
|
{
|
|
wbCompleteRegistration();
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// We must wait for sufficiently many pages to be ready
|
|
//
|
|
m_subState = STATE_REG_PENDING_READY_PAGES;
|
|
TRACE_OUT(("Moved substate to STATE_REG_PENDING_READY_PAGES"));
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitBOOL(wbOnWBPPageOrderUpdated, processed);
|
|
return(processed);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPersonGet
|
|
//
|
|
// Purpose: Get user details
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPersonGet
|
|
(
|
|
POM_OBJECT pObjUser,
|
|
PWB_PERSON pUser
|
|
)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pUserObject;
|
|
|
|
DebugEntry(wbPersonGet);
|
|
|
|
if (pObjUser == m_pObjLocal)
|
|
{
|
|
TRACE_OUT(("Call is for local user details"));
|
|
}
|
|
|
|
//
|
|
// Read the object.
|
|
//
|
|
result = OM_ObjectRead(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjUser,
|
|
&pUserObject);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectRead = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Copy the read user object into the buffer passed
|
|
//
|
|
memcpy(pUser, pUserObject->data, sizeof(WB_PERSON));
|
|
TRACE_OUT(("CMG personID %u", pUser->cmgPersonID));
|
|
|
|
//
|
|
// Release the object
|
|
//
|
|
OM_ObjectRelease(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
pObjUser,
|
|
&pUserObject);
|
|
|
|
//
|
|
// If the call is for the local user, update the color field to ensure it
|
|
// doesn't get overwritten in a race condition with the front-end (i.e.
|
|
// the front end tries to update the user before the color-change event
|
|
// has been received). The core "knows better" than ObMan what the local
|
|
// user's color is. This is safe because the color field is only ever
|
|
// changed locally.
|
|
//
|
|
if (pObjUser == m_pObjLocal)
|
|
{
|
|
pUser->colorId = (TSHR_UINT16)m_colorId;
|
|
}
|
|
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPersonGet, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbPersonUpdate
|
|
//
|
|
// Purpose: Update the local user object - this is only used by the CORE -
|
|
// the front-end calls WBP_SetPersonData, which does a _replace_.
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
//
|
|
UINT WbClient::wbPersonUpdate(PWB_PERSON pUser)
|
|
{
|
|
UINT result = 0;
|
|
POM_OBJECTDATA pUserObject;
|
|
|
|
DebugEntry(wbPersonUpdate);
|
|
|
|
//
|
|
// Allocate a user object
|
|
//
|
|
result = OM_ObjectAlloc(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
sizeof(WB_PERSON),
|
|
&pUserObject);
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectAlloc = %d", result));
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Set the length of the object
|
|
//
|
|
pUserObject->length = WB_PERSON_OBJECT_UPDATE_SIZE,
|
|
|
|
//
|
|
// Copy the user information into the ObMan object
|
|
//
|
|
memcpy(pUserObject->data, pUser, sizeof(WB_PERSON));
|
|
|
|
//
|
|
// Update the object
|
|
//
|
|
result = OM_ObjectUpdate(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
m_pObjLocal,
|
|
&pUserObject);
|
|
|
|
if (result != 0)
|
|
{
|
|
ERROR_OUT(("OM_ObjectUpdate = %d", result));
|
|
|
|
//
|
|
// Discard the object
|
|
//
|
|
OM_ObjectDiscard(m_pomClient,
|
|
m_hWSGroup,
|
|
USER_INFORMATION_WORKSET,
|
|
&pUserObject);
|
|
|
|
DC_QUIT;
|
|
}
|
|
|
|
//
|
|
// Note that the object has not yet been updated. An
|
|
// OM_OBJECT_UPDATE_IND event will be generated.
|
|
//
|
|
|
|
DC_EXIT_POINT:
|
|
DebugExitDWORD(wbPersonUpdate, result);
|
|
return(result);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Name: wbGetNetUserID().
|
|
//
|
|
// Purpose: Get the network user ID for this client
|
|
//
|
|
// Returns:
|
|
//
|
|
//
|
|
BOOL WbClient::wbGetNetUserID(void)
|
|
{
|
|
BOOL result = TRUE;
|
|
UINT rc = 0;
|
|
|
|
DebugEntry(wbGetNetUserID);
|
|
|
|
rc = OM_GetNetworkUserID(m_pomClient,
|
|
m_hWSGroup,
|
|
&m_clientNetID);
|
|
if (rc != 0)
|
|
{
|
|
if (rc == OM_RC_LOCAL_WSGROUP)
|
|
{
|
|
m_clientNetID = 0;
|
|
}
|
|
else
|
|
{
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
DebugExitBOOL(wbGetNetUserID, result);
|
|
return(result);
|
|
}
|