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.
2805 lines
77 KiB
2805 lines
77 KiB
// ScopeNode.cpp : Implementation of CBOMSnapApp and DLL registration.
|
|
|
|
#include "stdafx.h"
|
|
#include "streamio.h"
|
|
#include "BOMSnap.h"
|
|
#include "ScopeNode.h"
|
|
#include "atlgdi.h"
|
|
|
|
#include "rootprop.h"
|
|
#include "qryprop.h"
|
|
#include "grpprop.h"
|
|
#include "namemap.h"
|
|
#include "query.h"
|
|
#include "compont.h"
|
|
#include "compdata.h"
|
|
#include "wizards.h"
|
|
#include "cmndlgs.h"
|
|
|
|
#include <algorithm>
|
|
#include <lmcons.h> // for UNLEN
|
|
|
|
extern HWND g_hwndMain;
|
|
extern DWORD g_dwFileVer; // Current console file version (from compdata.cpp)
|
|
|
|
|
|
// Register static clipboard format members
|
|
UINT CScopeNode::m_cfDisplayName = RegisterClipboardFormat(TEXT("CCF_DISPLAY_NAME"));
|
|
UINT CScopeNode::m_cfSnapInClsid = RegisterClipboardFormat(TEXT("CCF_SNAPIN_CLASSID"));
|
|
UINT CScopeNode::m_cfNodeType = RegisterClipboardFormat(TEXT("CCF_NODETYPE"));
|
|
UINT CScopeNode::m_cfszNodeType = RegisterClipboardFormat(TEXT("CCF_SZNODETYPE"));
|
|
UINT CScopeNode::m_cfNodeID2 = RegisterClipboardFormat(TEXT("CCF_NODEID2"));
|
|
UINT CScopeNode::m_cfColumnSetID = RegisterClipboardFormat(TEXT("CCF_COLUMN_SET_ID"));
|
|
|
|
// {316A1EEA-C249-44e0-958B-00D2AB989D2F}
|
|
static const GUID GUID_RootNode =
|
|
{ 0x316a1eea, 0xc249, 0x44e0, { 0x95, 0x8b, 0x0, 0xd2, 0xab, 0x98, 0x9d, 0x2f } };
|
|
|
|
|
|
// {2A34413B-B565-469e-9C28-5E733768264F}
|
|
static const GUID GUID_GroupNode =
|
|
{ 0x2a34413b, 0xb565, 0x469e, { 0x9c, 0x28, 0x5e, 0x73, 0x37, 0x68, 0x26, 0x4f } };
|
|
|
|
|
|
// {1030A359-F520-4748-95CA-8C8CEFA5C63F}
|
|
static const GUID GUID_QueryNode =
|
|
{ 0x1030a359, 0xf520, 0x4748, { 0x95, 0xca, 0x8c, 0x8c, 0xef, 0xa5, 0xc6, 0x3f } };
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CScopeNode
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CScopeNode::CreateNode(NODETYPE nodetype, CScopeNode** ppnode)
|
|
{
|
|
VALIDATE_POINTER( ppnode );
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
switch( nodetype )
|
|
{
|
|
case GROUP_NODETYPE:
|
|
{
|
|
CComObject<CGroupNode>* pGroupNode = NULL;
|
|
hr = CComObject<CGroupNode>::CreateInstance(&pGroupNode);
|
|
*ppnode = pGroupNode;
|
|
}
|
|
break;
|
|
|
|
case QUERY_NODETYPE:
|
|
{
|
|
CComObject<CQueryNode>* pQueryNode = NULL;
|
|
hr = CComObject<CQueryNode>::CreateInstance(&pQueryNode);
|
|
*ppnode = pQueryNode;
|
|
}
|
|
break;
|
|
|
|
case ROOT_NODETYPE:
|
|
{
|
|
CComObject<CRootNode>* pRootNode = NULL;
|
|
hr = CComObject<CRootNode>::CreateInstance(&pRootNode);
|
|
*ppnode = pRootNode;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0 && "Invalid node type");
|
|
}
|
|
|
|
// return addref'd object
|
|
if( SUCCEEDED(hr) )
|
|
(*ppnode)->AddRef();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// AddNewChild should only be called when a new node is created, not
|
|
// to add an existing node, such as when loading the node tree from
|
|
// a console file.
|
|
HRESULT CScopeNode::AddNewChild(CScopeNode* pnodeChild, LPCWSTR pszName)
|
|
{
|
|
VALIDATE_POINTER(pnodeChild);
|
|
ASSERT(pszName && pszName[0]);
|
|
|
|
// Assign permanent node ID
|
|
// Root node tracks last used ID in its lNodeID member
|
|
CRootNode* pRootNode = GetRootNode();
|
|
pnodeChild->m_lNodeID = pRootNode ? ++(pRootNode->m_lNodeID) : 0;
|
|
|
|
// Assign parent node
|
|
pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
|
|
|
|
// Now that node has parent we can set the name
|
|
// (needs parent to get to IStringTable)
|
|
HRESULT hr = pnodeChild->SetName(pszName);
|
|
ASSERT(SUCCEEDED(hr));
|
|
RETURN_ON_FAILURE( hr );
|
|
|
|
// In order to persist the Column Data, we'll need to get a unique ID
|
|
hr = CoCreateGuid(&m_gColumnID);
|
|
ASSERT(SUCCEEDED(hr));
|
|
RETURN_ON_FAILURE( hr );
|
|
|
|
return AddChild(pnodeChild);
|
|
}
|
|
|
|
|
|
// Call AddChild to add a new node or a moved node to the parent node
|
|
HRESULT CScopeNode::AddChild(CScopeNode* pnodeChild)
|
|
{
|
|
VALIDATE_POINTER( pnodeChild );
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Add new child to end of child list
|
|
if( m_pnodeChild == NULL )
|
|
m_pnodeChild = pnodeChild;
|
|
else
|
|
{
|
|
CScopeNode* pnodePrev = m_pnodeChild;
|
|
while( pnodePrev->Next() )
|
|
pnodePrev = pnodePrev->Next();
|
|
|
|
pnodePrev->m_pnodeNext = pnodeChild;
|
|
}
|
|
|
|
// Assign parent node
|
|
pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
|
|
|
|
pnodeChild->AddRef();
|
|
|
|
// if this node has been added to the scope pane
|
|
if( m_hScopeItem != NULL )
|
|
{
|
|
IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
|
|
ASSERT( pNameSpace );
|
|
if( !pNameSpace ) return E_FAIL;
|
|
|
|
SCOPEDATAITEM sdi;
|
|
sdi.ID = m_hScopeItem;
|
|
sdi.mask = SDI_STATE;
|
|
|
|
// Has it been expanded?
|
|
HRESULT hr2 = pNameSpace->GetItem(&sdi);
|
|
if( SUCCEEDED(hr2) && (sdi.nState & MMC_SCOPE_ITEM_STATE_EXPANDEDONCE) )
|
|
{
|
|
hr = pnodeChild->Insert(pNameSpace);
|
|
}
|
|
else
|
|
{
|
|
// if can't add children yet, then set children to show the '+'
|
|
SCOPEDATAITEM sdi2;
|
|
sdi2.ID = m_hScopeItem;
|
|
sdi2.mask = SDI_CHILDREN;
|
|
sdi2.cChildren = 1;
|
|
|
|
pNameSpace->SetItem(&sdi2);
|
|
}
|
|
}
|
|
|
|
// Force refresh on both child and parent because a query node may be modified
|
|
// by its new parent and a group node is always modified by its children
|
|
OnRefresh(NULL);
|
|
pnodeChild->OnRefresh(NULL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CScopeNode::RemoveChild(CScopeNode* pnodeDelete)
|
|
{
|
|
VALIDATE_POINTER(pnodeDelete);
|
|
ASSERT(pnodeDelete->Parent() == this);
|
|
|
|
// if deleting the first child
|
|
if( m_pnodeChild == pnodeDelete )
|
|
{
|
|
// just set first child to its next sibling
|
|
m_pnodeChild = m_pnodeChild->Next();
|
|
}
|
|
else
|
|
{
|
|
// Locate preceding sibling
|
|
CScopeNode* pnodePrev = m_pnodeChild;
|
|
while( pnodePrev && pnodePrev->Next() != pnodeDelete )
|
|
{
|
|
pnodePrev = pnodePrev->Next();
|
|
}
|
|
|
|
// remove deleted node from list
|
|
if( pnodePrev )
|
|
{
|
|
pnodePrev->m_pnodeNext = pnodeDelete->Next();
|
|
}
|
|
}
|
|
|
|
pnodeDelete->m_pnodeNext = NULL;
|
|
|
|
// release the node
|
|
pnodeDelete->Release();
|
|
|
|
// Do refresh in case this is a group node losing a child
|
|
OnRefresh(NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
CRootNode* CScopeNode::GetRootNode()
|
|
{
|
|
CScopeNode* pNode = this;
|
|
while( pNode && !pNode->IsRootNode() )
|
|
{
|
|
pNode = pNode->Parent();
|
|
}
|
|
|
|
return static_cast<CRootNode*>(pNode);
|
|
}
|
|
|
|
CComponentData* CScopeNode::GetCompData()
|
|
{
|
|
CRootNode* pRootNode = GetRootNode();
|
|
return pRootNode ? pRootNode->GetRootCompData() : NULL;
|
|
}
|
|
|
|
CScopeNode::~CScopeNode()
|
|
{
|
|
// Release all nodes on child list
|
|
OnRemoveChildren(NULL);
|
|
}
|
|
|
|
HRESULT CScopeNode::GetDataImpl(UINT cf, HGLOBAL* phGlobal)
|
|
{
|
|
VALIDATE_POINTER( phGlobal );
|
|
|
|
HRESULT hr = DV_E_FORMATETC;
|
|
|
|
if( cf == m_cfDisplayName )
|
|
{
|
|
hr = DataToGlobal(phGlobal, m_strName.c_str(), (m_strName.size() + 1) * sizeof(WCHAR));
|
|
}
|
|
else if( cf == m_cfSnapInClsid )
|
|
{
|
|
hr = DataToGlobal(phGlobal, &CLSID_BOMSnapIn, sizeof(GUID));
|
|
}
|
|
else if( cf == m_cfNodeType )
|
|
{
|
|
hr = DataToGlobal(phGlobal, NodeTypeGuid(), sizeof(GUID));
|
|
}
|
|
else if( cf == m_cfszNodeType )
|
|
{
|
|
WCHAR szGuid[GUID_STRING_LEN+1];
|
|
StringFromGUID2(*NodeTypeGuid(), szGuid, GUID_STRING_LEN+1);
|
|
|
|
hr = DataToGlobal(phGlobal, szGuid, GUID_STRING_SIZE);
|
|
}
|
|
else if( cf == m_cfNodeID2 )
|
|
{
|
|
// return SNodeID2 struct with the node's ID
|
|
// For a root node always return 1; m_lNodeID for a root node holds the last ID
|
|
// assigned to an enumerated node. It is incremented for each new child node.
|
|
int nSize = sizeof(SNodeID2) + sizeof(long) - 1;
|
|
SNodeID2* pNodeID = reinterpret_cast<SNodeID2*>(malloc( nSize ));
|
|
if( !pNodeID ) return E_OUTOFMEMORY;
|
|
|
|
pNodeID->dwFlags = 0;
|
|
pNodeID->cBytes = sizeof(long);
|
|
*((long*)(pNodeID->id)) = IsRootNode() ? 1 : m_lNodeID;
|
|
|
|
hr = DataToGlobal( phGlobal, pNodeID, nSize );
|
|
|
|
free( pNodeID );
|
|
}
|
|
else if( cf == m_cfColumnSetID)
|
|
{
|
|
int nSize2 = sizeof(SColumnSetID) + sizeof(m_gColumnID) - 1;
|
|
SColumnSetID* pColumnSetID = reinterpret_cast<SColumnSetID*>(malloc( nSize2 ));
|
|
if( !pColumnSetID ) return E_OUTOFMEMORY;
|
|
|
|
pColumnSetID->dwFlags = 0;
|
|
pColumnSetID->cBytes = sizeof(m_gColumnID);
|
|
::CopyMemory(pColumnSetID->id, &m_gColumnID, pColumnSetID->cBytes);
|
|
|
|
hr = DataToGlobal( phGlobal, pColumnSetID, nSize2 );
|
|
|
|
free( pColumnSetID );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CScopeNode::GetDisplayInfo(RESULTDATAITEM* pRDI)
|
|
{
|
|
VALIDATE_POINTER( pRDI );
|
|
|
|
if( pRDI->bScopeItem )
|
|
{
|
|
ASSERT(pRDI->lParam == reinterpret_cast<LPARAM>(this));
|
|
|
|
if( pRDI->mask & RDI_STR )
|
|
pRDI->str = const_cast<LPWSTR>(GetName());
|
|
|
|
if( pRDI->mask & RDI_IMAGE )
|
|
pRDI->nImage = GetImage();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::GetDisplayInfo(SCOPEDATAITEM* pSDI)
|
|
{
|
|
VALIDATE_POINTER( pSDI );
|
|
|
|
if( pSDI->mask & SDI_STR )
|
|
pSDI->displayname = const_cast<LPWSTR>(GetName());
|
|
|
|
if( pSDI->mask & SDI_IMAGE )
|
|
pSDI->nImage = GetImage();
|
|
|
|
if( pSDI->mask & SDI_OPENIMAGE )
|
|
pSDI->nOpenImage = GetOpenImage();
|
|
|
|
if( pSDI->mask & SDI_CHILDREN )
|
|
pSDI->cChildren = HasChildren() ? 1 : 0;
|
|
|
|
if( pSDI->mask & SDI_PARAM )
|
|
pSDI->lParam = reinterpret_cast<LPARAM>(this);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::AttachComponent(CComponent* pComponent)
|
|
{
|
|
VALIDATE_POINTER( pComponent );
|
|
|
|
if( std::find(m_vComponents.begin(), m_vComponents.end(), pComponent) != m_vComponents.end() )
|
|
return S_FALSE;
|
|
|
|
m_vComponents.push_back(pComponent);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::DetachComponent(CComponent* pComponent)
|
|
{
|
|
VALIDATE_POINTER( pComponent );
|
|
|
|
std::vector<CComponent*>::iterator it = std::find(m_vComponents.begin(), m_vComponents.end(), pComponent);
|
|
if( it == m_vComponents.end() )
|
|
return S_FALSE;
|
|
|
|
m_vComponents.erase(it);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL CScopeNode::OwnsConsoleView(LPCONSOLE2 pConsole)
|
|
{
|
|
if( !pConsole ) return FALSE;
|
|
|
|
std::vector<CComponent*>::iterator it;
|
|
for( it = m_vComponents.begin(); it != m_vComponents.end(); ++it )
|
|
{
|
|
if( (*it)->GetConsole() == pConsole )
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
/************************************************************************************
|
|
* Notification handlers
|
|
************************************************************************************/
|
|
|
|
BEGIN_NOTIFY_MAP(CScopeNode)
|
|
ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp)
|
|
ON_SELECT()
|
|
ON_EXPAND()
|
|
ON_RENAME()
|
|
ON_REMOVE_CHILDREN()
|
|
ON_ADD_IMAGES()
|
|
END_NOTIFY_MAP()
|
|
|
|
HRESULT CScopeNode::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/)
|
|
{
|
|
VALIDATE_POINTER( pConsole );
|
|
|
|
tstring strHelpFile = _T("");
|
|
tstring strHelpTopic = _T("");
|
|
tstring strHelpFull = _T("");
|
|
|
|
strHelpFile = StrLoadString(IDS_HELPFILE);
|
|
if( strHelpFile.empty() ) return E_FAIL;
|
|
|
|
// Special Hack to get a different help topic for the first two nodes.
|
|
switch( m_lNodeID )
|
|
{
|
|
case 2:
|
|
{
|
|
// Users Node
|
|
strHelpTopic = StrLoadString(IDS_USERSHELPTOPIC);
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
// Printers Node
|
|
strHelpTopic = StrLoadString(IDS_PRINTERSHELPTOPIC);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Verify that we got a help topic!
|
|
if( strHelpTopic.empty() ) return E_FAIL;
|
|
|
|
// Build path to %systemroot%\help
|
|
TCHAR szWindowsDir[MAX_PATH+1] = {0};
|
|
UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
|
|
if( nSize == 0 || nSize > MAX_PATH )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
strHelpFull = szWindowsDir;
|
|
strHelpFull += _T("\\Help\\");
|
|
strHelpFull += strHelpFile;
|
|
strHelpFull += _T("::/");
|
|
strHelpFull += strHelpTopic;
|
|
|
|
// Show the Help topic
|
|
CComQIPtr<IDisplayHelp> spHelp = pConsole;
|
|
if( !spHelp ) return E_NOINTERFACE;
|
|
|
|
return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() );
|
|
}
|
|
|
|
HRESULT CScopeNode::Insert(LPCONSOLENAMESPACE pNameSpace)
|
|
{
|
|
if( !pNameSpace ) return E_POINTER;
|
|
if( !m_pnodeParent ) return E_FAIL;
|
|
ASSERT( m_pnodeParent->m_hScopeItem != 0 );
|
|
ASSERT( m_hScopeItem == 0 );
|
|
|
|
// if not set yet, get name from string table (mmc will ask for it after insertion)
|
|
// (name will be set a new node and not set for reloaded nodes)
|
|
if( m_strName.empty() )
|
|
{
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
HRESULT hr = StringTableRead(pStringTable, m_nameID, m_strName);
|
|
ASSERT(SUCCEEDED(hr));
|
|
RETURN_ON_FAILURE(hr);
|
|
}
|
|
|
|
SCOPEDATAITEM sdi;
|
|
|
|
sdi.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_PARAM | SDI_CHILDREN | SDI_PARENT;
|
|
|
|
sdi.relativeID = m_pnodeParent->m_hScopeItem;
|
|
sdi.displayname = MMC_TEXTCALLBACK; // MMC only allows callback for name
|
|
sdi.nImage = GetImage();
|
|
sdi.nOpenImage = GetOpenImage();
|
|
sdi.cChildren = HasChildren() ? 1 : 0;
|
|
sdi.lParam = reinterpret_cast<LPARAM>(this);
|
|
|
|
HRESULT hr = pNameSpace->InsertItem(&sdi);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
m_hScopeItem = sdi.ID;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::OnExpand(LPCONSOLE2 pConsole, BOOL bExpand, HSCOPEITEM hScopeItem)
|
|
{
|
|
VALIDATE_POINTER( pConsole );
|
|
|
|
// Nothing to do on collapse
|
|
if( !bExpand )
|
|
return S_OK;
|
|
|
|
// Scope item ID shouldn't change
|
|
ASSERT(m_hScopeItem == 0 || m_hScopeItem == hScopeItem);
|
|
|
|
// Save scope item ID
|
|
m_hScopeItem = hScopeItem;
|
|
|
|
// If expanding root node
|
|
if( m_pnodeParent == NULL )
|
|
{
|
|
// Get Scope image list interface
|
|
IImageListPtr spImageList;
|
|
HRESULT hr = pConsole->QueryScopeImageList(&spImageList);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
// Add standard images to the scope pane
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = OnAddImages(pConsole, spImageList);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
}
|
|
|
|
// Get namespace interface
|
|
IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
|
|
if( pNameSpace == NULL )
|
|
return E_FAIL;
|
|
|
|
// Step through child list and add each one to scope pane
|
|
CScopeNode* pnode = FirstChild();
|
|
while( pnode != NULL )
|
|
{
|
|
pnode->Insert(pNameSpace);
|
|
pnode = pnode->m_pnodeNext;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::OnRename(LPCONSOLE2 pConsole, LPCWSTR pszName)
|
|
{
|
|
if( pszName == NULL || pszName[0] == 0 )
|
|
return E_INVALIDARG;
|
|
|
|
return SetName(pszName);
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::SetName(LPCWSTR pszName)
|
|
{
|
|
if( !pszName || !pszName[0] ) return E_POINTER;
|
|
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
HRESULT hr = StringTableWrite(pStringTable, pszName, &m_nameID);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
m_strName = pszName;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::OnRemoveChildren(LPCONSOLE2 pConsole)
|
|
{
|
|
// Step through child list and release each one
|
|
CScopeNode* pnode = m_pnodeChild;
|
|
while( pnode != NULL )
|
|
{
|
|
CScopeNode* pnodeNext = pnode->m_pnodeNext;
|
|
pnode->Release();
|
|
pnode = pnodeNext;
|
|
}
|
|
|
|
m_pnodeChild = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CScopeNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
|
|
{
|
|
VALIDATE_POINTER(pImageList);
|
|
|
|
CBitmap bmp16;
|
|
CBitmap bmp32;
|
|
|
|
bmp16.LoadBitmap(IDB_QUERY16);
|
|
bmp32.LoadBitmap(IDB_QUERY32);
|
|
|
|
ASSERT(bmp16 != (HBITMAP)NULL && (HBITMAP)bmp32 != (HBITMAP)NULL);
|
|
|
|
if( bmp16 == (HBITMAP)NULL || bmp32 == (HBITMAP)NULL )
|
|
return E_FAIL;
|
|
|
|
HRESULT hr = pImageList->ImageListSetStrip(
|
|
(LONG_PTR*)static_cast<HBITMAP>(bmp16),
|
|
(LONG_PTR*)static_cast<HBITMAP>(bmp32),
|
|
0, RGB(255,0,255));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::OnSelect(LPCONSOLE2 pConsole, BOOL bSelect, BOOL bScope)
|
|
{
|
|
VALIDATE_POINTER( pConsole );
|
|
|
|
// See CScopeNode::OnRefresh for explanation of m_bIgnoreSelect
|
|
if( bSelect && !m_bIgnoreSelect )
|
|
{
|
|
CComPtr<IConsoleVerb> spConsVerb;
|
|
pConsole->QueryConsoleVerb(&spConsVerb);
|
|
ASSERT(spConsVerb != NULL);
|
|
if( !spConsVerb ) return E_NOINTERFACE;
|
|
|
|
BOOL bOwnsView = OwnsConsoleView(pConsole);
|
|
|
|
if( spConsVerb != NULL )
|
|
{
|
|
EnableVerbs(spConsVerb, bOwnsView);
|
|
|
|
spConsVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, FALSE);
|
|
spConsVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, TRUE);
|
|
|
|
spConsVerb->SetVerbState(MMC_VERB_RENAME, ENABLED, FALSE);
|
|
spConsVerb->SetVerbState(MMC_VERB_RENAME, HIDDEN, TRUE);
|
|
|
|
spConsVerb->SetVerbState(MMC_VERB_DELETE, ENABLED, FALSE);
|
|
spConsVerb->SetVerbState(MMC_VERB_DELETE, HIDDEN, TRUE);
|
|
|
|
// default verb for scope nodes is open
|
|
spConsVerb->SetDefaultVerb(MMC_VERB_OPEN);
|
|
}
|
|
}
|
|
|
|
if( bSelect )
|
|
{
|
|
m_bIgnoreSelect = FALSE;
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/******************************************************************************************
|
|
* Menus and verbs
|
|
******************************************************************************************/
|
|
|
|
BOOL AddMenuItem(LPCONTEXTMENUCALLBACK pCallback, long nID, long lInsertID, long lFlags, TCHAR* szNoLocName)
|
|
{
|
|
if( !pCallback ) return FALSE;
|
|
|
|
CComQIPtr<IContextMenuCallback2> spContext2 = pCallback;
|
|
if( !spContext2 ) return FALSE;
|
|
|
|
CONTEXTMENUITEM2 item;
|
|
|
|
CString strItem;
|
|
strItem.LoadString(nID);
|
|
ASSERT(!strItem.IsEmpty());
|
|
|
|
int iSep = strItem.Find(L'\n');
|
|
ASSERT(iSep != -1);
|
|
|
|
CString strName = strItem.Left(iSep);
|
|
CString strDescr = strItem.Right(strItem.GetLength() - iSep);
|
|
|
|
item.strName = const_cast<LPWSTR>((LPCWSTR)strName);
|
|
item.strStatusBarText = const_cast<LPWSTR>((LPCWSTR)strDescr);
|
|
item.lCommandID = nID;
|
|
item.lInsertionPointID = lInsertID;
|
|
item.fFlags = lFlags;
|
|
item.fSpecialFlags = 0;
|
|
item.strLanguageIndependentName = szNoLocName;
|
|
|
|
return SUCCEEDED(spContext2->AddItem(&item));
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
* Persistance methods
|
|
******************************************************************************************/
|
|
HRESULT CScopeNode::LoadNode(IStream& stm)
|
|
{
|
|
stm >> m_nameID;
|
|
ASSERT(m_nameID != 0);
|
|
|
|
stm >> m_lNodeID;
|
|
stm >> m_gColumnID;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode:: SaveNode(IStream& stm)
|
|
{
|
|
ASSERT(m_nameID != 0);
|
|
stm << m_nameID;
|
|
stm << m_lNodeID;
|
|
stm << m_gColumnID;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::Load(IStream& stm)
|
|
{
|
|
HRESULT hr = LoadNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// if container node, then load children
|
|
if( IsContainer() )
|
|
{
|
|
NODETYPE nodetype;
|
|
stm >> *(int*)&nodetype;
|
|
|
|
// If container has a child node
|
|
if( nodetype != NULL_NODETYPE )
|
|
{
|
|
hr = CreateNode(nodetype, &m_pnodeChild);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// Set parent before loading, so node can pass it on when
|
|
// it loads its siblings
|
|
m_pnodeChild->m_pnodeParent = static_cast<CScopeNode*>(this);
|
|
|
|
// Load first child only; it will load its siblings
|
|
hr = m_pnodeChild->Load(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
}
|
|
}
|
|
|
|
// if this is the first child of a node, then load siblings
|
|
// (Iteration rather than recursion to avoid a potentially
|
|
// very deep stack.)
|
|
if( m_pnodeParent && m_pnodeParent->FirstChild() == this )
|
|
{
|
|
CScopeNode* pnodePrev = static_cast<CScopeNode*>(this);
|
|
|
|
NODETYPE nodetype;
|
|
stm >> *(int*)&nodetype;
|
|
|
|
// Loop until terminating null node type encountered
|
|
while( nodetype != NULL_NODETYPE )
|
|
{
|
|
CScopeNodePtr spnode;
|
|
hr = CreateNode(nodetype, &spnode);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
spnode->m_pnodeParent = m_pnodeParent;
|
|
|
|
hr = spnode->Load(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// Link to previous sibling
|
|
pnodePrev->m_pnodeNext = spnode.Detach();
|
|
pnodePrev = pnodePrev->m_pnodeNext;
|
|
|
|
stm >> *(int*)&nodetype;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::Save(IStream& stm)
|
|
{
|
|
// Save the node's data
|
|
HRESULT hr = SaveNode(stm);
|
|
RETURN_ON_FAILURE(hr)
|
|
|
|
// if container type node
|
|
if( IsContainer() )
|
|
{
|
|
// Save children (first child saves all its siblings)
|
|
if( FirstChild() )
|
|
{
|
|
stm << (int)FirstChild()->NodeType();
|
|
hr = FirstChild()->Save(stm);
|
|
RETURN_ON_FAILURE(hr)
|
|
}
|
|
|
|
// Terminate child list with null node
|
|
stm << (int)NULL_NODETYPE;
|
|
}
|
|
|
|
// if this is the first child, save its siblings
|
|
if( m_pnodeParent && m_pnodeParent->FirstChild() == this )
|
|
{
|
|
CScopeNode* pnode = m_pnodeNext;
|
|
while( pnode != NULL )
|
|
{
|
|
stm << (int)pnode->NodeType();
|
|
|
|
hr = pnode->Save(stm);
|
|
BREAK_ON_FAILURE(hr);
|
|
|
|
pnode = pnode->m_pnodeNext;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CScopeNode::AddQueryNode(LPCONSOLE2 pConsole)
|
|
{
|
|
VALIDATE_POINTER( pConsole );
|
|
ASSERT(NodeType() != QUERY_NODETYPE);
|
|
|
|
HRESULT hr;
|
|
do
|
|
{
|
|
// Create a new query node
|
|
CQueryNodePtr spnode;
|
|
hr = CreateNode(QUERY_NODETYPE, reinterpret_cast<CScopeNode**>(&spnode));
|
|
BREAK_ON_FAILURE(hr);
|
|
|
|
// Create and init wizard
|
|
CAddQueryWizard queryWiz;
|
|
queryWiz.Initialize(spnode, GetRootNode(), GetCompData()->GetStringTable());
|
|
|
|
// Run the wizard
|
|
IPropertySheetProviderPtr spProvider = pConsole;
|
|
if( spProvider == NULL ) return E_NOINTERFACE;
|
|
|
|
HWND hwndMain;
|
|
pConsole->GetMainWindow(&hwndMain);
|
|
|
|
hr = queryWiz.Run(spProvider, hwndMain);
|
|
if( hr != S_OK )
|
|
break;
|
|
|
|
// Add any new classes to root node
|
|
CRootNode* pRootNode = GetRootNode();
|
|
if( pRootNode )
|
|
{
|
|
std::vector<CClassInfo*>::iterator itpClass;
|
|
for( itpClass = queryWiz.GetNewClassInfo().begin(); itpClass != queryWiz.GetNewClassInfo().end(); ++itpClass )
|
|
{
|
|
pRootNode->AddClass(*itpClass);
|
|
}
|
|
}
|
|
|
|
// Add the new node
|
|
hr = AddNewChild(spnode, queryWiz.GetQueryName());
|
|
}
|
|
while( FALSE );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CScopeNode::AddGroupNode(LPCONSOLE2 pConsole)
|
|
{
|
|
ASSERT(NodeType() == ROOT_NODETYPE);
|
|
|
|
HRESULT hr;
|
|
do
|
|
{
|
|
// Create a new group node
|
|
CGroupNodePtr spnode;
|
|
hr = CreateNode(GROUP_NODETYPE, reinterpret_cast<CScopeNode**>(&spnode));
|
|
BREAK_ON_FAILURE(hr);
|
|
|
|
// Create Add Group Node dialog
|
|
CAddGroupNodeDlg GrpDlg;
|
|
|
|
// run dialog and add node as child if successful
|
|
if( GrpDlg.DoModal(spnode, g_hwndMain) == IDOK )
|
|
hr = AddNewChild(spnode, GrpDlg.GetNodeName());
|
|
}
|
|
while( FALSE );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CRootNode
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
BEGIN_NOTIFY_MAP(CRootNode)
|
|
ON_NOTIFY(MMCN_CONTEXTHELP, OnHelp)
|
|
ON_PROPERTY_CHANGE()
|
|
CHAIN_NOTIFY_MAP(CScopeNode)
|
|
END_NOTIFY_MAP()
|
|
|
|
HRESULT CRootNode::Initialize(CComponentData* pCompData)
|
|
{
|
|
VALIDATE_POINTER( pCompData );
|
|
m_pCompData = pCompData;
|
|
|
|
tstring strName = StrLoadString(IDS_ROOTNODE);
|
|
RETURN_ON_FAILURE(SetName(strName.c_str()));
|
|
|
|
// Set creation/modify times to now
|
|
GetSystemTimeAsFileTime(&m_ftCreateTime);
|
|
m_ftModifyTime = m_ftCreateTime;
|
|
|
|
WCHAR szName[UNLEN+1];
|
|
DWORD cName = UNLEN+1;
|
|
|
|
// Set owner to current user
|
|
if( GetUserName(szName, &cName) )
|
|
m_strOwner = szName;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CRootNode::OnHelp(LPCONSOLE2 pConsole, LPARAM /*arg*/, LPARAM /*param*/)
|
|
{
|
|
VALIDATE_POINTER( pConsole );
|
|
|
|
tstring strHelpFile = _T("");
|
|
tstring strHelpTopic = _T("");
|
|
tstring strHelpFull = _T("");
|
|
|
|
strHelpFile = StrLoadString(IDS_HELPFILE);
|
|
if( strHelpFile.empty() ) return E_FAIL;
|
|
|
|
// Verify that we got a help topic!
|
|
strHelpTopic = StrLoadString(IDS_DEFAULTHELPTOPIC);
|
|
if( strHelpTopic.empty() ) return E_FAIL;
|
|
|
|
// Build path to %systemroot%\help
|
|
TCHAR szWindowsDir[MAX_PATH+1] = {0};
|
|
UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
|
|
if( nSize == 0 || nSize > MAX_PATH )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
strHelpFull = szWindowsDir;
|
|
strHelpFull += _T("\\Help\\");
|
|
strHelpFull += strHelpFile;
|
|
strHelpFull += _T("::/");
|
|
strHelpFull += strHelpTopic;
|
|
|
|
// Show the Help topic
|
|
CComQIPtr<IDisplayHelp> spHelp = pConsole;
|
|
if( !spHelp ) return E_NOINTERFACE;
|
|
|
|
return spHelp->ShowTopic( (LPTSTR)strHelpFull.c_str() );
|
|
}
|
|
|
|
HRESULT CRootNode::OnPropertyChange(LPCONSOLE2 pConsole, LPARAM lParam)
|
|
{
|
|
VALIDATE_POINTER( lParam );
|
|
string_vector* pvstrClassesChanged = reinterpret_cast<string_vector*>(lParam);
|
|
|
|
// Notify all child nodes of class change
|
|
CScopeNode* pNode = FirstChild();
|
|
while( pNode != NULL )
|
|
{
|
|
ASSERT(pNode->NodeType() == QUERY_NODETYPE || pNode->NodeType() == GROUP_NODETYPE);
|
|
|
|
static_cast<CQueryableNode*>(pNode)->OnClassChange(*pvstrClassesChanged);
|
|
|
|
pNode = pNode->Next();
|
|
}
|
|
|
|
|
|
delete pvstrClassesChanged;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CRootNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
|
|
{
|
|
VALIDATE_POINTER( ppViewType );
|
|
VALIDATE_POINTER( pViewOptions );
|
|
|
|
//Show our homepage snapin in this console
|
|
TCHAR szWindowsDir[MAX_PATH+1] = {0};
|
|
UINT nSize = GetSystemWindowsDirectory( szWindowsDir, MAX_PATH );
|
|
if( nSize == 0 || nSize > MAX_PATH )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
tstring strHomePage = szWindowsDir;
|
|
strHomePage += _T("\\system32\\administration\\servhome.htm");
|
|
|
|
*ppViewType = (TCHAR*)CoTaskMemAlloc((strHomePage.length() + 1) * sizeof(OLECHAR));
|
|
VALIDATE_POINTER( *ppViewType );
|
|
|
|
ocscpy( *ppViewType, T2OLE((LPTSTR)strHomePage.c_str()) );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::LoadNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::LoadNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm >> m_ftCreateTime;
|
|
stm >> m_ftModifyTime;
|
|
|
|
stm >> m_strOwner;
|
|
stm >> m_commentID;
|
|
|
|
stm >> m_vClassInfo;
|
|
|
|
// Root node's Insert() method is never called, so load the name string here
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
hr = StringTableRead(pStringTable, m_nameID, m_strName);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::SaveNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::SaveNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm << m_ftCreateTime;
|
|
stm << m_ftModifyTime;
|
|
|
|
stm << m_strOwner;
|
|
stm << m_commentID;
|
|
|
|
stm << m_vClassInfo;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::GetComment(tstring& strComment)
|
|
{
|
|
if( m_commentID == 0 )
|
|
{
|
|
strComment.erase();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
return StringTableRead(pStringTable, m_commentID, strComment);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::SetComment(LPCWSTR pszComment)
|
|
{
|
|
VALIDATE_POINTER(pszComment);
|
|
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
return StringTableWrite(pStringTable, pszComment, &m_commentID);
|
|
}
|
|
|
|
|
|
CClassInfo* CRootNode::FindClass(LPCWSTR pszClassName)
|
|
{
|
|
if( !pszClassName ) return NULL;
|
|
|
|
classInfo_vector::iterator itClass;
|
|
for( itClass = m_vClassInfo.begin(); itClass != m_vClassInfo.end(); ++itClass )
|
|
{
|
|
if( wcscmp(pszClassName, itClass->Name()) == 0 )
|
|
break;
|
|
}
|
|
|
|
if( itClass == m_vClassInfo.end() )
|
|
return NULL;
|
|
|
|
|
|
// Load any strings before returning the class info, so they will be
|
|
// available when referenced
|
|
IStringTable* pStringTable = GetRootCompData()->GetStringTable();
|
|
if( !pStringTable ) return NULL;
|
|
|
|
itClass->LoadStrings(pStringTable);
|
|
|
|
return itClass;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
|
|
{
|
|
VALIDATE_POINTER( pCallback );
|
|
VALIDATE_POINTER( plAllowed );
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if( *plAllowed & CCM_INSERTIONALLOWED_NEW )
|
|
{
|
|
//hr = AddMenuItem(pCallback, MID_ADDGROUPNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWGROUPFROMROOT"));
|
|
//ASSERT(SUCCEEDED(hr));
|
|
|
|
//hr = AddMenuItem(pCallback, MID_ADDQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWQUERYFROMROOT"));
|
|
//ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CRootNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
|
|
{
|
|
VALIDATE_POINTER(pConsole);
|
|
|
|
HRESULT hr;
|
|
|
|
switch( lCommand )
|
|
{
|
|
case MID_ADDGROUPNODE:
|
|
hr = AddGroupNode(pConsole);
|
|
break;
|
|
|
|
case MID_ADDQUERYNODE:
|
|
hr = AddQueryNode(pConsole);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0 && "Unknown menu command");
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::QueryPagesFor()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CRootNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR lNotifyHandle)
|
|
{
|
|
// Create a share edit list for all the prop pages to reference
|
|
CEditObjList* pObjList = new CEditObjList();
|
|
if( pObjList == NULL ) return E_OUTOFMEMORY;
|
|
|
|
// Keep it alive until prop pages ref it
|
|
pObjList->AddRef();
|
|
|
|
|
|
// Create an instance of each prop page class and call Create on each.
|
|
|
|
// General page
|
|
HPROPSHEETPAGE hpageGen = NULL;
|
|
CRootGeneralPage* pGenPage = new CRootGeneralPage(*pObjList);
|
|
if( pGenPage != NULL )
|
|
{
|
|
hpageGen = pGenPage->Create();
|
|
}
|
|
|
|
// Object page
|
|
HPROPSHEETPAGE hpageObj = NULL;
|
|
CRootObjectPage* pObjPage = new CRootObjectPage(*pObjList);
|
|
if( pObjPage != NULL )
|
|
{
|
|
hpageObj = pObjPage->Create();
|
|
}
|
|
|
|
// Context menu page
|
|
HPROPSHEETPAGE hpageMenu = NULL;
|
|
CRootMenuPage* pMenuPage = new CRootMenuPage(*pObjList);
|
|
if( pMenuPage != NULL )
|
|
{
|
|
hpageMenu = pMenuPage->Create();
|
|
}
|
|
|
|
// Listview page
|
|
HPROPSHEETPAGE hpageView = NULL;
|
|
CRootViewPage* pViewPage = new CRootViewPage(*pObjList);
|
|
if( pViewPage != NULL )
|
|
{
|
|
hpageView = pViewPage->Create();
|
|
}
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
// if all pages were created, add each one to the prop sheet
|
|
if( hpageGen && hpageObj && hpageMenu && hpageView )
|
|
{
|
|
hr = pProvider->AddPage(hpageGen);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageObj);
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageMenu);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageView);
|
|
}
|
|
|
|
// If ok so far, initialilze the common edit list
|
|
// It is now responsible for freeing the notify handle (and itself)
|
|
if( SUCCEEDED(hr) )
|
|
hr = pObjList->Initialize(this, m_vClassInfo, lNotifyHandle);
|
|
|
|
|
|
// On failure, destroy the pages. If a page failed to create
|
|
// then delete the page class object instead (the object is
|
|
// automatically deleted when the page is destroyed)
|
|
if( FAILED(hr) )
|
|
{
|
|
if( hpageGen )
|
|
DestroyPropertySheetPage(hpageGen);
|
|
else
|
|
SAFE_DELETE(pGenPage);
|
|
|
|
if( hpageObj )
|
|
DestroyPropertySheetPage(hpageObj);
|
|
else
|
|
SAFE_DELETE(pObjPage);
|
|
|
|
if( hpageMenu )
|
|
DestroyPropertySheetPage(hpageMenu);
|
|
else
|
|
SAFE_DELETE(pMenuPage);
|
|
|
|
if( hpageView )
|
|
DestroyPropertySheetPage(hpageView);
|
|
else
|
|
SAFE_DELETE(pViewPage);
|
|
}
|
|
|
|
// Release temp ref on edit list
|
|
// it will go away when the prop pages release it
|
|
pObjList->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CRootNode::GetWatermarks(HBITMAP* phWatermark, HBITMAP* phHeader,
|
|
HPALETTE* phPalette, BOOL* bStreach)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CQueryableNode
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CQueryableNode::AttachComponent(CComponent* pComponent)
|
|
{
|
|
VALIDATE_POINTER( pComponent );
|
|
|
|
HRESULT hr = CScopeNode::AttachComponent(pComponent);
|
|
if( hr != S_OK )
|
|
return hr;
|
|
|
|
// Get attributes query will collect
|
|
attrib_map mapAttr;
|
|
|
|
hr = GetQueryAttributes(mapAttr);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
// Add column header for each attribute
|
|
IHeaderCtrl* pHdrCtrl = pComponent->GetHeaderCtrl();
|
|
ASSERT( pHdrCtrl );
|
|
if( !pHdrCtrl ) return E_FAIL;
|
|
|
|
int iPos = 0;
|
|
|
|
// Always add Name and Type columns first
|
|
CString strName;
|
|
strName.LoadString(IDS_NAME);
|
|
pHdrCtrl->InsertColumn(iPos++, strName, LVCFMT_LEFT, 200);
|
|
|
|
strName.LoadString(IDS_TYPE);
|
|
pHdrCtrl->InsertColumn(iPos++, strName, LVCFMT_LEFT, 100);
|
|
|
|
// Add user selected attributes next (use display name which is the map value)
|
|
attrib_map::iterator itCol;
|
|
for( itCol = mapAttr.begin(); itCol != mapAttr.end(); itCol++ )
|
|
{
|
|
pHdrCtrl->InsertColumn(iPos++, itCol->second, LVCFMT_LEFT, 150);
|
|
}
|
|
|
|
// if need to execute query and one is not in progress
|
|
if( m_bQueryChange && m_pQueryReq == NULL )
|
|
{
|
|
// Create vector of query attribute names
|
|
m_vstrColumns.clear();
|
|
|
|
if( mapAttr.size() != 0 )
|
|
{
|
|
m_vstrColumns.reserve(mapAttr.size());
|
|
|
|
attrib_map::iterator itAttr;
|
|
for( itAttr = mapAttr.begin(); itAttr != mapAttr.end(); itAttr++ )
|
|
m_vstrColumns.push_back(itAttr->first);
|
|
}
|
|
|
|
// Clear previous query items
|
|
ClearQueryRowItems();
|
|
|
|
// Start the query
|
|
hr = StartQuery(m_vstrColumns, this, &m_pQueryReq);
|
|
|
|
// if query started (note group node returns S_FALSE if no children)
|
|
if( hr == S_OK )
|
|
{
|
|
// Enable stop query button for all attached components
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
|
|
{
|
|
IToolbar* pToolbar = (*itComp)->GetToolbar();
|
|
if( pToolbar )
|
|
pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, TRUE);
|
|
}
|
|
}
|
|
|
|
// allow node to be attached even if query fails
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Replace component's row items with ours
|
|
pComponent->ClearRowItems();
|
|
pComponent->AddRowItems(m_vRowItems);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CQueryableNode::ClearQueryRowItems()
|
|
{
|
|
// discard local row items
|
|
m_vRowItems.clear();
|
|
|
|
// Clear all attached components rows
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
|
|
(*itComp)->ClearRowItems();
|
|
}
|
|
|
|
|
|
void CQueryableNode::QueryCallback(QUERY_NOTIFY event, CQueryRequest* pQueryReq, LPARAM lUserParam)
|
|
{
|
|
ASSERT(pQueryReq && pQueryReq == m_pQueryReq);
|
|
if( !pQueryReq || pQueryReq != m_pQueryReq ) return;
|
|
|
|
CString strMsgFmt;
|
|
|
|
switch( event )
|
|
{
|
|
case QRYN_NEWROWITEMS:
|
|
{
|
|
// Get new row items
|
|
RowItemVector& newRows = pQueryReq->GetNewRowItems();
|
|
|
|
DisplayNameMap* pNameMap = DisplayNames::GetClassMap(); //use for icon/class lookup
|
|
if( !pNameMap ) return;
|
|
|
|
LPCWSTR strClassName; //holds the name of the current lookup class
|
|
static tstring strLastName; //holds the last lookup class name
|
|
static ICONHOLDER* pLastIcons = NULL; //holds the indices of the last icon lookup
|
|
|
|
// Attach owner query node (passed as user param) to each row item
|
|
for( RowItemVector::iterator itRow = newRows.begin(); itRow != newRows.end(); ++itRow )
|
|
{
|
|
itRow->SetOwnerParam(lUserParam);
|
|
|
|
//establish icon virtual index
|
|
strClassName = (*itRow)[ROWITEM_CLASS_INDEX];
|
|
if( strLastName.compare(strClassName) != 0 )
|
|
{
|
|
//new class type requested. Load from namemap and cache.
|
|
pNameMap->GetIcons(strClassName, &pLastIcons);
|
|
strLastName = strClassName;
|
|
|
|
}
|
|
|
|
//use the cached normal/disabled icon depending on object state
|
|
if( pLastIcons )
|
|
{
|
|
if( itRow->Disabled() )
|
|
itRow->SetIconIndex(pLastIcons->iDisabled);
|
|
else
|
|
itRow->SetIconIndex(pLastIcons->iNormal);
|
|
}
|
|
}
|
|
|
|
// Add to node's vector
|
|
m_vRowItems.insert(m_vRowItems.end(), newRows.begin(), newRows.end());
|
|
|
|
// Add to all attach components
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
|
|
(*itComp)->AddRowItems(newRows);
|
|
|
|
// Free the rows
|
|
pQueryReq->ReleaseNewRowItems();
|
|
|
|
strMsgFmt.LoadString(IDS_SEARCHING);
|
|
break;
|
|
}
|
|
|
|
case QRYN_COMPLETED:
|
|
m_bQueryChange = FALSE;
|
|
strMsgFmt.LoadString(IDS_QUERYDONE);
|
|
break;
|
|
|
|
case QRYN_STOPPED:
|
|
strMsgFmt.LoadString(IDS_QUERYSTOPPED);
|
|
break;
|
|
|
|
case QRYN_FAILED:
|
|
strMsgFmt.LoadString(IDS_QUERYFAILED);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
// if components attached, display query progress
|
|
if( m_vComponents.size() != 0 )
|
|
{
|
|
CString strMsg;
|
|
strMsg.Format(strMsgFmt, m_vRowItems.size());
|
|
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); ++itComp )
|
|
{
|
|
(*itComp)->GetConsole()->SetStatusText((LPWSTR)(LPCWSTR)strMsg);
|
|
}
|
|
}
|
|
|
|
|
|
// if query terminated, do cleanup
|
|
if( event != QRYN_NEWROWITEMS )
|
|
{
|
|
pQueryReq->Release();
|
|
m_pQueryReq = NULL;
|
|
|
|
// disable query stop button for all components
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); ++itComp )
|
|
{
|
|
IToolbar* pToolbar = (*itComp)->GetToolbar();
|
|
if( pToolbar )
|
|
pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT CQueryableNode::DetachComponent(CComponent* pComponent)
|
|
{
|
|
VALIDATE_POINTER( pComponent );
|
|
|
|
HRESULT hr = CScopeNode::DetachComponent(pComponent);
|
|
if( hr != S_OK )
|
|
{
|
|
return FAILED(hr) ? hr : E_FAIL;
|
|
}
|
|
|
|
// if that was the last one, stop active query
|
|
if( m_vComponents.size() == 0 && m_pQueryReq != NULL )
|
|
m_pQueryReq->Stop(TRUE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CQueryableNode::OnRefresh(LPCONSOLE2 pCons)
|
|
{
|
|
// if query in progress stop it
|
|
if( m_pQueryReq != NULL )
|
|
{
|
|
m_pQueryReq->Stop(TRUE);
|
|
}
|
|
|
|
// Set change flag to force new query
|
|
m_bQueryChange = TRUE;
|
|
|
|
|
|
// Have each attached component reselect this node
|
|
std::vector<CComponent*>::iterator itComp;
|
|
for( itComp = m_vComponents.begin(); itComp != m_vComponents.end(); itComp++ )
|
|
{
|
|
// Here's a kludge to get around an MMC bug. If the snap-in reselects its scope node
|
|
// while the focus is on a taskpad background, then MMC sends a deselect/select
|
|
// sequence to the snap-in causing it to enable its verbs. But if the user then clicks
|
|
// an enabled tool button (e.g., Rename) nothing happens other than an MMC assert
|
|
// because MMC thinks nothing is selected.
|
|
//
|
|
// The fix is to check the state of the properties verbs prior to doing a reselect.
|
|
// If the verb is disabled then don't enabled it (or any other verbs) when the select
|
|
// notify is received. This has to be done per component because each may have a
|
|
// different pane focused.
|
|
|
|
CComPtr<IConsoleVerb> spConsVerb;
|
|
(*itComp)->GetConsole()->QueryConsoleVerb(&spConsVerb);
|
|
|
|
ASSERT(spConsVerb != NULL);
|
|
if( spConsVerb != NULL )
|
|
{
|
|
// Ignore select notify if verbs are disabled before the reselect
|
|
static BOOL bEnabled;
|
|
if( spConsVerb->GetVerbState(MMC_VERB_PROPERTIES, ENABLED, &bEnabled) == S_OK )
|
|
{
|
|
m_bIgnoreSelect = !bEnabled;
|
|
}
|
|
}
|
|
|
|
(*itComp)->GetConsole()->SelectScopeItem(m_hScopeItem);
|
|
|
|
// Go back to normal select processing
|
|
ASSERT(!m_bIgnoreSelect);
|
|
m_bIgnoreSelect = FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CGroupNode
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_NOTIFY_MAP(CGroupNode)
|
|
ON_REFRESH()
|
|
ON_DELETE()
|
|
ON_ADD_IMAGES()
|
|
CHAIN_NOTIFY_MAP(CScopeNode)
|
|
END_NOTIFY_MAP()
|
|
|
|
HRESULT CGroupNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
|
|
{
|
|
VALIDATE_POINTER( plAllowed );
|
|
|
|
BOOL bRes = TRUE;
|
|
|
|
if( *plAllowed & CCM_INSERTIONALLOWED_NEW )
|
|
{
|
|
bRes = AddMenuItem(pCallback, MID_ADDQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, _T("NEWQUERYFROMGROUP"));
|
|
ASSERT(bRes);
|
|
}
|
|
|
|
return bRes ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT CGroupNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
|
|
{
|
|
VALIDATE_POINTER(pConsole);
|
|
|
|
HRESULT hr;
|
|
|
|
switch( lCommand )
|
|
{
|
|
|
|
case MID_ADDQUERYNODE:
|
|
hr = AddQueryNode(pConsole);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0 && "Unknown menu command");
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CGroupNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
|
|
{
|
|
VALIDATE_POINTER( pViewOptions );
|
|
|
|
*pViewOptions = MMC_VIEW_OPTIONS_OWNERDATALIST;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT CGroupNode::QueryPagesFor()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CGroupNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR handle)
|
|
{
|
|
// Create a group edit object
|
|
CGroupEditObj* pEditObj = new CGroupEditObj(this);
|
|
if( !pEditObj ) return E_OUTOFMEMORY;
|
|
|
|
// Create an instance of each prop page class and call Create on each.
|
|
|
|
// Keep it alive until prop pages ref it
|
|
pEditObj->AddRef();
|
|
|
|
// General page
|
|
HPROPSHEETPAGE hpageGen = NULL;
|
|
CGroupGeneralPage* pGenPage = new CGroupGeneralPage(pEditObj);
|
|
if( pGenPage != NULL )
|
|
{
|
|
hpageGen = pGenPage->Create();
|
|
}
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// if all pages were created, add each one to the prop sheet
|
|
if( hpageGen )
|
|
{
|
|
hr = pProvider->AddPage(hpageGen);
|
|
}
|
|
|
|
// On failure, destroy the pages. If a page failed to create
|
|
// then delete the page class object instead (the object is
|
|
// automatically deleted when the page is destroyed)
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
if( hpageGen )
|
|
DestroyPropertySheetPage(hpageGen);
|
|
else
|
|
SAFE_DELETE(pGenPage);
|
|
}
|
|
|
|
// Release temp ref on edit list
|
|
// it will go away when the prop pages release it
|
|
pEditObj->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CGroupNode::GetQueryAttributes(attrib_map& mapAttr)
|
|
{
|
|
// Get union of attributes for child query nodes
|
|
CScopeNode* pNode = FirstChild();
|
|
while( pNode != NULL )
|
|
{
|
|
ASSERT(pNode->NodeType() == QUERY_NODETYPE);
|
|
CQueryNode* pQNode = static_cast<CQueryNode*>(pNode);
|
|
|
|
// if query defined for this node, get the attributes
|
|
if( pQNode->Query() && pQNode->Query()[0] )
|
|
pQNode->GetQueryAttributes(mapAttr);
|
|
|
|
pNode = pNode->Next();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CGroupNode::StartQuery(string_vector& vstrColumns, CQueryCallback* pCallback, CQueryRequest** ppReq)
|
|
{
|
|
VALIDATE_POINTER( pCallback );
|
|
VALIDATE_POINTER( ppReq );
|
|
|
|
*ppReq = NULL;
|
|
|
|
// if no children, there is no query to perform
|
|
if( FirstChild() == NULL )
|
|
return S_FALSE;
|
|
|
|
ASSERT(FirstChild()->NodeType() == QUERY_NODETYPE);
|
|
CQueryNode* pQNode = static_cast<CQueryNode*>(FirstChild());
|
|
|
|
// Start a query one the first one
|
|
HRESULT hr = pQNode->StartQuery(vstrColumns, pCallback, ppReq);
|
|
|
|
// Save pointer to active query node for callback handler
|
|
if( SUCCEEDED(hr) )
|
|
m_pQNodeActive = pQNode;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CGroupNode::QueryCallback(QUERY_NOTIFY event, CQueryRequest* pQueryReq, LPARAM lUserParam)
|
|
{
|
|
if( !pQueryReq || !m_pQNodeActive || (pQueryReq != m_pQueryReq) ) return;
|
|
|
|
// if current query is complete and there are more child query nodes
|
|
if( event == QRYN_COMPLETED && m_pQNodeActive->Next() != NULL )
|
|
{
|
|
CQueryNode* pQNodeNext = static_cast<CQueryNode*>(m_pQNodeActive->Next());
|
|
|
|
// Start a query on the next child node
|
|
CQueryRequest* pReqNew = NULL;
|
|
HRESULT hr = pQNodeNext->StartQuery(m_vstrColumns, this, &pReqNew);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Release the current query node and save new query and node
|
|
pQueryReq->Release();
|
|
m_pQueryReq = pReqNew;
|
|
|
|
m_pQNodeActive = pQNodeNext;
|
|
|
|
// Bypass normal query termination processing
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Do common callback processing
|
|
CQueryableNode::QueryCallback(event, pQueryReq, lUserParam);
|
|
}
|
|
|
|
|
|
void CGroupNode::EnableVerbs(IConsoleVerb* pConsVerb, BOOL bOwnsView)
|
|
{
|
|
if( bOwnsView && pConsVerb )
|
|
{
|
|
pConsVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CGroupNode::OnDelete(LPCONSOLE2 pConsole)
|
|
{
|
|
// Get namespace interface
|
|
IConsoleNameSpacePtr spNameSpace = pConsole;
|
|
ASSERT(spNameSpace != NULL);
|
|
if( spNameSpace == NULL )
|
|
return E_FAIL;
|
|
|
|
// Get confirmation from user before deleting node
|
|
CString strTitle;
|
|
strTitle.LoadString(IDS_DELETENODE_TITLE);
|
|
|
|
CString strMsgFmt;
|
|
strMsgFmt.LoadString(IDS_DELETEGROUPNODE);
|
|
|
|
CString strMsg;
|
|
strMsg.Format(strMsgFmt, GetName());
|
|
|
|
int iRet;
|
|
HRESULT hr = pConsole->MessageBox(strMsg, strTitle, MB_YESNOCANCEL|MB_ICONWARNING, &iRet);
|
|
|
|
if( SUCCEEDED(hr) && (iRet == IDYES || iRet == IDNO) )
|
|
{
|
|
// if No, move child nodes up one level before deleting this node
|
|
if( iRet == IDNO )
|
|
{
|
|
// Move each child to the parent of this node
|
|
CScopeNode* pnodeChild = m_pnodeChild;
|
|
while( pnodeChild != NULL )
|
|
{
|
|
// Detach each child from old list before adding it
|
|
CScopeNode* pnodeNext = pnodeChild->m_pnodeNext;
|
|
pnodeChild->m_pnodeNext = NULL;
|
|
|
|
// clear the item handle associated with the old position
|
|
// MMC will provide a new one when the node is added
|
|
pnodeChild->m_hScopeItem = NULL;
|
|
|
|
Parent()->AddChild(pnodeChild);
|
|
|
|
// Release because new parent has ref'd it
|
|
pnodeChild->Release();
|
|
|
|
pnodeChild = pnodeNext;
|
|
}
|
|
|
|
// Set child list to NULL
|
|
m_pnodeChild = NULL;
|
|
}
|
|
|
|
// Tell MMC to delete this node and all the children
|
|
ASSERT(m_hScopeItem != 0);
|
|
hr = spNameSpace->DeleteItem(m_hScopeItem, TRUE);
|
|
|
|
// Caution: this call will usually delete this object,
|
|
// so don't access any members after making it
|
|
if( SUCCEEDED(hr) )
|
|
hr = Parent()->RemoveChild(this);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CGroupNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
|
|
{
|
|
|
|
CScopeNode* pNode = FirstChild();
|
|
while( pNode != NULL )
|
|
{
|
|
ASSERT(pNode->NodeType() == QUERY_NODETYPE);
|
|
static_cast<CQueryNode*>(pNode)->OnAddImages(pConsole, pImageList);
|
|
pNode = pNode->Next();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL
|
|
CGroupNode::OnClassChange(string_vector& vstrClasses)
|
|
{
|
|
|
|
BOOL bChanged = FALSE;
|
|
|
|
// Notify all child query nodes of class change
|
|
CScopeNode* pnode = FirstChild();
|
|
while( pnode != NULL )
|
|
{
|
|
// Set change flag if any child node has changed
|
|
ASSERT(pnode->NodeType() == QUERY_NODETYPE);
|
|
|
|
bChanged |= static_cast<CQueryNode*>(pnode)->OnClassChange(vstrClasses);
|
|
|
|
pnode = pnode->m_pnodeNext;
|
|
}
|
|
|
|
// if any child has changed, need to rerun the group query
|
|
if( bChanged )
|
|
OnRefresh(NULL);
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CGroupNode::LoadNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::LoadNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm >> m_strScope;
|
|
stm >> m_strFilter;
|
|
stm >> m_bApplyScope;
|
|
stm >> m_bApplyFilter;
|
|
stm >> m_bLocalScope;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CGroupNode::SaveNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::SaveNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm << m_strScope;
|
|
stm << m_strFilter;
|
|
stm << m_bApplyScope;
|
|
stm << m_bApplyFilter;
|
|
stm << m_bLocalScope;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CQueryNode
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
BEGIN_NOTIFY_MAP(CQueryNode)
|
|
ON_REFRESH()
|
|
ON_DELETE()
|
|
ON_ADD_IMAGES()
|
|
CHAIN_NOTIFY_MAP(CScopeNode)
|
|
END_NOTIFY_MAP()
|
|
|
|
|
|
HRESULT CQueryNode::GetResultViewType(LPOLESTR* ppViewType, long* pViewOptions)
|
|
{
|
|
VALIDATE_POINTER( pViewOptions );
|
|
|
|
*pViewOptions = MMC_VIEW_OPTIONS_OWNERDATALIST;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::GetDisplayInfo(RESULTDATAITEM* pRDI)
|
|
{
|
|
VALIDATE_POINTER( pRDI );
|
|
|
|
if( !pRDI->bScopeItem )
|
|
{
|
|
if( pRDI->nIndex < 0 || pRDI->nIndex >= m_vRowItems.size() )
|
|
return E_INVALIDARG;
|
|
|
|
if( pRDI->mask & RDI_STR )
|
|
pRDI->str = const_cast<LPWSTR>(m_vRowItems[pRDI->nIndex][pRDI->nCol]);
|
|
|
|
if( pRDI->mask & RDI_IMAGE )
|
|
pRDI->nImage = RESULT_ITEM_IMAGE;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return CScopeNode::GetDisplayInfo(pRDI);
|
|
}
|
|
}
|
|
|
|
HRESULT CQueryNode::GetClassMenuItems(LPCWSTR pszClass, menucmd_vector& vMenus, int* piDefault, BOOL* pbPropertyMenu)
|
|
{
|
|
VALIDATE_POINTER( pszClass );
|
|
VALIDATE_POINTER( piDefault );
|
|
VALIDATE_POINTER( pbPropertyMenu );
|
|
|
|
*piDefault = -1;
|
|
*pbPropertyMenu = TRUE;
|
|
|
|
QueryObjVector::iterator itQObj;
|
|
for( itQObj = Objects().begin(); itQObj != Objects().end(); ++itQObj )
|
|
{
|
|
if( wcscmp(itQObj->Name(), pszClass) == 0 )
|
|
break;
|
|
}
|
|
|
|
if( itQObj == Objects().end() )
|
|
return S_FALSE;
|
|
|
|
CRootNode* pRootNode = GetRootNode();
|
|
if( pRootNode == NULL )
|
|
return S_FALSE;
|
|
|
|
CClassInfo* pClassInfo = pRootNode->FindClass(pszClass);
|
|
if( pClassInfo == NULL )
|
|
return S_FALSE;
|
|
|
|
menuref_vector& vMenuRefs = itQObj->MenuRefs();
|
|
menuref_vector::iterator itMenuRef;
|
|
|
|
menucmd_vector& vMenuCmds = pClassInfo->Menus();
|
|
menucmd_vector::iterator itMenuCmd;
|
|
|
|
// First add all root menu items that preceed the first query menu item
|
|
for( itMenuCmd = vMenuCmds.begin(); itMenuCmd != vMenuCmds.end(); ++itMenuCmd )
|
|
{
|
|
if( std::find(vMenuRefs.begin(), vMenuRefs.end(), (*itMenuCmd)->ID()) != vMenuRefs.end() )
|
|
break;
|
|
|
|
vMenus.push_back(*itMenuCmd);
|
|
}
|
|
|
|
// For each query menu item
|
|
for( itMenuRef = vMenuRefs.begin(); itMenuRef != vMenuRefs.end(); ++itMenuRef )
|
|
{
|
|
// Find the root menu item by name
|
|
for( itMenuCmd = vMenuCmds.begin(); itMenuCmd != vMenuCmds.end(); ++itMenuCmd )
|
|
{
|
|
if( (*itMenuCmd)->ID() == itMenuRef->ID() )
|
|
break;
|
|
}
|
|
|
|
// if item was deleted at the root node, then skip it
|
|
if( itMenuCmd == vMenuCmds.end() )
|
|
continue;
|
|
|
|
// If item is enabled at query level add it to the list
|
|
if( itMenuRef->IsEnabled() )
|
|
{
|
|
vMenus.push_back(*itMenuCmd);
|
|
|
|
if( itMenuRef->IsDefault() )
|
|
*piDefault = vMenus.size() - 1;
|
|
}
|
|
|
|
++itMenuCmd;
|
|
|
|
// Add any following root items that aren't in the query list
|
|
while( itMenuCmd != vMenuCmds.end() &&
|
|
std::find(vMenuRefs.begin(), vMenuRefs.end(), (*itMenuCmd)->ID()) == vMenuRefs.end() )
|
|
{
|
|
vMenus.push_back(*itMenuCmd);
|
|
++itMenuCmd;
|
|
}
|
|
}
|
|
|
|
*pbPropertyMenu = itQObj->HasPropertyMenu();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CQueryNode::GetQueryAttributes(attrib_map& mapAttr)
|
|
{
|
|
CRootNode* pRootNode = GetRootNode();
|
|
if( !pRootNode ) return E_UNEXPECTED;
|
|
|
|
QueryObjVector::iterator itQObj;
|
|
for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
|
|
{
|
|
// skip classes that aren't defined at the root
|
|
CClassInfo* pClassInfo = pRootNode->FindClass(itQObj->Name());
|
|
if( pClassInfo == NULL )
|
|
continue;
|
|
|
|
// get display name map for this class
|
|
DisplayNameMap* pNameMap = DisplayNames::GetMap(itQObj->Name());
|
|
ASSERT(pNameMap != NULL);
|
|
if( pNameMap == NULL )
|
|
continue;
|
|
|
|
// Use all attributes defined at the root level that aren't disabled at the query level
|
|
string_vector& vstrDisabled = itQObj->DisabledColumns();
|
|
string_vector::iterator itCol;
|
|
for( itCol = pClassInfo->Columns().begin(); itCol != pClassInfo->Columns().end(); ++itCol )
|
|
{
|
|
if( std::find(vstrDisabled.begin(), vstrDisabled.end(), *itCol) == vstrDisabled.end() )
|
|
{
|
|
mapAttr.insert(attrib_map::value_type
|
|
(itCol->c_str(), pNameMap->GetAttributeDisplayName(itCol->c_str())));
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::StartQuery(string_vector& vstrColumns, CQueryCallback* pQueryCallback, CQueryRequest** ppQueryReq)
|
|
{
|
|
VALIDATE_POINTER( pQueryCallback );
|
|
VALIDATE_POINTER( ppQueryReq );
|
|
|
|
*ppQueryReq = NULL;
|
|
|
|
// Create a query request object
|
|
CQueryRequest* pQueryReq = NULL;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get query scope and filter
|
|
LPCWSTR pszScope = Scope();
|
|
|
|
tstring strTempFilter;
|
|
ExpandQuery(strTempFilter);
|
|
LPCWSTR pszFilter = strTempFilter.c_str();
|
|
|
|
CString strJointFilter;
|
|
|
|
// Check for scope or filter override by parent group node
|
|
if( Parent()->NodeType() == GROUP_NODETYPE )
|
|
{
|
|
CGroupNode* pGrpNode = static_cast<CGroupNode*>(Parent());
|
|
|
|
// if group imposed scope, use it instead
|
|
if( pGrpNode->ApplyScope() )
|
|
pszScope = pGrpNode->Scope();
|
|
|
|
// if group imposed filter, AND it with query filter
|
|
if( pGrpNode->ApplyFilter() )
|
|
{
|
|
strJointFilter.Format(L"(&(%s)(%s))", strTempFilter.c_str(), pGrpNode->Filter());
|
|
pszFilter = strJointFilter;
|
|
}
|
|
}
|
|
|
|
// Get list of object classes expected from query
|
|
string_vector vstrClasses;
|
|
QueryObjVector::iterator itQObj;
|
|
for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
|
|
vstrClasses.push_back(itQObj->Name());
|
|
|
|
// Set query parameters
|
|
hr = CQueryRequest::CreateInstance(&pQueryReq);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
pQueryReq->SetQueryParameters(pszScope, pszFilter, &vstrClasses, &vstrColumns);
|
|
|
|
// Set search preferences
|
|
ADS_SEARCHPREF_INFO srchPrefs[3];
|
|
|
|
srchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
|
|
srchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
|
|
srchPrefs[0].vValue.Integer = ADS_SCOPE_SUBTREE;
|
|
|
|
srchPrefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
|
|
srchPrefs[1].vValue.dwType = ADSTYPE_INTEGER;
|
|
srchPrefs[1].vValue.Integer = 32;
|
|
|
|
srchPrefs[2].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
|
|
srchPrefs[2].vValue.dwType = ADSTYPE_BOOLEAN;
|
|
srchPrefs[2].vValue.Boolean = TRUE;
|
|
|
|
pQueryReq->SetSearchPreferences(srchPrefs, lengthof(srchPrefs));
|
|
|
|
// Set callback info (pass query node ptr as parameter)
|
|
pQueryReq->SetCallback(pQueryCallback, (LPARAM)this);
|
|
|
|
// Start query
|
|
hr = pQueryReq->Start();
|
|
}
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Return active query pointer
|
|
*ppQueryReq = pQueryReq;
|
|
}
|
|
|
|
if( FAILED(hr) && pQueryReq )
|
|
{
|
|
pQueryReq->Release();
|
|
pQueryReq = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CQueryNode::GetComment(tstring& strComment)
|
|
{
|
|
if( m_commentID == 0 )
|
|
{
|
|
strComment.erase();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT(pStringTable != NULL);
|
|
|
|
return StringTableRead(pStringTable, m_commentID, strComment);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::SetComment(LPCWSTR pszComment)
|
|
{
|
|
VALIDATE_POINTER(pszComment);
|
|
|
|
IStringTable* pStringTable = GetCompData()->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
return StringTableWrite(pStringTable, pszComment, &m_commentID);
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::OnDelete(LPCONSOLE2 pConsole)
|
|
{
|
|
// Get namespace interface
|
|
IConsoleNameSpacePtr spNameSpace = pConsole;
|
|
ASSERT(spNameSpace != NULL);
|
|
if( spNameSpace == NULL )
|
|
return E_FAIL;
|
|
|
|
// Get confirmation from user before deleting node
|
|
CString strTitle;
|
|
strTitle.LoadString(IDS_DELETENODE_TITLE);
|
|
|
|
CString strMsgFmt;
|
|
strMsgFmt.LoadString(IDS_DELETEQUERYNODE);
|
|
|
|
CString strMsg;
|
|
strMsg.Format(strMsgFmt, GetName());
|
|
|
|
int iRet;
|
|
HRESULT hr = pConsole->MessageBox(strMsg, strTitle, MB_YESNO|MB_ICONWARNING, &iRet);
|
|
if( SUCCEEDED(hr) && iRet == IDYES )
|
|
{
|
|
ASSERT(m_hScopeItem != 0);
|
|
hr = spNameSpace->DeleteItem(m_hScopeItem, TRUE);
|
|
|
|
// Caution: this call will usually delete this object,
|
|
// so don't access any members after making it
|
|
if( SUCCEEDED(hr) )
|
|
hr = Parent()->RemoveChild(this);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL CQueryNode::OnClassChange(string_vector& vstrClasses)
|
|
{
|
|
BOOL bChanged = FALSE;
|
|
|
|
// Check if this node's query returns objects of a changed class
|
|
// (object types returned by the query will be in the ObjInfo vector)
|
|
QueryObjVector::iterator itQObj;
|
|
for( itQObj = m_vObjInfo.begin(); itQObj != m_vObjInfo.end(); ++itQObj )
|
|
{
|
|
if( std::find(vstrClasses.begin(), vstrClasses.end(), itQObj->Name()) != vstrClasses.end() )
|
|
{
|
|
bChanged = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if a queried class has changed, refresh the query
|
|
if( bChanged )
|
|
OnRefresh(NULL);
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
HRESULT CQueryNode::OnAddImages(LPCONSOLE2 pConsole, LPIMAGELIST pImageList)
|
|
{
|
|
VALIDATE_POINTER(pImageList);
|
|
|
|
std::vector<CQueryObjInfo>::iterator vecIter;
|
|
DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
|
|
if( !pNameMap ) return E_FAIL;
|
|
|
|
ICONHOLDER* pIH = NULL;
|
|
|
|
// iterate through classes to be displayed. Call the global namemap
|
|
// to determine icons for each class. Load both large and small icons.
|
|
for( vecIter = m_vObjInfo.begin(); vecIter != m_vObjInfo.end(); vecIter++ )
|
|
{
|
|
//check for class name in namemap
|
|
if( pNameMap->GetIcons(pNameMap->GetFriendlyName(vecIter->Name()), &pIH) && pIH )
|
|
{
|
|
//verify normal icon exists
|
|
if( pIH->hSmall )
|
|
{
|
|
pImageList->ImageListSetIcon((LONG_PTR *)pIH->hSmall, pIH->iNormal); // add small icon
|
|
pImageList->ImageListSetIcon((LONG_PTR *)pIH->hLarge, ILSI_LARGE_ICON(pIH->iNormal)); // add large icon
|
|
}
|
|
//verify disabled icon exists
|
|
if( pIH->hSmallDis )
|
|
{
|
|
pImageList->ImageListSetIcon((LONG_PTR *)pIH->hSmallDis, pIH->iDisabled); // add small disabled icon
|
|
pImageList->ImageListSetIcon((LONG_PTR *)pIH->hLargeDis, ILSI_LARGE_ICON(pIH->iDisabled)); // add large disabled icon
|
|
}
|
|
}
|
|
}
|
|
return CScopeNode::OnAddImages(pConsole, pImageList); //add default images too
|
|
}
|
|
|
|
HRESULT
|
|
CQueryNode::LoadNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::LoadNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm >> m_strScope;
|
|
stm >> m_strQuery;
|
|
stm >> m_bsQueryData;
|
|
stm >> m_commentID;
|
|
stm >> m_vObjInfo;
|
|
stm >> m_bLocalScope;
|
|
stm >> m_vMenus;
|
|
|
|
if( g_dwFileVer >= 150 )
|
|
{
|
|
stm >> m_nIconIndex; //Load the icon
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::SaveNode(IStream& stm)
|
|
{
|
|
HRESULT hr = CScopeNode::SaveNode(stm);
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
stm << m_strScope;
|
|
stm << m_strQuery;
|
|
stm << m_bsQueryData;
|
|
stm << m_commentID;
|
|
stm << m_vObjInfo;
|
|
stm << m_bLocalScope;
|
|
stm << m_vMenus;
|
|
stm << m_nIconIndex;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CQueryNode::EnableVerbs(IConsoleVerb* pConsVerb, BOOL bOwnsView)
|
|
{
|
|
if( bOwnsView && pConsVerb )
|
|
{
|
|
pConsVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::AddMenuItems(LPCONTEXTMENUCALLBACK pCallback, long* plAllowed)
|
|
{
|
|
VALIDATE_POINTER( plAllowed );
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CComQIPtr<IContextMenuCallback2> spContext2 = pCallback;
|
|
if( !spContext2 ) return E_NOINTERFACE;
|
|
|
|
if( *plAllowed & CCM_INSERTIONALLOWED_TOP )
|
|
{
|
|
// Add our new Querynode menus
|
|
// Make sure our strings are loaded.
|
|
CRootNode* pRootNode = GetRootNode();
|
|
if( !pRootNode ) return E_FAIL;
|
|
|
|
CComponentData* pCompData = pRootNode->GetCompData();
|
|
if( !pCompData ) return E_FAIL;
|
|
|
|
IStringTable* pStringTable = pCompData->GetStringTable();
|
|
ASSERT( pStringTable );
|
|
if( !pStringTable ) return E_FAIL;
|
|
|
|
LoadStrings(pStringTable);
|
|
|
|
menucmd_vector::iterator itMenu;
|
|
long lCmdID = 0;
|
|
for( itMenu = m_vMenus.begin(); itMenu != m_vMenus.end(); ++itMenu, ++lCmdID )
|
|
{
|
|
CONTEXTMENUITEM2 item;
|
|
OLECHAR szGuid[50] = {0};
|
|
|
|
::StringFromGUID2((*itMenu)->NoLocID(), szGuid, 50);
|
|
|
|
item.strName = const_cast<LPWSTR>((*itMenu)->Name());
|
|
item.strStatusBarText = L"";
|
|
item.lCommandID = lCmdID;
|
|
item.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
|
|
item.fFlags = 0;
|
|
item.fSpecialFlags = 0;
|
|
item.strLanguageIndependentName = szGuid;
|
|
|
|
hr = spContext2->AddItem(&item);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
//hr = AddMenuItem(pCallback, MID_EDITQUERY, CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, _T("EDITQUERY"));
|
|
//ASSERT(SUCCEEDED(hr));
|
|
|
|
long lFlags = (m_pQueryReq != NULL) ? MF_ENABLED : MF_GRAYED;
|
|
BOOL bRes = AddMenuItem(pCallback, MID_STOPQUERY, CCM_INSERTIONPOINTID_PRIMARY_TOP, lFlags, _T("STOPQUERY"));
|
|
hr = bRes ? S_OK : E_FAIL;
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
// Show the "Move to" menu item if there is at least one group node
|
|
CScopeNode* pnode = pRootNode->FirstChild();
|
|
while( pnode != NULL )
|
|
{
|
|
if( pnode->NodeType() == GROUP_NODETYPE )
|
|
{
|
|
bRes = AddMenuItem(pCallback, MID_MOVEQUERYNODE, CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, _T("MOVEQUERY"));
|
|
hr = bRes ? S_OK : E_FAIL;
|
|
ASSERT(SUCCEEDED(hr));
|
|
break;
|
|
}
|
|
pnode = pnode->Next();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::SetToolButtons(LPTOOLBAR pToolbar)
|
|
{
|
|
VALIDATE_POINTER( pToolbar );
|
|
|
|
pToolbar->SetButtonState(MID_EDITQUERY, ENABLED, TRUE);
|
|
pToolbar->SetButtonState(MID_EDITQUERY, HIDDEN, FALSE);
|
|
|
|
pToolbar->SetButtonState(MID_STOPQUERY, ENABLED, (m_pQueryReq != NULL));
|
|
pToolbar->SetButtonState(MID_STOPQUERY, HIDDEN, FALSE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CQueryNode::EditQuery(HWND hWndParent)
|
|
{
|
|
tstring strQueryTmp;
|
|
tstring strScopeTmp = Scope();
|
|
ExpandQuery(strQueryTmp);
|
|
|
|
HRESULT hr = GetQuery(strScopeTmp, strQueryTmp, m_bsQueryData, hWndParent);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
DisplayMessageBox(NULL, IDS_ERRORTITLE_EDITQUERY, IDS_ERROR_EDITQUERY,
|
|
MB_OK|MB_ICONEXCLAMATION, GetName());
|
|
}
|
|
|
|
if( hr != S_OK )
|
|
return hr;
|
|
|
|
m_strQuery = strQueryTmp;
|
|
|
|
// if user changed the scope setting
|
|
if( strScopeTmp != Scope() )
|
|
{
|
|
// Update the node scope and turn off local scope option (i.e., use query specified scope)
|
|
SetScope(strScopeTmp.c_str());
|
|
SetLocalScope(FALSE);
|
|
}
|
|
|
|
// Determine the classes this query can return
|
|
std::set<tstring> setClasses;
|
|
GetQueryClasses(m_strQuery, setClasses);
|
|
|
|
// Delete current objects that aren't in new query
|
|
QueryObjVector::iterator itObj = m_vObjInfo.begin();
|
|
while( itObj != m_vObjInfo.end() )
|
|
{
|
|
if( setClasses.find(itObj->Name()) == setClasses.end() )
|
|
{
|
|
// delete item from list and leave iterator at this position
|
|
m_vObjInfo.erase(itObj);
|
|
}
|
|
else
|
|
{
|
|
// if found delete from set, so only new ones remain
|
|
setClasses.erase(itObj->Name());
|
|
++itObj;
|
|
}
|
|
}
|
|
|
|
DisplayNameMap* pNameMap = DisplayNames::GetClassMap();
|
|
|
|
// Add any new objects
|
|
std::set<tstring>::iterator itClass;
|
|
for( itClass = setClasses.begin(); itClass != setClasses.end(); itClass++ )
|
|
{
|
|
if( pNameMap == NULL ||
|
|
pNameMap->GetAttributeDisplayName(itClass->c_str()) != itClass->c_str() )
|
|
{
|
|
CQueryObjInfo* pQueryObj = new CQueryObjInfo(itClass->c_str());
|
|
|
|
if( pQueryObj )
|
|
{
|
|
m_vObjInfo.push_back(*pQueryObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
class CRefreshCallback : public CEventCallback
|
|
{
|
|
public:
|
|
CRefreshCallback(HANDLE hProcess, CQueryNode* pQueryNode)
|
|
: m_hProcess(hProcess), m_spQueryNode(pQueryNode)
|
|
{
|
|
}
|
|
|
|
virtual void Execute()
|
|
{
|
|
if( m_spQueryNode )
|
|
{
|
|
m_spQueryNode->OnRefresh(NULL);
|
|
}
|
|
|
|
CloseHandle(m_hProcess);
|
|
}
|
|
|
|
HANDLE m_hProcess;
|
|
CQueryNodePtr m_spQueryNode;
|
|
};
|
|
|
|
class CNoLookup : public CParamLookup
|
|
{
|
|
public:
|
|
virtual BOOL operator() (tstring& strParam, tstring& strValue)
|
|
{
|
|
return FALSE;
|
|
};
|
|
};
|
|
|
|
|
|
HRESULT CQueryNode::MenuCommand(LPCONSOLE2 pConsole, long lCommand)
|
|
{
|
|
VALIDATE_POINTER(pConsole);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
switch( lCommand )
|
|
{
|
|
case MID_EDITQUERY:
|
|
{
|
|
HWND hWndMain;
|
|
hr = pConsole->GetMainWindow(&hWndMain);
|
|
BREAK_ON_FAILURE(hr);
|
|
|
|
hr = EditQuery(hWndMain);
|
|
|
|
if( hr == S_FALSE )
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
m_bQueryChange = TRUE;
|
|
|
|
OnRefresh(pConsole);
|
|
}
|
|
break;
|
|
|
|
case MID_STOPQUERY:
|
|
if( m_pQueryReq != NULL )
|
|
m_pQueryReq->Stop(TRUE);
|
|
break;
|
|
|
|
case MID_MOVEQUERYNODE:
|
|
{
|
|
CScopeNode* pnodeDest = NULL;
|
|
|
|
CMoveQueryDlg dlg;
|
|
if( dlg.DoModal(Parent(), &pnodeDest) == IDOK )
|
|
{
|
|
ASSERT( pnodeDest );
|
|
if( !pnodeDest ) return E_FAIL;
|
|
|
|
// Ref node during move to prevent deletion
|
|
AddRef();
|
|
|
|
// Tell MMC to remove the node
|
|
IConsoleNameSpace* pNameSpace = GetCompData()->GetNameSpace();
|
|
ASSERT( pNameSpace );
|
|
if( !pNameSpace ) return E_FAIL;
|
|
|
|
pNameSpace->DeleteItem(m_hScopeItem, TRUE);
|
|
|
|
// clear item handle becuase it's no longer valid
|
|
m_hScopeItem = NULL;
|
|
|
|
// Now remove the node internally (MMC does not send a delete notify)
|
|
Parent()->RemoveChild(this);
|
|
|
|
// Add back to the new parent
|
|
hr = pnodeDest->AddChild(this);
|
|
|
|
Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
// Must be one of the Querynode menus
|
|
ASSERT(lCommand < m_vMenus.size());
|
|
if( lCommand >= m_vMenus.size() )
|
|
return E_INVALIDARG;
|
|
|
|
HANDLE hProcess = NULL;
|
|
CNoLookup lookup;
|
|
hr = static_cast<CShellMenuCmd*>((CMenuCmd*)m_vMenus[lCommand])->Execute(&lookup, &hProcess);
|
|
|
|
// if process started and auto-refresh wanted, setup event-triggered callback
|
|
if( SUCCEEDED(hr) && hProcess != NULL && m_vMenus[lCommand]->IsAutoRefresh() )
|
|
{
|
|
CallbackOnEvent(hProcess, new CRefreshCallback(hProcess, this));
|
|
}
|
|
}
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::QueryPagesFor()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CQueryNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR handle)
|
|
{
|
|
VALIDATE_POINTER( pProvider );
|
|
|
|
// Create a query edit object
|
|
CQueryEditObj* pEditObj = new CQueryEditObj(this);
|
|
if( !pEditObj ) return E_OUTOFMEMORY;
|
|
|
|
// Create an instance of each prop page class and call Create on each.
|
|
|
|
// Keep it alive until prop pages ref it
|
|
pEditObj->AddRef();
|
|
|
|
// General page
|
|
HPROPSHEETPAGE hpageGen = NULL;
|
|
CQueryGeneralPage* pGenPage = new CQueryGeneralPage(pEditObj);
|
|
if( pGenPage != NULL )
|
|
{
|
|
hpageGen = pGenPage->Create();
|
|
}
|
|
|
|
// Context menu page
|
|
HPROPSHEETPAGE hpageMenu = NULL;
|
|
CQueryMenuPage* pMenuPage = new CQueryMenuPage(pEditObj);
|
|
if( pMenuPage != NULL )
|
|
{
|
|
hpageMenu = pMenuPage->Create();
|
|
}
|
|
|
|
// Listview page
|
|
HPROPSHEETPAGE hpageView = NULL;
|
|
CQueryViewPage* pViewPage = new CQueryViewPage(pEditObj);
|
|
if( pViewPage != NULL )
|
|
hpageView = pViewPage->Create();
|
|
|
|
// Node Menu page
|
|
HPROPSHEETPAGE hpageNodeMenu = NULL;
|
|
CQueryNodeMenuPage* pNodeMenuPage = new CQueryNodeMenuPage(pEditObj);
|
|
if( pNodeMenuPage != NULL )
|
|
{
|
|
hpageNodeMenu = pNodeMenuPage->Create();
|
|
}
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
// if all pages were created, add each one to the prop sheet
|
|
if( hpageGen && hpageMenu && hpageView )
|
|
{
|
|
hr = pProvider->AddPage(hpageGen);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageMenu);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageView);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = pProvider->AddPage(hpageNodeMenu);
|
|
}
|
|
|
|
// On failure, destroy the pages. If a page failed to create
|
|
// then delete the page class object instead (the object is
|
|
// automatically deleted when the page is destroyed)
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
if( hpageGen )
|
|
DestroyPropertySheetPage(hpageGen);
|
|
else
|
|
SAFE_DELETE(pGenPage);
|
|
|
|
if( hpageMenu )
|
|
DestroyPropertySheetPage(hpageMenu);
|
|
else
|
|
SAFE_DELETE(pMenuPage);
|
|
|
|
if( hpageView )
|
|
DestroyPropertySheetPage(hpageView);
|
|
else
|
|
SAFE_DELETE(pViewPage);
|
|
|
|
if( hpageNodeMenu )
|
|
DestroyPropertySheetPage(hpageNodeMenu);
|
|
else
|
|
SAFE_DELETE(pNodeMenuPage);
|
|
}
|
|
|
|
// Release temp ref on edit list
|
|
// it will go away when the prop pages release it
|
|
pEditObj->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// CQueryLookup
|
|
//
|
|
|
|
BOOL CQueryLookup::operator() (tstring& strParam, tstring& strValue)
|
|
{
|
|
if( !m_pRowItem )
|
|
{
|
|
strValue = _T("");
|
|
return FALSE;
|
|
}
|
|
// Check for single digit parameter ID
|
|
if( strParam.size() == 1 && strParam[0] <= MENU_PARAM_LAST )
|
|
{
|
|
switch( strParam[0] )
|
|
{
|
|
case MENU_PARAM_SCOPE:
|
|
strValue = reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->Scope();
|
|
break;
|
|
|
|
case MENU_PARAM_FILTER:
|
|
reinterpret_cast<CQueryNode*>(m_pRowItem->GetOwnerParam())->ExpandQuery(strValue);
|
|
break;
|
|
|
|
case MENU_PARAM_NAME:
|
|
strValue = (*m_pRowItem)[ROWITEM_NAME_INDEX];
|
|
break;
|
|
|
|
case MENU_PARAM_TYPE:
|
|
strValue = (*m_pRowItem)[ROWITEM_CLASS_INDEX];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// see if parameter name matches column name
|
|
string_vector& vstrColumns = m_pQNode->QueryColumns();
|
|
string_vector::iterator itCol = std::find(vstrColumns.begin(), vstrColumns.end(), strParam);
|
|
|
|
// if so, substitue row item value at that position
|
|
if( itCol != vstrColumns.end() )
|
|
strValue = (*m_pRowItem)[(itCol - vstrColumns.begin()) + ROWITEM_USER_INDEX];
|
|
}
|
|
|
|
return !strValue.empty();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// Stream operators (<< >>)
|
|
|
|
IStream& operator<< (IStream& stm, CClassInfo& classInfo)
|
|
{
|
|
stm << classInfo.m_strName;
|
|
stm << classInfo.m_vstrColumns;
|
|
stm << classInfo.m_vMenus;
|
|
return stm;
|
|
}
|
|
|
|
|
|
IStream& operator>> (IStream& stm, CClassInfo& classInfo)
|
|
{
|
|
stm >> classInfo.m_strName;
|
|
stm >> classInfo.m_vstrColumns;
|
|
stm >> classInfo.m_vMenus;
|
|
return stm;
|
|
}
|
|
|
|
IStream& operator<< (IStream& stm, CQueryObjInfo& objInfo)
|
|
{
|
|
stm << objInfo.m_strName;
|
|
stm << objInfo.m_vMenuRefs;
|
|
stm << objInfo.m_vstrDisabledColumns;
|
|
|
|
DWORD dwFlags = objInfo.m_bPropertyMenu ? 1 : 0;
|
|
stm << dwFlags;
|
|
|
|
return stm;
|
|
}
|
|
|
|
|
|
IStream& operator>> (IStream& stm, CQueryObjInfo& objInfo)
|
|
{
|
|
stm >> objInfo.m_strName;
|
|
stm >> objInfo.m_vMenuRefs;
|
|
stm >> objInfo.m_vstrDisabledColumns;
|
|
|
|
// File versions >= 102 include flag word
|
|
// Bit 0 enables the property menu
|
|
if( g_dwFileVer >= 102 )
|
|
{
|
|
DWORD dwFlags;
|
|
stm >> dwFlags;
|
|
objInfo.m_bPropertyMenu = (dwFlags & 1);
|
|
}
|
|
else
|
|
{
|
|
objInfo.m_bPropertyMenu = TRUE;
|
|
}
|
|
|
|
return stm;
|
|
}
|
|
|
|
IStream& operator>> (IStream& stm, CMenuRef& menuref)
|
|
{
|
|
stm >> menuref.m_menuID;
|
|
stm >> menuref.m_flags;
|
|
return stm;
|
|
}
|
|
|
|
IStream& operator<< (IStream& stm, CMenuRef& menuref)
|
|
{
|
|
stm << menuref.m_menuID;
|
|
stm << menuref.m_flags;
|
|
return stm;
|
|
}
|