Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1038 lines
27 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1998 - 1999
//
// File: treedat_.cpp
//
//--------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
// Miscellanea
LPCWSTR g_lpszNullString = L"\0";
///////////////////////////////////////////////////////////////////////
// Global Helper functions
BOOL LoadContextMenuResources(MENUMAP* pMenuMap)
{
HINSTANCE hInstance = _Module.GetModuleInstance();
for (int i = 0; pMenuMap->ctxMenu[i].strName; i++)
{
if (0 == ::LoadString(hInstance, pMenuMap->dataRes[i].uResID, pMenuMap->dataRes[i].szBuffer, MAX_CONTEXT_MENU_STRLEN*2))
return FALSE;
pMenuMap->ctxMenu[i].strName = pMenuMap->dataRes[i].szBuffer;
for (WCHAR* pCh = pMenuMap->dataRes[i].szBuffer; (*pCh) != NULL; pCh++)
{
if ( (*pCh) == L'\n')
{
pMenuMap->ctxMenu[i].strStatusBarText = (pCh+1);
(*pCh) = NULL;
break;
}
}
}
return TRUE;
}
BOOL LoadResultHeaderResources(RESULT_HEADERMAP* pHeaderMap, int nCols)
{
HINSTANCE hInstance = _Module.GetModuleInstance();
for ( int i = 0; i < nCols ; i++)
{
if ( 0 == ::LoadString(hInstance, pHeaderMap[i].uResID, pHeaderMap[i].szBuffer, MAX_RESULT_HEADER_STRLEN))
return TRUE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////
// CTreeNode
BEGIN_TOOLBAR_MAP(CTreeNode)
END_TOOLBAR_MAP()
BOOL CTreeNode::HasContainer(CContainerNode* pContainerNode)
{
if (m_pContainer == NULL)
return FALSE; // root
if (m_pContainer == pContainerNode)
return TRUE; // got it
return m_pContainer->HasContainer(pContainerNode);
}
HRESULT CTreeNode::GetResultViewType(CComponentDataObject* pComponentData,
LPOLESTR* ppViewType,
long* pViewOptions)
{
if (pComponentData->IsMultiSelect())
{
*pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT;
}
else
{
*pViewOptions = MMC_VIEW_OPTIONS_NONE;
}
*ppViewType = NULL;
return S_FALSE;
}
void CTreeNode::Show(BOOL bShow, CComponentDataObject* pComponentData)
{
if (bShow)
{
ASSERT(m_dwNodeFlags & TN_FLAG_HIDDEN); // must be currently hidden
SetFlagsDown(TN_FLAG_HIDDEN,FALSE); // mark it visible
VERIFY(SUCCEEDED(pComponentData->AddNode(this)));
}
else
{
ASSERT(!(m_dwNodeFlags & TN_FLAG_HIDDEN)); // must be currently visible
SetFlagsDown(TN_FLAG_HIDDEN,TRUE); // mark it hidden
VERIFY(SUCCEEDED(pComponentData->DeleteNode(this)));
if (IsContainer())
{
((CContainerNode*)this)->RemoveAllChildrenFromList();
((CContainerNode*)this)->MarkEnumerated(FALSE);
}
}
}
void CTreeNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet)
{
if (bSet)
m_dwNodeFlags |= dwNodeFlags;
else
m_dwNodeFlags &= ~dwNodeFlags;
}
void CTreeNode::SetFlagsUp(DWORD dwNodeFlags, BOOL bSet)
{
if (bSet)
m_dwNodeFlags |= dwNodeFlags;
else
m_dwNodeFlags &= ~dwNodeFlags;
if (m_pContainer != NULL)
{
ASSERT(m_pContainer != this);
m_pContainer->SetFlagsUp(dwNodeFlags, bSet);
}
}
//
// Property Page methods
//
void CTreeNode::ShowPageForNode(CComponentDataObject* pComponentDataObject)
{
ASSERT(pComponentDataObject != NULL);
pComponentDataObject->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1);
}
BOOL CTreeNode::HasPropertyPages(DATA_OBJECT_TYPES,
BOOL* pbHideVerb,
CNodeList*)
{
*pbHideVerb = TRUE;
return FALSE;
}
//
// Menu Item methods
//
HRESULT CTreeNode::OnAddMenuItems(IContextMenuCallback2* pContextMenuCallback2,
DATA_OBJECT_TYPES type,
long *pInsertionAllowed,
CNodeList* pNodeList)
{
HRESULT hr = S_OK;
LPCONTEXTMENUITEM2 pContextMenuItem = NULL;
if (pNodeList->GetCount() == 1) // single selection
{
pContextMenuItem = OnGetContextMenuItemTable();
if (pContextMenuItem == NULL)
return hr;
//
// Loop through and add each of the menu items
//
for (LPCONTEXTMENUITEM2 m = pContextMenuItem; m->strName; m++)
{
if (
( (*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) &&
(m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_NEW) ) ||
( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) &&
(m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TASK) ) ||
( (*pInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) &&
(m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_VIEW) ) ||
( (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP) &&
(m->lInsertionPointID == CCM_INSERTIONPOINTID_PRIMARY_TOP) )
)
{
// make a temporary copy that can be modified
CONTEXTMENUITEM2 tempItem;
::memcpy(&tempItem, m, sizeof(CONTEXTMENUITEM2));
if (OnAddMenuItem(&tempItem, pInsertionAllowed))
{
hr = pContextMenuCallback2->AddItem(&tempItem);
if (FAILED(hr))
break;
}
}
}
}
else if (pNodeList->GetCount() > 1) // multiple selection
{
hr = OnAddMenuItemsMultipleSelect(pContextMenuCallback2,
type,
pInsertionAllowed,
pNodeList);
}
return hr;
}
BOOL CTreeNode::OnSetRenameVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetDeleteVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetRefreshVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetCutVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetCopyVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetPasteVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
BOOL CTreeNode::OnSetPrintVerbState(DATA_OBJECT_TYPES,
BOOL* pbHide,
CNodeList*)
{
*pbHide = TRUE;
return FALSE;
}
MMC_CONSOLE_VERB CTreeNode::GetDefaultVerb(DATA_OBJECT_TYPES type,
CNodeList* pNodeList)
{
ASSERT((type == CCT_SCOPE) || (type == CCT_RESULT));
if (type == CCT_SCOPE)
return MMC_VERB_OPEN;
BOOL bHideVerbDummy;
if (HasPropertyPages(type, &bHideVerbDummy, pNodeList))
return MMC_VERB_PROPERTIES;
return MMC_VERB_NONE;
}
void CTreeNode::OnSetVerbState(LPCONSOLEVERB pConsoleVerb,
DATA_OBJECT_TYPES type,
CNodeList* pNodeList)
{
//
// Use the virtual functions to get the verb state
//
BOOL bHideCut;
BOOL bCanCut = OnSetCutVerbState(type, &bHideCut, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_CUT, HIDDEN, bHideCut);
pConsoleVerb->SetVerbState(MMC_VERB_CUT, ENABLED, bCanCut);
BOOL bHideCopy;
BOOL bCanCopy = OnSetCopyVerbState(type, &bHideCopy, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_COPY, HIDDEN, bHideCopy);
pConsoleVerb->SetVerbState(MMC_VERB_COPY, ENABLED, bCanCopy);
BOOL bHidePaste;
BOOL bCanPaste = OnSetPasteVerbState(type, &bHidePaste, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_PASTE, HIDDEN, bHidePaste);
pConsoleVerb->SetVerbState(MMC_VERB_PASTE, ENABLED, bCanPaste);
BOOL bHidePrint;
BOOL bCanPrint = OnSetPrintVerbState(type, &bHidePrint, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_PRINT, HIDDEN, bHidePrint);
pConsoleVerb->SetVerbState(MMC_VERB_PRINT, ENABLED, bCanPrint);
BOOL bHideRename;
BOOL bCanRename = OnSetRenameVerbState(type, &bHideRename, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_RENAME, HIDDEN, bHideRename);
pConsoleVerb->SetVerbState(MMC_VERB_RENAME, ENABLED, bCanRename);
// MMC_VERB_PROPERTIES
BOOL bHideProperties;
BOOL bHasProperties = HasPropertyPages(type, &bHideProperties, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, bHasProperties);
pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, bHideProperties);
// MMC_VERB_DELETE
BOOL bHideDelete;
BOOL bCanDelete = OnSetDeleteVerbState(type, &bHideDelete, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, bCanDelete);
pConsoleVerb->SetVerbState(MMC_VERB_DELETE, HIDDEN, bHideDelete);
// MMC_VERB_REFRESH
BOOL bHideRefresh;
BOOL bCanRefresh = OnSetRefreshVerbState(type, &bHideRefresh, pNodeList);
pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, bCanRefresh);
pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, HIDDEN, bHideRefresh);
}
HRESULT CTreeNode::OnSetToolbarVerbState(IToolbar*,
CNodeList*)
{
HRESULT hr = S_OK;
//
// Set the button state for each button on the toolbar using
// hr = pToolbar->SetButtonState(event, MMC_BUTTON_STATE, bState);
//
return hr;
}
void CTreeNode::DeleteHelper(CComponentDataObject* pComponentData)
{
ASSERT(pComponentData != NULL);
ASSERT(m_pContainer != NULL);
ASSERT((CTreeNode*)m_pContainer != this);
CContainerNode* pCont = m_pContainer;
VERIFY(m_pContainer->RemoveChildFromList(this));
ASSERT(m_pContainer == NULL);
m_pContainer = pCont; // not in the container's list of children, but still needed
// remove from UI only if the container is visible
if (pCont->IsVisible())
VERIFY(SUCCEEDED(pComponentData->DeleteNode(this))); // remove from the UI
}
void CTreeNode::IncrementSheetLockCount()
{
++m_nSheetLockCount;
if (m_pContainer != NULL)
m_pContainer->IncrementSheetLockCount();
}
void CTreeNode::DecrementSheetLockCount()
{
--m_nSheetLockCount;
if (m_pContainer != NULL)
m_pContainer->DecrementSheetLockCount();
}
void CTreeNode::OnPropertyChange(CComponentDataObject* pComponentData,
BOOL, long changeMask)
{
// function called when the PPHolder successfully updated the node
ASSERT(pComponentData != NULL);
VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, changeMask)));
}
void CTreeNode::OnCreateSheet()
{
++m_nSheetCount;
IncrementSheetLockCount();
SetFlagsUp(TN_FLAG_HAS_SHEET, TRUE);
}
void CTreeNode::OnDeleteSheet()
{
DecrementSheetLockCount();
--m_nSheetCount;
SetFlagsUp(TN_FLAG_HAS_SHEET,FALSE);
}
////////////////////////////////////////////////////////////////////////
// CContainerNode
void CContainerNode::IncrementThreadLockCount()
{
++m_nThreadLockCount;
if (m_pContainer != NULL)
m_pContainer->IncrementThreadLockCount();
}
void CContainerNode::DecrementThreadLockCount()
{
--m_nThreadLockCount;
if (m_pContainer != NULL)
m_pContainer->DecrementThreadLockCount();
}
BOOL CContainerNode::OnRefresh(CComponentDataObject* pComponentData,
CNodeList* pNodeList)
{
BOOL bRet = TRUE;
if (pNodeList->GetCount() == 1) // single selection
{
if (IsSheetLocked())
{
if (!CanCloseSheets())
return FALSE;
pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this);
}
ASSERT(!IsSheetLocked());
RemoveAllChildrenHelper(pComponentData);
ASSERT(!HasChildren());
OnEnumerate(pComponentData);
AddCurrentChildrenToUI(pComponentData);
MarkEnumerated();
}
else // multiple selection
{
POSITION pos = pNodeList->GetHeadPosition();
while (pos != NULL)
{
CTreeNode* pNode = pNodeList->GetNext(pos);
ASSERT(pNode != NULL);
//
// Have each node refresh itself
//
CNodeList nodeList;
nodeList.AddTail(pNode);
if (!pNode->OnRefresh(pComponentData, &nodeList))
{
bRet = FALSE;
}
}
}
return bRet;
}
BOOL CContainerNode::RemoveChildFromList(CTreeNode* p)
{
if (p->IsContainer())
{
if (m_containerChildList.RemoveNode(p))
{
p->m_pContainer = NULL;
return TRUE;
}
}
else
{
if (m_leafChildList.RemoveNode(p))
{
p->m_pContainer = NULL;
return TRUE;
}
}
return FALSE;
}
void CContainerNode::RemoveAllChildrenHelper(CComponentDataObject* pComponentData)
{
ASSERT(pComponentData != NULL);
// remove from the UI
VERIFY(SUCCEEDED(pComponentData->RemoveAllChildren(this)));
// remove from memory, recursively from the bottom
RemoveAllChildrenFromList();
}
void CContainerNode::AddCurrentChildrenToUI(CComponentDataObject* pComponentData)
{
POSITION pos;
//
// Add leaves
//
for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild)));
}
//
// Add Containers
//
for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
VERIFY(SUCCEEDED(pComponentData->AddNode(pCurrentChild)));
}
}
void CContainerNode::SetFlagsDown(DWORD dwNodeFlags, BOOL bSet)
{
CTreeNode::SetFlagsDown(dwNodeFlags,bSet);
// scan the list of children
POSITION pos;
for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
}
for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
}
}
void CContainerNode::SetFlagsOnNonContainers(DWORD dwNodeFlags, BOOL bSet)
{
// do not set on urselves, we are a container
// scan the list of children
POSITION pos;
for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
pCurrentChild->SetFlagsDown(dwNodeFlags,bSet);
}
for (pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
{
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
((CContainerNode*)pCurrentChild)->SetFlagsOnNonContainers(dwNodeFlags,bSet);
}
}
BOOL CContainerNode::AddChildToList(CTreeNode* p)
{
BOOL bRet = FALSE;
p->m_pContainer = this;
if (p->IsContainer())
{
bRet = NULL != m_containerChildList.AddTail(p);
}
else
{
bRet = NULL != m_leafChildList.AddTail(p);
}
return bRet;
}
BOOL CContainerNode::FindChild(CTreeNode* pNode, CTreeNode** ppContainer)
{
*ppContainer = NULL;
if (pNode == NULL)
return FALSE; // no sense in continuing
if (pNode == this)
{
*ppContainer = m_pContainer;
return TRUE; // the node is ourselves
}
//
// If we are looking for a leaf node search the list of leaves first
//
if (!pNode->IsContainer())
{
POSITION pos;
for (pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
{
CLeafNode* pCurrentLeafNode = (CLeafNode*)m_leafChildList.GetNext(pos);
ASSERT(pCurrentLeafNode != NULL);
if (pCurrentLeafNode == pNode)
{
*ppContainer = this;
return TRUE;
}
}
}
//
// scan and recurse the containers if necessary
//
POSITION contPos;
for( contPos = m_containerChildList.GetHeadPosition(); contPos != NULL; )
{
CContainerNode* pCurrentChild = (CContainerNode*)m_containerChildList.GetNext(contPos);
ASSERT(pCurrentChild != NULL);
if (pCurrentChild == pNode)
{
*ppContainer = this;
return TRUE; // we directly contain the node
}
//
// if the current node is a container, look inside it
//
if (pCurrentChild->FindChild(pNode,ppContainer))
{
return TRUE; // got it in the recursion
}
}
return FALSE; // not found
}
BOOL CContainerNode::AddChildToListAndUI(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData)
{
ASSERT(pComponentData != NULL);
VERIFY(AddChildToList(pChildToAdd)); // at the end of the list of children
ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath
// add to UI only if currently visible and already expanded
if (!IsVisible() || !IsExpanded())
return TRUE;
return SUCCEEDED(pComponentData->AddNode(pChildToAdd)); // add to the UI
}
BOOL CContainerNode::AddChildToListAndUISorted(CTreeNode* pChildToAdd, CComponentDataObject* pComponentData)
{
ASSERT(pComponentData != NULL);
VERIFY(AddChildToListSorted(pChildToAdd, pComponentData));
ASSERT(pChildToAdd->GetContainer() == this); // inserted underneath
// add to UI only if currently visible and already expanded
if (!IsVisible() || !IsExpanded())
return TRUE;
return SUCCEEDED(pComponentData->AddNodeSorted(pChildToAdd)); // add to the UI
}
BOOL CContainerNode::AddChildToListSorted(CTreeNode* p, CComponentDataObject*)
{
//
// Containers will be sorted with respect to containers and leaves will be
// sorted with respect to leaves but they won't be intermingled.
//
p->m_pContainer = this;
CNodeList* pChildNodeList = NULL;
if (p->IsContainer())
{
pChildNodeList = &m_containerChildList;
}
else
{
pChildNodeList = &m_leafChildList;
}
//
// Find the position to insert the node in the list in sorted order
//
POSITION pos = pChildNodeList->GetHeadPosition();
while (pos != NULL)
{
CTreeNode* pNodeInList = pChildNodeList->GetAt(pos);
if (_wcsicoll(p->GetDisplayName(), pNodeInList->GetDisplayName()) < 0)
{
break;
}
pChildNodeList->GetNext(pos);
}
if (pos == NULL)
{
return NULL != pChildNodeList->AddTail(p);
}
return NULL != pChildNodeList->InsertBefore(pos, p);
}
void CContainerNode::RemoveAllChildrenFromList()
{
RemoveAllContainersFromList();
RemoveAllLeavesFromList();
}
int CContainerNode::Compare(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol, LPARAM)
{
// default sorting behavior
LPCTSTR lpszA = pNodeA->GetString(nCol);
LPCTSTR lpszB = pNodeB->GetString(nCol);
// cannot process NULL strings, have to use ""
ASSERT(lpszA != NULL);
ASSERT(lpszB != NULL);
return _tcsicoll( (lpszA != NULL) ? lpszA : g_lpszNullString, (lpszB != NULL) ? lpszB : g_lpszNullString);
}
void CContainerNode::ForceEnumeration(CComponentDataObject* pComponentData)
{
if (IsEnumerated())
return;
OnEnumerate(pComponentData);
MarkEnumerated();
}
void CContainerNode::MarkEnumerated(BOOL bEnum)
{
ASSERT(IsContainer());
if (bEnum)
m_dwNodeFlags |= TN_FLAG_CONTAINER_ENUM;
else
m_dwNodeFlags &= ~TN_FLAG_CONTAINER_ENUM;
}
void CContainerNode::MarkEnumeratedAndLoaded(CComponentDataObject* pComponentData)
{
MarkEnumerated();
OnChangeState(pComponentData); // move to loading
OnChangeState(pComponentData); // move to loaded
}
/////////////////////////////////////////////////////////////////////////////
// CBackgroundThread
CBackgroundThread::CBackgroundThread()
{
m_pQueryObj = NULL;
m_bAutoDelete = FALSE;
m_bAbandoned = FALSE;
m_pContNode = NULL;
m_hEventHandle = NULL;
ExceptionPropagatingInitializeCriticalSection(&m_cs);
m_nQueueCountMax = 10;
}
CBackgroundThread::~CBackgroundThread()
{
TRACE(_T("CBackgroundThread::~CBackgroundThread()\n"));
ASSERT(IsAbandoned() || IsQueueEmpty());
::DeleteCriticalSection(&m_cs);
if (m_hEventHandle != NULL)
{
VERIFY(::CloseHandle(m_hEventHandle));
m_hEventHandle = NULL;
}
if (m_pQueryObj != NULL)
{
delete m_pQueryObj;
m_pQueryObj = NULL;
}
}
void CBackgroundThread::SetQueryObj(CQueryObj* pQueryObj)
{
ASSERT(pQueryObj != NULL);
m_pQueryObj = pQueryObj;
m_pQueryObj->SetThread(this);
}
BOOL CBackgroundThread::Start(CMTContainerNode* pNode, CComponentDataObject* pComponentData)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
ASSERT(m_pContNode == NULL);
m_pContNode = pNode;
m_hHiddenWnd = pComponentData->GetHiddenWindow();
ASSERT(m_hEventHandle == NULL); // cannot call start twice or reuse the same C++ object
m_hEventHandle = ::CreateEvent(NULL,TRUE /*bManualReset*/,FALSE /*signalled*/, NULL);
if (m_hEventHandle == NULL)
return FALSE;
return CreateThread();
}
int CBackgroundThread::Run()
{
ASSERT(m_pContNode != NULL);
ASSERT(m_pQueryObj != NULL);
TRACE(_T("CBackgroundThread::Run() started\n"));
while (m_pQueryObj->Enumerate());
// before exiting, have to make sure there are no items in the queue
if (!IsQueueEmpty())
VERIFY(PostHaveData());
VERIFY(PostExiting());
// wait for the main thread to acknowledge the exiting message
WaitForExitAcknowledge();
ASSERT(IsAbandoned() || IsQueueEmpty()); // we cannot lose items in the queue
TRACE(_T("CBackgroundThread::Run() terminated\n"));
return 0;
}
void CBackgroundThread::Abandon()
{
Lock();
TRACE(_T("CBackgroundThread::Abandon()\n"));
m_bAutoDelete = TRUE;
m_bAbandoned = TRUE;
Unlock();
VERIFY(0 != ::SetEvent(m_hEventHandle));
}
BOOL CBackgroundThread::IsAbandoned()
{
Lock();
BOOL b = m_bAbandoned;
Unlock();
return b;
}
BOOL CBackgroundThread::OnAddToQueue(INT_PTR nCount)
{
BOOL bPostedMessage = FALSE;
if (nCount >= m_nQueueCountMax)
{
VERIFY(PostHaveData());
bPostedMessage = TRUE;
}
return bPostedMessage;
}
CObjBase* CBackgroundThread::RemoveFromQueue()
{
Lock();
ASSERT(m_pQueryObj != NULL);
CObjBaseList* pQueue = m_pQueryObj->GetQueue();
CObjBase* p = pQueue->IsEmpty() ? NULL : pQueue->RemoveHead();
Unlock();
return p;
}
BOOL CBackgroundThread::IsQueueEmpty()
{
Lock();
ASSERT(m_pQueryObj != NULL);
CObjBaseList* pQueue = m_pQueryObj->GetQueue();
BOOL bRes = pQueue->IsEmpty();
Unlock();
return bRes;
}
BOOL CBackgroundThread::PostHaveData()
{
return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadHaveDataNotificationMessage,
(WPARAM)m_pContNode, (LPARAM)0);
}
BOOL CBackgroundThread::PostError(DWORD dwErr)
{
return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadErrorNotificationMessage,
(WPARAM)m_pContNode, (LPARAM)dwErr);
}
BOOL CBackgroundThread::PostExiting()
{
return PostMessageToComponentDataRaw(CHiddenWnd::s_NodeThreadExitingNotificationMessage,
(WPARAM)m_pContNode, (LPARAM)0);
}
BOOL CBackgroundThread::PostMessageToComponentDataRaw(UINT Msg, WPARAM wParam, LPARAM lParam)
{
BOOL b = IsAbandoned();
if (b)
{
return TRUE; // no need to post
}
ASSERT(m_pContNode != NULL);
ASSERT(m_hHiddenWnd != NULL);
ASSERT(::IsWindow(m_hHiddenWnd));
return ::PostMessage(m_hHiddenWnd, Msg, wParam, lParam);
}
void CBackgroundThread::WaitForExitAcknowledge()
{
VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEventHandle,INFINITE));
}
////////////////////////////////////////////////////////////////////////
// CMTContainerNode
CMTContainerNode::~CMTContainerNode()
{
ASSERT(m_pThread == NULL);
}
BOOL CMTContainerNode::OnEnumerate(CComponentDataObject* pComponentData, BOOL bAsync)
{
OnChangeState(pComponentData);
VERIFY(StartBackgroundThread(pComponentData, bAsync));
return FALSE; // children not added, the thread will add them later
}
BOOL CMTContainerNode::OnRefresh(CComponentDataObject* pComponentData,
CNodeList* pNodeList)
{
BOOL bRet = TRUE;
if (pNodeList->GetCount() == 1) // single selection
{
BOOL bLocked = IsThreadLocked();
ASSERT(!bLocked); // cannot do refresh on locked node, the UI should prevent this
if (bLocked)
return FALSE;
if (IsSheetLocked())
{
if (!CanCloseSheets())
{
pComponentData->GetPropertyPageHolderTable()->BroadcastSelectPage(this, -1);
return FALSE;
}
pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this);
}
ASSERT(!IsSheetLocked());
RemoveAllChildrenHelper(pComponentData);
ASSERT(!HasChildren());
OnEnumerate(pComponentData); // will spawn a thread to do enumeration
MarkEnumerated();
}
else // multiple selection
{
POSITION pos = pNodeList->GetHeadPosition();
while (pos != NULL)
{
CTreeNode* pNode = pNodeList->GetNext(pos);
ASSERT(pNode != NULL);
CNodeList nodeList;
nodeList.AddTail(pNode);
if (!pNode->OnRefresh(pComponentData, &nodeList))
{
bRet = FALSE;
}
}
}
return TRUE;
}
void CMTContainerNode::AbandonThread(CComponentDataObject* pComponentData)
{
if(m_pThread == NULL) // nothing running
return;
m_pThread->Abandon();
m_pThread = NULL;
pComponentData->GetRunningThreadTable()->Remove(this);
}
BOOL CMTContainerNode::StartBackgroundThread(CComponentDataObject* pComponentData, BOOL bAsync)
{
ASSERT(m_pThread == NULL); // nothing running
// notify the UI to change icon, if needed
VERIFY(SUCCEEDED(pComponentData->ChangeNode(this, CHANGE_RESULT_ITEM_ICON)));
m_pThread = CreateThreadObject();
ASSERT(m_pThread != NULL);
m_pThread->SetQueryObj(OnCreateQuery());
BOOL bRes = m_pThread->Start(this, pComponentData);
if (bRes)
{
pComponentData->GetRunningThreadTable()->Add(this);
// we need to call UpdateVerbState() because the lock count changed
// by adding the node from the running thread table
VERIFY(SUCCEEDED(pComponentData->UpdateVerbState(this)));
}
//
// If we don't want this call to be asynchronous then we have to wait for
// the thread to finish
//
if (!bAsync)
{
pComponentData->WaitForThreadExitMessage(this);
}
return bRes;
}
void CMTContainerNode::OnThreadHaveDataNotification(CComponentDataObject* pComponentDataObject)
{
ASSERT(m_pThread != NULL);
ASSERT(IsThreadLocked());
// do data transfer from thread queue
CObjBase* p = m_pThread->RemoveFromQueue();
while (p)
{
// add new node to the list of children and propagate to the UI
OnHaveData(p,pComponentDataObject);
p = m_pThread->RemoveFromQueue();
}
}
void CMTContainerNode::OnThreadErrorNotification(DWORD dwErr, CComponentDataObject*)
{
ASSERT(m_pThread != NULL);
ASSERT(IsThreadLocked());
OnError(dwErr);
}
void CMTContainerNode::OnThreadExitingNotification(CComponentDataObject* pComponentDataObject)
{
ASSERT(m_pThread != NULL);
ASSERT(IsThreadLocked());
#if (TRUE)
// let the thread know it can shut down
m_pThread->AcknowledgeExiting();
VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(m_pThread->m_hThread,INFINITE));
OnChangeState(pComponentDataObject);
delete m_pThread;
m_pThread = NULL;
pComponentDataObject->GetRunningThreadTable()->Remove(this);
// we need to call UpdateVerbState() because the lock count changed
// by removing the node from the running thread table
VERIFY(SUCCEEDED(pComponentDataObject->UpdateVerbState(this)));
TRACE(_T("OnThreadExitingNotification()\n"));
#else // maybe better way of doing it???
// we are going to detach from the thread, so make copies of variables
HANDLE hThread = m_pThread->m_hThread;
CBackgroundThread* pThread = m_pThread;
AbandonThread(pComponentDataObject); // sets m_pThread = NULL
// acknowledge to thread
pThread->AcknowledgeExiting();
VERIFY(WAIT_OBJECT_0 == ::WaitForSingleObject(hThread,INFINITE));
OnChangeState(pComponentDataObject);
#endif
VERIFY(SUCCEEDED(pComponentDataObject->SortResultPane(this)));
}
///////////////////////////////////////////////////////////////////////////////