3109 lines
85 KiB
C++
3109 lines
85 KiB
C++
//+----------------------------------------------------------------------------
|
|
//
|
|
// Windows NT Directory Service Administration SnapIn
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
//
|
|
// File: DSEvent.cpp
|
|
//
|
|
// Contents: Main DS Snapin file
|
|
// This file contains all the interfaces between the snapin and
|
|
// the slate console. IComponent, IDataObject...etc
|
|
//
|
|
// History: 02-Oct-96 WayneSc Created
|
|
// 06-Mar-97 EricB - added Property Page Extension support
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "uiutil.h"
|
|
#include "dsutil.h"
|
|
|
|
#include "dssnap.h" // Note: this has to be before dsevent.h
|
|
#include "DSEvent.h"
|
|
|
|
#include "ContextMenu.h"
|
|
#include "DataObj.h"
|
|
#include "dsctx.h"
|
|
#include "dsdirect.h"
|
|
#include "dsfilter.h"
|
|
#include "helpids.h"
|
|
#include "query.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
// DS Snapin CLSID - {E355E538-1C2E-11d0-8C37-00C04FD8FE93}
|
|
const CLSID CLSID_DSSnapin =
|
|
{0xe355e538, 0x1c2e, 0x11d0, {0x8c, 0x37, 0x0, 0xc0, 0x4f, 0xd8, 0xfe, 0x93}};
|
|
|
|
// DS Snapin Extension CLSID - {006A2A75-547F-11d1-B930-00A0C9A06D2D}
|
|
const CLSID CLSID_DSSnapinEx =
|
|
{ 0x6a2a75, 0x547f, 0x11d1, { 0xb9, 0x30, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x2d } };
|
|
|
|
// DS Site CLSID - {d967f824-9968-11d0-b936-00c04fd8d5b0}
|
|
const CLSID CLSID_SiteSnapin = { 0xd967f824, 0x9968, 0x11d0, { 0xb9, 0x36, 0x0, 0xc0, 0x4f, 0xd8, 0xd5, 0xb0 } };
|
|
|
|
// Default Nodetype GUID - {FC04A81C-1DFA-11D0-8C3b-00C04FD8FE93}
|
|
const GUID cDefaultNodeType =
|
|
{0xFC04A81C, 0x1dfa, 0x11d0, {0x8C, 0x3B, 0x00, 0xC0, 0x4F, 0xD8, 0xFE, 0x93}};
|
|
|
|
// DS About Snapin CLSID - {c3a904fe-c4f2-11d1-b10b-00104b243180}
|
|
const CLSID CLSID_DSAboutSnapin =
|
|
{0xc3a904fe, 0xc4f2, 0x11d1, {0xb1, 0x0b, 0x00, 0x10, 0x4b, 0x24, 0x31, 0x80}};
|
|
|
|
// DS About Snapin CLSID - {765901ea-c5a1-11d1-b10c-00104b243180}
|
|
const CLSID CLSID_SitesAboutSnapin =
|
|
{0x765901ea, 0xc5a1, 0x11d1, {0xb1, 0x0c, 0x00, 0x10, 0x4b, 0x24, 0x31, 0x80}};
|
|
|
|
// DS Query UI Form extension for saved queries {8C16E7CB-17C2-4729-A669-8474D6712B81}
|
|
const CLSID CLSID_DSAdminQueryUIForm =
|
|
{ 0x8c16e7cb, 0x17c2, 0x4729, { 0xa6, 0x69, 0x84, 0x74, 0xd6, 0x71, 0x2b, 0x81 } };
|
|
|
|
const wchar_t* cszDefaultNodeType = _T("{FC04A81C-1DFA-11d0-8C3B-00C04FD8FE93}");
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDSEvent
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: Constructor / Destructor
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
CDSEvent::CDSEvent() :
|
|
m_pFrame(NULL),
|
|
m_pHeader(NULL),
|
|
m_pResultData(NULL),
|
|
m_pScopeData(NULL),
|
|
m_pConsoleVerb(NULL),
|
|
m_pRsltImageList(NULL),
|
|
m_pSelectedFolderNode(NULL),
|
|
m_pComponentData( NULL ),
|
|
m_pToolbar(NULL),
|
|
m_pControlbar(NULL),
|
|
m_bUpdateAllViewsOrigin(FALSE)
|
|
{
|
|
TRACE(_T("CDSEvent::CDSEvent() - Constructor\n"));
|
|
}
|
|
|
|
CDSEvent::~CDSEvent()
|
|
{
|
|
TRACE(_T("CDSEvent::~CDSEvent() - Destructor\n"));
|
|
|
|
SetIComponentData( NULL );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IComponent Interfaces
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: Destroy
|
|
//
|
|
// Synopsis: Used for clean up
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP CDSEvent::Destroy(MMC_COOKIE)
|
|
{
|
|
TRACE(_T("CDSEvent::Destroy()\n"));
|
|
|
|
if (NULL != m_pHeader)
|
|
m_pFrame->SetHeader(NULL);
|
|
|
|
if (NULL != m_pToolbar)
|
|
{
|
|
m_pToolbar->Release();
|
|
}
|
|
|
|
m_pHeader->Release();
|
|
|
|
m_pResultData->Release();
|
|
m_pScopeData->Release();
|
|
m_pRsltImageList->Release();
|
|
m_pFrame->Release();
|
|
m_pConsoleVerb->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: Initialize
|
|
//
|
|
// Synopsis: Called everytime the snapin get created.
|
|
//
|
|
// Arguments: IConsole - Pointer to calling object
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CDSEvent::Initialize(IConsole* pConsole)
|
|
{
|
|
TRACE(_T("CDSEvent::Initialize()\n"));
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
CWaitCursor wait;
|
|
|
|
if (pConsole == NULL)
|
|
{
|
|
// Invalid argument
|
|
return E_POINTER;
|
|
}
|
|
|
|
// hold on to the frame
|
|
HRESULT hr = pConsole->QueryInterface(IID_IConsole3, (void**)&m_pFrame);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// cache interface pointers we use
|
|
hr = m_pFrame->QueryInterface(IID_IHeaderCtrl, (void**)&m_pHeader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ASSERT(m_pHeader != NULL);
|
|
|
|
hr = m_pFrame->SetHeader(m_pHeader);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = m_pFrame->QueryInterface(IID_IResultData2, (void**)&m_pResultData);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ASSERT(m_pResultData != NULL);
|
|
|
|
hr = m_pFrame->QueryInterface(IID_IConsoleNameSpace, (void**)&m_pScopeData);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ASSERT(m_pScopeData != NULL);
|
|
|
|
hr = m_pFrame->QueryResultImageList(&m_pRsltImageList);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
ASSERT(m_pRsltImageList != NULL);
|
|
|
|
hr = m_pFrame->QueryConsoleVerb (&m_pConsoleVerb);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
m_hwnd = m_pComponentData->GetHWnd();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDSEvent::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject)
|
|
{
|
|
TRACE(_T("CDSEvent::GetDataObject()\n"));
|
|
|
|
HRESULT hr=S_OK;
|
|
|
|
CUINode* pUINode;
|
|
CDSDataObject* const pDataObject = new CComObject<CDSDataObject>;
|
|
ASSERT(pDataObject != 0);
|
|
|
|
pDataObject->SetType(type, m_pComponentData->QuerySnapinType());
|
|
pDataObject->SetComponentData(m_pComponentData);
|
|
|
|
if (cookie != MMC_MULTI_SELECT_COOKIE)
|
|
{
|
|
pUINode = reinterpret_cast<CUINode*>(cookie);
|
|
pDataObject->SetCookie(pUINode);
|
|
}
|
|
else
|
|
{
|
|
TRACE(_T("CDSEvent::GetDataObject() - multi-select.\n"));
|
|
RESULTDATAITEM rdi;
|
|
ZeroMemory(&rdi, sizeof(rdi));
|
|
rdi.mask = RDI_STATE;
|
|
rdi.nIndex = -1;
|
|
rdi.nState = LVIS_SELECTED;
|
|
|
|
do
|
|
{
|
|
rdi.lParam = 0;
|
|
ASSERT(rdi.mask == RDI_STATE);
|
|
ASSERT(rdi.nState == LVIS_SELECTED);
|
|
hr = m_pResultData->GetNextItem(&rdi);
|
|
if (hr != S_OK)
|
|
break;
|
|
|
|
pUINode = reinterpret_cast<CUINode*>(rdi.lParam);
|
|
pDataObject->AddCookie(pUINode);
|
|
} while (1);
|
|
|
|
}
|
|
|
|
// addref() the new pointer and return it.
|
|
pDataObject->AddRef();
|
|
*ppDataObject = pDataObject;
|
|
TRACE(_T("new data object is at %lx(%lx).\n"),
|
|
pDataObject, *pDataObject);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CDSEvent::GetDisplayInfo(LPRESULTDATAITEM pResult)
|
|
{
|
|
ASSERT(pResult != NULL);
|
|
HRESULT hr = S_OK;
|
|
|
|
// get the node we are interested in
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(pResult->lParam);
|
|
ASSERT( NULL != pUINode );
|
|
|
|
if (pResult->mask & RDI_STR)
|
|
{
|
|
// need string value
|
|
|
|
// get the parent to retrieve the column set
|
|
CUINode* pUIParentNode = pUINode->GetParent();
|
|
ASSERT(pUIParentNode != NULL);
|
|
ASSERT(pUIParentNode->IsContainer());
|
|
|
|
// retrieve the column set
|
|
CDSColumnSet* pColumnSet = pUIParentNode->GetColumnSet(m_pComponentData);
|
|
ASSERT(pColumnSet != NULL);
|
|
|
|
// ask the node to provide the string for the
|
|
// given column in the column set
|
|
pResult->str = const_cast<LPWSTR>(pUINode->GetDisplayString(pResult->nCol, pColumnSet));
|
|
}
|
|
|
|
if (pResult->mask & RDI_IMAGE)
|
|
{
|
|
// need an icon for result pane
|
|
pResult->nImage = m_pComponentData->GetImage(pUINode, FALSE);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//IResultCallback
|
|
|
|
STDMETHODIMP CDSEvent::GetResultViewType(MMC_COOKIE, LPWSTR* ppViewType,
|
|
long *pViewOptions)
|
|
{
|
|
*ppViewType = NULL;
|
|
*pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CDSEvent::IExtendPropertySheet::CreatePropertyPages
|
|
//
|
|
// Synopsis: Called in response to a user click on the Properties context
|
|
// menu item.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CDSEvent::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pCall,
|
|
LONG_PTR lNotifyHandle,
|
|
LPDATAOBJECT pDataObject)
|
|
{
|
|
IExtendPropertySheet * pEPS = (IExtendPropertySheet *)m_pComponentData;
|
|
return pEPS->CreatePropertyPages(pCall, lNotifyHandle, pDataObject);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CDSEvent::IExtendPropertySheet::QueryPagesFor
|
|
//
|
|
// Synopsis: Called before a context menu is posted. If we support a
|
|
// property sheet for this object, then return S_OK.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP
|
|
CDSEvent::QueryPagesFor(LPDATAOBJECT pDataObject)
|
|
{
|
|
TRACE(TEXT("CDSEvent::QueryPagesFor().\n"));
|
|
return m_pComponentData->QueryPagesFor( pDataObject);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LocaleStrCmp
|
|
//
|
|
// Synopsis: Do a case insensitive string compare that is safe for any
|
|
// locale.
|
|
//
|
|
// Arguments: [ptsz1] - strings to compare
|
|
// [ptsz2]
|
|
//
|
|
// Returns: -1, 0, or 1 just like lstrcmpi
|
|
//
|
|
// History: 10-28-96 DavidMun Created
|
|
//
|
|
// Notes: This is slower than lstrcmpi, but will work when sorting
|
|
// strings even in Japanese.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
int LocaleStrCmp(LPCTSTR ptsz1, LPCTSTR ptsz2)
|
|
{
|
|
int iRet = 0;
|
|
|
|
iRet = CompareString(LOCALE_USER_DEFAULT,
|
|
NORM_IGNORECASE |
|
|
NORM_IGNOREKANATYPE |
|
|
NORM_IGNOREWIDTH,
|
|
ptsz1,
|
|
-1,
|
|
ptsz2,
|
|
-1);
|
|
|
|
if (iRet)
|
|
{
|
|
iRet -= 2; // convert to lstrcmpi-style return -1, 0, or 1
|
|
|
|
if ( 0 == iRet )
|
|
{
|
|
UNICODE_STRING unistr1;
|
|
unistr1.Length = (USHORT)(::lstrlen(ptsz1)*sizeof(WCHAR));
|
|
unistr1.MaximumLength = unistr1.Length;
|
|
unistr1.Buffer = (LPWSTR)ptsz1;
|
|
UNICODE_STRING unistr2;
|
|
unistr2.Length = (USHORT)(::lstrlen(ptsz2)*sizeof(WCHAR));
|
|
unistr2.MaximumLength = unistr2.Length;
|
|
unistr2.Buffer = (LPWSTR)ptsz2;
|
|
iRet = ::RtlCompareUnicodeString(
|
|
&unistr1,
|
|
&unistr2,
|
|
FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwErr = GetLastError ();
|
|
if (dwErr != 0)
|
|
{
|
|
TRACE3 ("CompareString (%s, %s) failed: 0x%x\n", ptsz1, ptsz2, dwErr);
|
|
}
|
|
}
|
|
return iRet;
|
|
}
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CDSEvent::IResultDataCompareEx::Compare
|
|
//
|
|
// Synopsis: called to do the comparison for sorting in the result
|
|
// pane
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CDSEvent::Compare(RDCOMPARE* prdc, int* pnResult)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (pnResult == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pnResult = 0;
|
|
if (prdc == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_POINTER;
|
|
}
|
|
|
|
CUINode* pUINodeA = reinterpret_cast<CUINode*>(prdc->prdch1->cookie);
|
|
CUINode* pUINodeB = reinterpret_cast<CUINode*>(prdc->prdch2->cookie);
|
|
ASSERT(pUINodeA != NULL);
|
|
ASSERT(pUINodeB != NULL);
|
|
|
|
if ( (pUINodeA == NULL) || (pUINodeB == NULL) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CString strA, strB;
|
|
|
|
CDSColumnSet* pColSetA = pUINodeA->GetParent()->GetColumnSet(m_pComponentData);
|
|
CDSColumnSet* pColSetB = pUINodeB->GetParent()->GetColumnSet(m_pComponentData);
|
|
|
|
if ((pColSetA == NULL) || (pColSetB == NULL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CDSColumn* pColA = (CDSColumn*)pColSetA->GetColumnAt(prdc->nColumn);
|
|
|
|
if (IS_CLASS(CDSUINode, *pUINodeA) && IS_CLASS(CDSUINode, *pUINodeB))
|
|
{
|
|
//
|
|
// extract cookie info (DS objects)
|
|
//
|
|
CDSCookie* pCookieA = GetDSCookieFromUINode(pUINodeA);
|
|
CDSCookie* pCookieB = GetDSCookieFromUINode(pUINodeB);
|
|
if ( (pCookieB == NULL) || (pCookieA == NULL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
switch (pColA->GetColumnType())
|
|
{
|
|
case ATTR_COLTYPE_NAME: //name
|
|
strA = pCookieA->GetName();
|
|
strB = pCookieB->GetName();
|
|
|
|
*pnResult = LocaleStrCmp(strA, strB);
|
|
break;
|
|
|
|
case ATTR_COLTYPE_CLASS: //class
|
|
strA = pCookieA->GetLocalizedClassName();
|
|
strB = pCookieB->GetLocalizedClassName();
|
|
|
|
*pnResult = LocaleStrCmp(strA, strB);
|
|
break;
|
|
|
|
case ATTR_COLTYPE_DESC: //description
|
|
strA = pCookieA->GetDesc();
|
|
strB = pCookieB->GetDesc();
|
|
|
|
*pnResult = LocaleStrCmp(strA, strB);
|
|
break;
|
|
|
|
case ATTR_COLTYPE_SPECIAL: //special columns
|
|
{
|
|
int nSpecialCol = 0;
|
|
int idx = 0;
|
|
POSITION pos = pColSetA->GetHeadPosition();
|
|
while (idx < prdc->nColumn && pos != NULL) // JonN 4/3/01 313564
|
|
{
|
|
CDSColumn* pColumn = (CDSColumn*)pColSetA->GetNext(pos);
|
|
ASSERT(pColumn != NULL);
|
|
|
|
if ((pColumn->GetColumnType() == ATTR_COLTYPE_SPECIAL || pColumn->GetColumnType() == ATTR_COLTYPE_MODIFIED_TIME) &&
|
|
pColumn->IsVisible())
|
|
{
|
|
nSpecialCol++;
|
|
}
|
|
|
|
idx++;
|
|
}
|
|
CStringList& strlistA = pCookieA->GetParentClassSpecificStrings();
|
|
POSITION posA = strlistA.FindIndex( nSpecialCol );
|
|
CStringList& strlistB = pCookieB->GetParentClassSpecificStrings();
|
|
POSITION posB = strlistB.FindIndex( nSpecialCol );
|
|
if ( NULL != posA && NULL != posB)
|
|
{
|
|
strA = strlistA.GetAt( posA );
|
|
strB = strlistB.GetAt( posB );
|
|
}
|
|
*pnResult = LocaleStrCmp(strA, strB);
|
|
break;
|
|
}
|
|
case ATTR_COLTYPE_MODIFIED_TIME:
|
|
{
|
|
SYSTEMTIME* pTimeA = pCookieA->GetModifiedTime();
|
|
SYSTEMTIME* pTimeB = pCookieB->GetModifiedTime();
|
|
if (pTimeA == NULL)
|
|
{
|
|
*pnResult = -1;
|
|
break;
|
|
}
|
|
else if (pTimeB == NULL)
|
|
{
|
|
*pnResult = 1;
|
|
break;
|
|
}
|
|
|
|
FILETIME fileTimeA, fileTimeB;
|
|
|
|
if (!SystemTimeToFileTime(pTimeA, &fileTimeA))
|
|
return E_FAIL;
|
|
|
|
if (!SystemTimeToFileTime(pTimeB, &fileTimeB))
|
|
return E_FAIL;
|
|
|
|
*pnResult = CompareFileTime(&fileTimeA, &fileTimeB);
|
|
break;
|
|
}
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
else // Not DS objects
|
|
{
|
|
strA = pUINodeA->GetDisplayString(prdc->nColumn, pColSetA);
|
|
strB = pUINodeB->GetDisplayString(prdc->nColumn, pColSetB);
|
|
*pnResult = LocaleStrCmp(strA, strB);
|
|
}
|
|
|
|
// TRACE(_T("Compare: %d\n"), *pnResult);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member: CDSEvent::IComponent::CompareObjects
|
|
//
|
|
// Synopsis: If the data objects belong to the same DS object, then return
|
|
// S_OK.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
STDMETHODIMP CDSEvent::CompareObjects(LPDATAOBJECT pDataObject1, LPDATAOBJECT pDataObject2)
|
|
{
|
|
//
|
|
// Delegate to the IComponentData implementation.
|
|
//
|
|
return m_pComponentData->CompareObjects(pDataObject1, pDataObject2);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDSEvent::Notify(IDataObject * pDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param)
|
|
{
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
HRESULT hr = S_FALSE;
|
|
CInternalFormatCracker dobjCracker;
|
|
CUINode* pUINode = NULL;
|
|
|
|
if (pDataObject != NULL)
|
|
{
|
|
if (FAILED(dobjCracker.Extract(pDataObject)))
|
|
{
|
|
if ( (event == MMCN_ADD_IMAGES) && !m_pComponentData->m_bRunAsPrimarySnapin )
|
|
{
|
|
m_pComponentData->FillInIconStrip (m_pRsltImageList);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
pUINode = dobjCracker.GetCookie();
|
|
}
|
|
|
|
if (event == MMCN_PROPERTY_CHANGE)
|
|
{
|
|
CWaitCursor cwait;
|
|
TRACE(_T("CDSEvent::Notify() - property change, pDataObj = 0x%08x, param = 0x%08x, arg = %d.\n"),
|
|
pDataObject, param, arg);
|
|
if (param != 0)
|
|
{
|
|
hr = m_pComponentData->_OnPropertyChange((LPDATAOBJECT)param, FALSE);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// some of the MMCN_VIEW_CHANGE, MMCN_CUTORMOVE messages have a NULL data object
|
|
if ((event != MMCN_VIEW_CHANGE) && (event != MMCN_CUTORMOVE) && (pUINode == NULL))
|
|
return S_FALSE;
|
|
|
|
switch (event)
|
|
{
|
|
case MMCN_SHOW:
|
|
if (arg == TRUE)
|
|
{ // Show
|
|
CWaitCursor cwait;
|
|
_EnumerateCookie(pUINode,(HSCOPEITEM)param,event);
|
|
hr = S_OK;
|
|
}
|
|
|
|
break;
|
|
|
|
case MMCN_MINIMIZED:
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
case MMCN_SELECT:
|
|
{
|
|
BOOL bScope = LOWORD(arg);
|
|
BOOL bSelect = HIWORD(arg);
|
|
|
|
CContextMenuVerbs* pMenuVerbs = pUINode->GetContextMenuVerbsObject(m_pComponentData);
|
|
|
|
if (pMenuVerbs == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return S_FALSE;
|
|
}
|
|
pMenuVerbs->LoadStandardVerbs(m_pConsoleVerb,
|
|
bScope/*bScope*/,
|
|
bSelect/*bSelect*/,
|
|
pUINode,
|
|
pDataObject);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_DELETE:
|
|
{
|
|
CWaitCursor cwait;
|
|
_Delete(pDataObject, &dobjCracker);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_QUERY_PASTE:
|
|
{
|
|
hr = _QueryPaste(pUINode, (IDataObject*)(arg));
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MMCN_PASTE:
|
|
{
|
|
CWaitCursor cwait;
|
|
_Paste(pUINode, (IDataObject*)(arg), (LPDATAOBJECT*)param);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_CUTORMOVE:
|
|
{
|
|
CWaitCursor cwait;
|
|
ASSERT(pUINode == NULL);
|
|
_CutOrMove((IDataObject*)(arg));
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_RENAME:
|
|
{
|
|
CWaitCursor cwait;
|
|
|
|
hr = m_pComponentData->_Rename (pUINode,
|
|
(LPWSTR) param);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pFrame->UpdateAllViews (pDataObject,
|
|
(LPARAM)pUINode,
|
|
DS_RENAME_OCCURRED);
|
|
|
|
MMC_SORT_SET_DATA* pColumnData = NULL;
|
|
|
|
CDSColumnSet* pColumnSet = pUINode->GetParent()->GetColumnSet(m_pComponentData);
|
|
if (pColumnSet == NULL)
|
|
break;
|
|
|
|
LPCWSTR lpszID = pColumnSet->GetColumnID();
|
|
size_t iLen = wcslen(lpszID);
|
|
|
|
//
|
|
// allocate enough memory for the struct and the guid
|
|
//
|
|
SColumnSetID* pNodeID = (SColumnSetID*)malloc(sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
|
|
if (pNodeID != NULL)
|
|
{
|
|
memset(pNodeID, 0, sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
|
|
pNodeID->cBytes = static_cast<ULONG>(iLen * sizeof(WCHAR));
|
|
memcpy(pNodeID->id, lpszID, (iLen * sizeof(WCHAR)));
|
|
|
|
CComPtr<IColumnData> spColumnData;
|
|
hr = m_pFrame->QueryInterface(IID_IColumnData, (void**)&spColumnData);
|
|
if (spColumnData != NULL)
|
|
{
|
|
hr = spColumnData->GetColumnSortData(pNodeID, &pColumnData);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pColumnData != NULL)
|
|
{
|
|
if (pColumnData->pSortData[0].nColIndex == 0)
|
|
{
|
|
m_pFrame->UpdateAllViews(NULL,
|
|
(LPARAM)pUINode->GetParent(),
|
|
DS_SORT_RESULT_PANE);
|
|
}
|
|
CoTaskMemFree(pColumnData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
free(pNodeID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MMCN_VIEW_CHANGE:
|
|
{
|
|
CWaitCursor cwait;
|
|
TRACE (_T("CDSEvent::Notify() - view change message.\n"));
|
|
HandleViewChange (pDataObject, arg, param);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_ADD_IMAGES:
|
|
{
|
|
CWaitCursor cwait;
|
|
m_pComponentData->FillInIconStrip (m_pRsltImageList);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
|
|
case MMCN_REFRESH:
|
|
{
|
|
CWaitCursor cwait;
|
|
m_pComponentData->Refresh(pUINode);
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
case MMCN_DBLCLICK:
|
|
hr = S_FALSE;
|
|
break;
|
|
case MMCN_COLUMN_CLICK:
|
|
hr = S_OK;
|
|
break;
|
|
case MMCN_COLUMNS_CHANGED:
|
|
{
|
|
CWaitCursor cwait;
|
|
MMC_VISIBLE_COLUMNS* pVisibleColumns = reinterpret_cast<MMC_VISIBLE_COLUMNS*>(param);
|
|
// Delegate to IComponentData
|
|
hr = m_pComponentData->ColumnsChanged(this, pUINode, pVisibleColumns, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case MMCN_RESTORE_VIEW :
|
|
{
|
|
CWaitCursor cwait;
|
|
m_pComponentData->ColumnsChanged(this, pUINode, NULL, FALSE);
|
|
*((BOOL*)param) = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
break;
|
|
case MMCN_CONTEXTHELP:
|
|
{
|
|
CWaitCursor cwait;
|
|
|
|
IDisplayHelp * phelp = NULL;
|
|
hr = m_pFrame->QueryInterface (IID_IDisplayHelp,
|
|
(void **)&phelp);
|
|
CString strDefTopic;
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_pComponentData->QuerySnapinType() == SNAPINTYPE_SITE)
|
|
{
|
|
strDefTopic = DSSITES_DEFAULT_TOPIC;
|
|
}
|
|
else
|
|
{
|
|
strDefTopic = DSADMIN_DEFAULT_TOPIC;
|
|
}
|
|
phelp->ShowTopic ((LPWSTR)(LPCWSTR)strDefTopic);
|
|
phelp->Release();
|
|
}
|
|
else
|
|
{
|
|
ReportErrorEx (m_hwnd, IDS_HELPLESS, hr, NULL, 0);
|
|
hr = S_FALSE;
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IExtendContextMenu
|
|
|
|
STDMETHODIMP CDSEvent::AddMenuItems(IDataObject* piDataObject,
|
|
IContextMenuCallback* piCallback,
|
|
long *pInsertionAllowed)
|
|
{
|
|
TRACE(_T("CDSEvent::AddExtensionContextMenuItems()\n"));
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
HRESULT hr;
|
|
CWaitCursor cwait;
|
|
CInternalFormatCracker dobjCracker;
|
|
|
|
hr = dobjCracker.Extract(piDataObject);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
DATA_OBJECT_TYPES dotType = dobjCracker.GetType();
|
|
CUINode* pUINode = dobjCracker.GetCookie();
|
|
|
|
//
|
|
// Retrieve the verb handler from the node
|
|
// NOTE: multi-selection is handled by cracking the dataobject not by which node
|
|
// is called to retrieve the CContextMenuVerbs object
|
|
//
|
|
CContextMenuVerbs* pMenuVerbs = pUINode->GetContextMenuVerbsObject(m_pComponentData);
|
|
if (pMenuVerbs == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
CComPtr<IContextMenuCallback2> spContextMenuCallback2;
|
|
hr = piCallback->QueryInterface(IID_IContextMenuCallback2, (PVOID*)&spContextMenuCallback2);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE && L"Unable to QI for the IContextMenuCallback2 interface.");
|
|
return hr;
|
|
}
|
|
|
|
if (dotType == CCT_RESULT)
|
|
{
|
|
pMenuVerbs->LoadStandardVerbs(m_pConsoleVerb, FALSE/*bScope*/, TRUE /*bSelect*/, pUINode, piDataObject);
|
|
|
|
//
|
|
// Create the main menu, if allowed
|
|
//
|
|
if (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP)
|
|
{
|
|
hr = pMenuVerbs->LoadMainMenu(spContextMenuCallback2,piDataObject,pUINode);
|
|
hr = pMenuVerbs->LoadMenuExtensions(spContextMenuCallback2,
|
|
m_pComponentData->m_pShlInit,
|
|
piDataObject,
|
|
pUINode);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// create the task menu
|
|
if (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK)
|
|
{
|
|
hr = pMenuVerbs->LoadTaskMenu(spContextMenuCallback2,pUINode);
|
|
}
|
|
}
|
|
}
|
|
else if (dotType == CCT_SCOPE)
|
|
{
|
|
|
|
pMenuVerbs->LoadStandardVerbs(m_pConsoleVerb, TRUE/*bScope*/, TRUE /*bSelect*/, pUINode, piDataObject);
|
|
|
|
hr = m_pComponentData->AddMenuItems (piDataObject,
|
|
piCallback,
|
|
pInsertionAllowed);
|
|
}
|
|
else // CCT_UNINITIALIZED
|
|
{
|
|
if (dobjCracker.GetCookieCount() > 1)
|
|
{
|
|
hr = pMenuVerbs->LoadMenuExtensions(spContextMenuCallback2,
|
|
m_pComponentData->m_pShlInit,
|
|
piDataObject,
|
|
pUINode);
|
|
}
|
|
}
|
|
ASSERT( SUCCEEDED(hr) );
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDSEvent::Command(long lCommandID, IDataObject * pDataObject)
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
TRACE(_T("CDSEvent::Command()\n"));
|
|
|
|
CWaitCursor CWait;
|
|
|
|
// crack data object
|
|
|
|
CInternalFormatCracker dobjCracker;
|
|
HRESULT hr = dobjCracker.Extract(pDataObject);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE); // not our data object
|
|
return hr;
|
|
}
|
|
|
|
DATA_OBJECT_TYPES dotType = dobjCracker.GetType();
|
|
if (dotType == CCT_SCOPE)
|
|
{
|
|
// if called from the tree view context, delegate to ComponentData
|
|
return m_pComponentData->Command(lCommandID, pDataObject);
|
|
}
|
|
|
|
// context menu shell extensions
|
|
if ((lCommandID >= MENU_MERGE_BASE) && (lCommandID <= MENU_MERGE_LIMIT))
|
|
{
|
|
return _CommandShellExtension(lCommandID, pDataObject);
|
|
}
|
|
|
|
// standard commands
|
|
CUINode* pUINode = dobjCracker.GetCookie();
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if ( (pUINode == NULL) ||(pCookie==NULL) )
|
|
{
|
|
ASSERT(FALSE); // Invalid Cookie
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
switch (lCommandID)
|
|
{
|
|
case IDM_GEN_TASK_MOVE:
|
|
{
|
|
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(pUINode);
|
|
ASSERT(pDSUINode != NULL);
|
|
|
|
CDSCookie* pMoveCookie = pDSUINode->GetCookie();
|
|
|
|
hr = m_pComponentData->GetActiveDS()->MoveObject(pMoveCookie);
|
|
if (hr == S_OK)
|
|
{
|
|
CUINode* pNewParentNode = NULL;
|
|
hr = m_pComponentData->FindParentCookie(pMoveCookie->GetPath(), &pNewParentNode);
|
|
if ((hr == S_OK) && (pNewParentNode->GetFolderInfo()->IsExpanded()))
|
|
{
|
|
pNewParentNode->GetFolderInfo()->AddNode(pUINode);
|
|
}
|
|
m_pFrame->UpdateAllViews(pDataObject, (LPARAM)pUINode, DS_MOVE_OCCURRED);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
;
|
|
} // switch
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDSEvent::_CommandShellExtension(long nCommandID, LPDATAOBJECT pDataObject)
|
|
{
|
|
CWaitCursor wait;
|
|
|
|
// initialize shell code with data object
|
|
IShellExtInit* pShlInit = m_pComponentData->m_pShlInit; // local copy, no addref
|
|
HRESULT hr = pShlInit->Initialize(NULL, pDataObject, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(TEXT("pShlInit->Initialize failed, hr: 0x%x\n"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// get the context menu specific interface
|
|
CComPtr<IContextMenu> spICM;
|
|
hr = pShlInit->QueryInterface(IID_IContextMenu, (void **)&spICM);
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(TEXT("pShlInit->QueryInterface(IID_IContextMenu, ...) failed, hr: 0x%x\n"), hr);
|
|
return hr;
|
|
}
|
|
|
|
// invoke the shell extension command
|
|
HWND hwnd;
|
|
CMINVOKECOMMANDINFO cmiCommand;
|
|
hr = m_pFrame->GetMainWindow (&hwnd);
|
|
ASSERT (hr == S_OK);
|
|
cmiCommand.hwnd = hwnd;
|
|
cmiCommand.cbSize = sizeof (CMINVOKECOMMANDINFO);
|
|
cmiCommand.fMask = SEE_MASK_ASYNCOK;
|
|
cmiCommand.lpVerb = MAKEINTRESOURCEA(nCommandID - MENU_MERGE_BASE);
|
|
spICM->InvokeCommand (&cmiCommand);
|
|
|
|
|
|
CInternalFormatCracker dobjCracker;
|
|
hr = dobjCracker.Extract(pDataObject);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE); // not our data object
|
|
return hr;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
// code to update the views if the extension says it moved items
|
|
//
|
|
TRACE(_T("Command: returned from extension commdand\n"));
|
|
|
|
CUINodeList nodesMoved;
|
|
|
|
HSCOPEITEM ItemID;
|
|
CUINode* pCurrentParentNode = NULL;
|
|
CUINode* pNewParentNode = NULL;
|
|
|
|
for (UINT index = 0; index < dobjCracker.GetCookieCount(); index ++)
|
|
{
|
|
CUINode* pUINode = dobjCracker.GetCookie(index);
|
|
|
|
// make sure the node moved is of the right type: for the time
|
|
// being we just deal with DS objects
|
|
if (!IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
ASSERT(FALSE); // should not get here
|
|
continue;
|
|
}
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pUINode->GetExtOp() & OPCODE_MOVE)
|
|
{
|
|
if (pNewParentNode == NULL)
|
|
{
|
|
// get the parent from the first node
|
|
// assume that all have the same parent
|
|
m_pComponentData->FindParentCookie(pCookie->GetPath(), &pNewParentNode);
|
|
}
|
|
|
|
pCurrentParentNode = pUINode->GetParent();
|
|
if (pCurrentParentNode &&
|
|
IS_CLASS(*pCurrentParentNode, CDSUINode))
|
|
{
|
|
if (pUINode->IsContainer())
|
|
{
|
|
ItemID = pUINode->GetFolderInfo()->GetScopeItem();
|
|
|
|
// delete the scope item in MMC
|
|
hr = m_pComponentData->m_pScope->DeleteItem(ItemID, TRUE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(_T("DeleteItem failed on %lx (%s).\n"),
|
|
ItemID, pUINode->GetName());
|
|
}
|
|
TRACE(_T("Move postprocessing - deleted scope node: %x (%s)\n"),
|
|
ItemID, pUINode->GetName());
|
|
#endif
|
|
if (pCurrentParentNode)
|
|
{
|
|
pCurrentParentNode->GetFolderInfo()->RemoveNode(pUINode);
|
|
}
|
|
|
|
|
|
if ((pNewParentNode) && pNewParentNode->GetFolderInfo()->IsExpanded())
|
|
{
|
|
pUINode->ClearParent();
|
|
pNewParentNode->GetFolderInfo()->AddNode(pUINode);
|
|
hr = m_pComponentData->_AddScopeItem(pUINode, pNewParentNode->GetFolderInfo()->GetScopeItem());
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(_T("AddItem failed on %lx (%s).\n"),
|
|
ItemID, pUINode->GetName());
|
|
}
|
|
TRACE(_T("Move postprocessing - added scope node: %s\n"),
|
|
pUINode->GetName());
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// not expanded
|
|
delete pCookie;
|
|
pCookie = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not a container
|
|
if ((pNewParentNode) &&
|
|
(pNewParentNode->GetFolderInfo()->IsExpanded()))
|
|
{
|
|
pUINode->ClearParent();
|
|
pNewParentNode->GetFolderInfo()->AddNode(pUINode);
|
|
}
|
|
nodesMoved.AddTail(pUINode);
|
|
}
|
|
}
|
|
if (pUINode)
|
|
{
|
|
pUINode->SetExtOp(NULL);
|
|
}
|
|
}
|
|
} // for items in multiple selection
|
|
|
|
|
|
if (!nodesMoved.IsEmpty())
|
|
{
|
|
m_pFrame->UpdateAllViews(NULL, (LPARAM)&nodesMoved, DS_MULTIPLE_MOVE_OCCURRED);
|
|
}
|
|
//------------------------------ends here--------------------------------------
|
|
m_pComponentData->SortResultPane(pNewParentNode);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDSEvent::_InitView(CUINode* pUINode)
|
|
{
|
|
CWaitCursor wait;
|
|
|
|
HRESULT hr=S_OK;
|
|
|
|
//
|
|
// This is more a suggestion than anything so its OK to ignore the return value but
|
|
// we will ASSERT for testing purposes
|
|
//
|
|
hr = m_pResultData->ModifyViewStyle(MMC_ENSUREFOCUSVISIBLE, (MMC_RESULT_VIEW_STYLE)0);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr=_SetColumns(pUINode);
|
|
|
|
m_pSelectedFolderNode = pUINode;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CDSEvent::_EnumerateCookie(CUINode* pUINode, HSCOPEITEM hParent, MMC_NOTIFY_TYPE event)
|
|
{
|
|
TRACE(_T("CDSEvent::_EnumerateCookie()\n"));
|
|
HRESULT hr = S_OK;
|
|
|
|
CWaitCursor cwait;
|
|
|
|
if ( (pUINode == NULL) || (!pUINode->IsContainer()) )
|
|
{
|
|
ASSERT(FALSE); // Invalid Arguments
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (MMCN_SHOW == event)
|
|
{
|
|
_InitView(pUINode);
|
|
|
|
if (!pUINode->GetFolderInfo()->IsExpanded())
|
|
{
|
|
m_pComponentData->_OnExpand(pUINode, hParent, event);
|
|
}
|
|
|
|
_DisplayCachedNodes(pUINode);
|
|
pUINode->GetFolderInfo()->UpdateSerialNumber(m_pComponentData);
|
|
|
|
if (pUINode->GetFolderInfo()->GetSortOnNextSelect())
|
|
{
|
|
m_pFrame->UpdateAllViews(NULL, (LPARAM)pUINode, DS_SORT_RESULT_PANE);
|
|
pUINode->GetFolderInfo()->SetSortOnNextSelect(FALSE);
|
|
}
|
|
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CDSEvent::_DisplayCachedNodes(CUINode* pUINode)
|
|
{
|
|
if ( (pUINode == NULL) || (!pUINode->IsContainer()) )
|
|
{
|
|
ASSERT(FALSE); // Invalid Arguments
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Add the leaf nodes
|
|
CUINodeList* pLeafList = pUINode->GetFolderInfo()->GetLeafList();
|
|
|
|
for (POSITION pos = pLeafList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
POSITION prevPos = pos;
|
|
CUINode* pCurrChildUINode = pLeafList->GetNext(pos);
|
|
ASSERT(pCurrChildUINode != NULL);
|
|
if (pCurrChildUINode->GetExtOp() & OPCODE_MOVE)
|
|
{
|
|
pLeafList->RemoveAt(prevPos);
|
|
pCurrChildUINode->SetExtOp(NULL);
|
|
delete pCurrChildUINode;
|
|
}
|
|
else
|
|
{
|
|
hr = _AddResultItem(pCurrChildUINode);
|
|
}
|
|
}
|
|
|
|
_UpdateObjectCount(FALSE /* set count to 0?*/);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDSEvent::_AddResultItem(CUINode* pUINode, BOOL bSetSelect)
|
|
{
|
|
if (pUINode == NULL)
|
|
{
|
|
ASSERT(FALSE); // Invalid Arguments
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
RESULTDATAITEM rdiListView;
|
|
ZeroMemory(&rdiListView, sizeof(RESULTDATAITEM));
|
|
|
|
rdiListView.lParam = reinterpret_cast<LPARAM>(pUINode);
|
|
rdiListView.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
|
|
rdiListView.str = MMC_CALLBACK;
|
|
rdiListView.nImage = MMC_IMAGECALLBACK;
|
|
|
|
if (bSetSelect)
|
|
{
|
|
rdiListView.mask |= RDI_STATE;
|
|
rdiListView.nState = LVIS_SELECTED | LVIS_FOCUSED;
|
|
}
|
|
return hr = m_pResultData->InsertItem(&rdiListView);
|
|
}
|
|
|
|
HRESULT CDSEvent::SelectResultNode(CUINode* pUINode)
|
|
{
|
|
HRESULTITEM ItemID = 0;
|
|
HRESULT hr = m_pResultData->FindItemByLParam ((LPARAM)pUINode, &ItemID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pResultData->ModifyItemState(0 /*unused*/,
|
|
ItemID,
|
|
LVIS_FOCUSED | LVIS_SELECTED,
|
|
0 /*no removing*/);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
void CDSEvent::_DeleteSingleSel(IDataObject* pDataObject, CUINode* pUINode)
|
|
{
|
|
ASSERT(!pUINode->IsContainer());
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Get the parent container for later use
|
|
//
|
|
CUINode* pParentNode = pUINode->GetParent();
|
|
ASSERT(pParentNode != NULL);
|
|
|
|
CDSCookie* pCookie = NULL;
|
|
if (IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pCookie == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// delete from the back end
|
|
// this call will handle the notifification to extensions
|
|
//
|
|
hr = m_pComponentData->_DeleteFromBackEnd(pDataObject, pCookie);
|
|
}
|
|
else
|
|
{
|
|
hr = pUINode->Delete(m_pComponentData);
|
|
}
|
|
|
|
//
|
|
// update the result pane
|
|
//
|
|
if (SUCCEEDED(hr) && (hr != S_FALSE))
|
|
{
|
|
m_pFrame->UpdateAllViews(NULL, (LPARAM)pUINode, DS_DELETE_OCCURRED);
|
|
}
|
|
|
|
//
|
|
// Remove the '+' next to the parent in the UI if this is the last container
|
|
// object in this container
|
|
//
|
|
if (pParentNode != NULL &&
|
|
pParentNode->GetFolderInfo()->GetContainerList()->GetCount() == 0)
|
|
{
|
|
SCOPEDATAITEM sdi;
|
|
memset(&sdi, 0, sizeof(SCOPEDATAITEM));
|
|
|
|
sdi.ID = pParentNode->GetFolderInfo()->GetScopeItem();
|
|
sdi.mask |= SDI_CHILDREN;
|
|
sdi.cChildren = 0;
|
|
|
|
hr = m_pScopeData->SetItem(&sdi);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// CResultPaneMultipleDeleteHandler
|
|
|
|
class CResultPaneMultipleDeleteHandler : public CMultipleDeleteHandlerBase
|
|
{
|
|
public:
|
|
CResultPaneMultipleDeleteHandler(CDSComponentData* pComponentData, HWND hwnd,
|
|
IDataObject* pDataObject,
|
|
CInternalFormatCracker* pObjCracker,
|
|
CUINodeList* pNodesDeletedList)
|
|
: CMultipleDeleteHandlerBase(pComponentData, hwnd)
|
|
{
|
|
m_pDataObject = pDataObject;
|
|
m_pObjCracker = pObjCracker;
|
|
m_pNodesDeletedList = pNodesDeletedList;
|
|
}
|
|
|
|
protected:
|
|
virtual UINT GetItemCount() { return m_pObjCracker->GetCookieCount();}
|
|
virtual HRESULT BeginTransaction()
|
|
{
|
|
return GetTransaction()->Begin(m_pDataObject, NULL, NULL, FALSE);
|
|
}
|
|
virtual HRESULT DeleteObject(UINT i)
|
|
{
|
|
CUINode* pUINode = m_pObjCracker->GetCookie(i);
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pCookie != NULL)
|
|
{
|
|
// need to pass full ADSI path to ObjectDeletionCheck
|
|
CString strPath;
|
|
GetComponentData()->GetBasePathsInfo()->ComposeADsIPath(
|
|
strPath, pCookie->GetPath());
|
|
|
|
bool fAlternateDeleteMethod = false;
|
|
HRESULT hr = ObjectDeletionCheck(
|
|
strPath,
|
|
pCookie->GetName(),
|
|
pCookie->GetClass(),
|
|
fAlternateDeleteMethod );
|
|
if ( FAILED(hr)
|
|
|| HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr
|
|
|| fAlternateDeleteMethod )
|
|
return hr;
|
|
}
|
|
|
|
return GetComponentData()->GetActiveDS()->DeleteObject(pCookie,
|
|
FALSE); //raise UI for error?
|
|
}
|
|
virtual HRESULT DeleteSubtree(UINT i)
|
|
{
|
|
CUINode* pUINode = m_pObjCracker->GetCookie(i);
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
return GetComponentData()->_DeleteSubtreeFromBackEnd(pCookie);
|
|
}
|
|
virtual void OnItemDeleted(UINT i)
|
|
{
|
|
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(m_pObjCracker->GetCookie(i));
|
|
ASSERT(pDSUINode != NULL);
|
|
|
|
m_pNodesDeletedList->AddTail(pDSUINode);
|
|
}
|
|
virtual void GetItemName(IN UINT i, OUT CString& szName)
|
|
{
|
|
CUINode* pUINode = m_pObjCracker->GetCookie(i);
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pCookie != NULL)
|
|
{
|
|
szName = pCookie->GetName();
|
|
}
|
|
}
|
|
|
|
virtual void GetItemPath(UINT i, CString& szPath)
|
|
{
|
|
CUINode* pUINode = m_pObjCracker->GetCookie(i);
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pCookie != NULL)
|
|
{
|
|
GetComponentData()->GetBasePathsInfo()->ComposeADsIPath(szPath, pCookie->GetPath());
|
|
}
|
|
}
|
|
virtual PCWSTR GetItemClass(UINT i)
|
|
{
|
|
CUINode* pUINode = m_pObjCracker->GetCookie(i);
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
PCWSTR pszClass = NULL;
|
|
if (pCookie != NULL)
|
|
{
|
|
pszClass = pCookie->GetClass();
|
|
}
|
|
return pszClass;
|
|
}
|
|
private:
|
|
IDataObject* m_pDataObject;
|
|
CInternalFormatCracker* m_pObjCracker;
|
|
CUINodeList* m_pNodesDeletedList;
|
|
|
|
};
|
|
|
|
|
|
|
|
void CDSEvent::_DeleteNodeListFromUI(CUINodeList* pNodesDeletedList)
|
|
{
|
|
// finally, we have to update the UI
|
|
if (pNodesDeletedList->GetCount() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TIMER(_T("updating UI after delete, containers first.\n"));
|
|
|
|
//walk this cookie list and take
|
|
//care of the containers (scope pane items)
|
|
for (POSITION pos = pNodesDeletedList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
POSITION posCurrNode = pos;
|
|
CUINode* pCurrNode = pNodesDeletedList->GetNext(pos);
|
|
ASSERT(pCurrNode != NULL);
|
|
HSCOPEITEM ItemID, ParentItemID;
|
|
if (pCurrNode->IsContainer())
|
|
{
|
|
ItemID = pCurrNode->GetFolderInfo()->GetScopeItem();
|
|
CUINode* pParentNode = NULL;
|
|
HRESULT hr = m_pComponentData->m_pScope->GetParentItem(ItemID,
|
|
&ParentItemID,
|
|
(MMC_COOKIE *)&pParentNode);
|
|
m_pComponentData->m_pScope->DeleteItem(ItemID, TRUE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pParentNode->GetFolderInfo()->DeleteNode(pCurrNode);
|
|
pNodesDeletedList->RemoveAt(posCurrNode);
|
|
}
|
|
} // container
|
|
} // for
|
|
|
|
TIMER(_T("updating UI after delete, now the leaf items.\n"));
|
|
|
|
// now update all the views to take care of result pane items
|
|
m_pFrame->UpdateAllViews(NULL,
|
|
(LPARAM)pNodesDeletedList,
|
|
DS_MULTIPLE_DELETE_OCCURRED);
|
|
TIMER(_T("updating UI after delete, done.\n"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CDSEvent::_DeleteMultipleSel(IDataObject* pDataObject, CInternalFormatCracker* pObjCracker)
|
|
{
|
|
// handle the deletion in the back end involving the extensions
|
|
// by calling the delete handler
|
|
|
|
//
|
|
// Get the parent container
|
|
//
|
|
CUINode* pContainerNode = NULL;
|
|
CUINode* pUINode = pObjCracker->GetCookie();
|
|
if (pUINode != NULL)
|
|
{
|
|
pContainerNode = pUINode->GetParent();
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
// REVIEW_MARCOC_PORT: for the time being we assume that all the
|
|
// items in the multiple selection are of DS type
|
|
if (!AreAllNodesOfType<CDSUINode>(pObjCracker))
|
|
{
|
|
//
|
|
// Delegate the delete to the container object
|
|
//
|
|
if (pContainerNode != NULL)
|
|
{
|
|
pContainerNode->DeleteMultiselect(m_pComponentData, pObjCracker);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
else // All are DS nodes
|
|
{
|
|
CUINodeList nodesDeletedList;
|
|
|
|
CResultPaneMultipleDeleteHandler deleteHandler(m_pComponentData, m_hwnd,
|
|
pDataObject, pObjCracker, &nodesDeletedList);
|
|
deleteHandler.Delete();
|
|
|
|
_DeleteNodeListFromUI(&nodesDeletedList);
|
|
}
|
|
|
|
//
|
|
// Remove the '+' sign in the UI if this was the last container child in this container
|
|
//
|
|
if (pContainerNode != NULL &&
|
|
pContainerNode->GetFolderInfo()->GetContainerList()->GetCount() == 0)
|
|
{
|
|
SCOPEDATAITEM sdi;
|
|
memset(&sdi, 0, sizeof(SCOPEDATAITEM));
|
|
|
|
sdi.ID = pContainerNode->GetFolderInfo()->GetScopeItem();
|
|
sdi.mask |= SDI_CHILDREN;
|
|
sdi.cChildren = 0;
|
|
|
|
m_pComponentData->m_pScope->SetItem(&sdi);
|
|
}
|
|
}
|
|
|
|
|
|
void CDSEvent::_Delete(IDataObject* pDataObject, CInternalFormatCracker* pObjCracker)
|
|
{
|
|
CWaitCursor cwait;
|
|
|
|
// protect against deletion with sheets up
|
|
if (m_pComponentData->_WarningOnSheetsUp(pObjCracker))
|
|
return;
|
|
|
|
// do the actual deletion
|
|
if (pObjCracker->GetCookieCount() == 1)
|
|
{
|
|
_DeleteSingleSel(pDataObject, pObjCracker->GetCookie());
|
|
}
|
|
else
|
|
{
|
|
_DeleteMultipleSel(pDataObject, pObjCracker);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL AllObjectsHaveTheSameServerName(IN LPCWSTR lpszServerName,
|
|
IN CObjectNamesFormatCracker* pObjectNamesFormatPaste)
|
|
{
|
|
if (lpszServerName == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
CComBSTR bstrCurrServerName;
|
|
|
|
for (UINT k=0; k<pObjectNamesFormatPaste->GetCount(); k++)
|
|
{
|
|
HRESULT hr = GetServerFromLDAPPath(pObjectNamesFormatPaste->GetName(k),
|
|
&bstrCurrServerName);
|
|
if (FAILED(hr) || (&bstrCurrServerName == NULL))
|
|
{
|
|
// something was wrong
|
|
return FALSE;
|
|
}
|
|
if (_wcsicmp(lpszServerName, bstrCurrServerName) != 0)
|
|
{
|
|
// got something different
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE; // all are the same
|
|
}
|
|
|
|
BOOL HasSameObject(IN CUINode* pUINode, IN IDataObject* pPasteData)
|
|
{
|
|
if (pUINode == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check to see if the target is a DS node
|
|
//
|
|
CDSUINode* pDSTargetNode = NULL;
|
|
BOOL bCookieIsDSUINode = FALSE;
|
|
if(IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
bCookieIsDSUINode = TRUE;
|
|
pDSTargetNode = dynamic_cast<CDSUINode*>(pUINode);
|
|
}
|
|
|
|
CInternalFormatCracker ifc;
|
|
HRESULT hr = ifc.Extract(pPasteData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (UINT k=0; k < ifc.GetCookieCount(); k++)
|
|
{
|
|
//
|
|
// If the cookies are the same return TRUE
|
|
//
|
|
if (ifc.GetCookie(k) == pUINode)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (bCookieIsDSUINode && pDSTargetNode != NULL)
|
|
{
|
|
//
|
|
// If its a DS node and their DNs are the same return TRUE
|
|
//
|
|
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(ifc.GetCookie(k));
|
|
if (pDSUINode != NULL)
|
|
{
|
|
if (_wcsicmp(pDSUINode->GetName(), pDSTargetNode->GetName()) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return FALSE; // all are the different
|
|
}
|
|
|
|
|
|
HRESULT CDSEvent::_QueryPaste(IN CUINode* pUINode, // paste target data object (container)
|
|
IN IDataObject* pPasteData // paste argument data object
|
|
)
|
|
{
|
|
TRACE(L"CDSEvent::_QueryPaste()\n");
|
|
|
|
HRESULT hr = S_OK;
|
|
ASSERT(pUINode != NULL);
|
|
ASSERT(pUINode->IsContainer());
|
|
TRACE(L"MMCN_QUERY_PASTE on %s\n", pUINode->GetName());
|
|
|
|
// First lets make sure we are talking within the same snapin type
|
|
// For instance we will allow paste between instances of AD U&C
|
|
// but we will not allow paste between AD S&S and AD U&C
|
|
|
|
CInternalFormatCracker ifc;
|
|
hr = ifc.Extract(pPasteData);
|
|
if (FAILED(hr) || !ifc.HasData())
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (m_pComponentData->QuerySnapinType() != ifc.GetSnapinType())
|
|
{
|
|
// The snapins are not of the same type so fail
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
if (!IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
//
|
|
// For non DS nodes we will delegate the operation to the node itself
|
|
//
|
|
hr = pUINode->QueryPaste(pPasteData, m_pComponentData);
|
|
return hr;
|
|
}
|
|
|
|
// it is a DS object, extract the cookie
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
ASSERT(pCookie != NULL);
|
|
TRACE(L"MMCN_QUERY_PASTE on %s\n",pCookie->GetPath());
|
|
|
|
CObjectNamesFormatCracker objectNamesFormatPaste;
|
|
hr = objectNamesFormatPaste.Extract(pPasteData);
|
|
|
|
if (!objectNamesFormatPaste.HasData() || (objectNamesFormatPaste.GetCount() < 1))
|
|
{
|
|
// we have something that does not contain the
|
|
// data format for DS operations
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (SNAPINTYPE_SITE == m_pComponentData->QuerySnapinType())
|
|
{
|
|
//
|
|
// DSSite
|
|
//
|
|
if (_wcsicmp(pCookie->GetClass(), L"serversContainer") != 0)
|
|
{
|
|
//
|
|
// Drops only allowed on sites
|
|
//
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// We only allow servers to be moved between sites
|
|
//
|
|
for (UINT idx = 0; idx < objectNamesFormatPaste.GetCount(); idx++)
|
|
{
|
|
if (_wcsicmp(objectNamesFormatPaste.GetClass(idx), L"server") != 0)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
// make sure all items have the same server in the LDAP path
|
|
if (!AllObjectsHaveTheSameServerName(
|
|
m_pComponentData->GetBasePathsInfo()->GetServerName(),
|
|
&objectNamesFormatPaste))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// DSAdmin
|
|
//
|
|
|
|
// we do not allow drops on users, contacts,
|
|
// but we do allow drops on computers
|
|
// NTRAID#NTBUG9-342116-2001/05/07-sburns
|
|
// NOTICE: we allow groups because we allow add to group semantics
|
|
if ((_wcsicmp(pCookie->GetClass(), L"user") == 0) ||
|
|
#ifdef INETORGPERSON
|
|
(_wcsicmp(pCookie->GetClass(), L"inetOrgPerson") == 0) ||
|
|
#endif
|
|
(_wcsicmp(pCookie->GetClass(), L"contact") == 0))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
// make sure all items have the same server in the LDAP path
|
|
if (!AllObjectsHaveTheSameServerName(
|
|
m_pComponentData->GetBasePathsInfo()->GetServerName(),
|
|
&objectNamesFormatPaste))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// make sure we are not dropping an object on itself
|
|
//
|
|
if (HasSameObject(pUINode, pPasteData))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (_wcsicmp(pCookie->GetClass(), L"group") == 0)
|
|
{
|
|
//
|
|
// Check to see if we are trying to add a group type to this group
|
|
// that is illegal
|
|
//
|
|
|
|
//
|
|
// Retrieve the group type
|
|
//
|
|
INT iGroupType = -1;
|
|
CDSCookieInfoGroup* pExtraInfo = dynamic_cast<CDSCookieInfoGroup*>(pCookie->GetExtraInfo());
|
|
if (pExtraInfo != NULL)
|
|
{
|
|
iGroupType = pExtraInfo->m_GroupType;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Couldn't retrieve the group type so don't allow anything to be added
|
|
//
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// See if we are in native mode or mixed mode
|
|
//
|
|
BOOL bMixedMode = TRUE;
|
|
CString szDomainRoot;
|
|
m_pComponentData->GetBasePathsInfo()->GetDefaultRootPath(szDomainRoot);
|
|
|
|
if (!szDomainRoot.IsEmpty())
|
|
{
|
|
//
|
|
// bind to the domain object
|
|
//
|
|
CComPtr<IADs> spDomainObj;
|
|
hr = DSAdminOpenObject(szDomainRoot,
|
|
IID_IADs,
|
|
(void **) &spDomainObj,
|
|
TRUE /*bServer*/);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// retrieve the mixed node attribute
|
|
//
|
|
CComVariant Mixed;
|
|
CComBSTR bsMixed(L"nTMixedDomain");
|
|
spDomainObj->Get(bsMixed, &Mixed);
|
|
bMixedMode = (BOOL)Mixed.bVal;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop through the objects passed by the data object
|
|
// looking for groups
|
|
//
|
|
for (UINT k=0; k < ifc.GetCookieCount(); k++)
|
|
{
|
|
CUINode* pNode = ifc.GetCookie(k);
|
|
if (pNode != NULL)
|
|
{
|
|
//
|
|
// Must be a DS node to be added to a group
|
|
//
|
|
if (!IS_CLASS(*pNode, CDSUINode))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
CDSCookie* pTempCookie = dynamic_cast<CDSCookie*>(pNode->GetNodeData());
|
|
if (pTempCookie)
|
|
{
|
|
if (!m_pComponentData->CanAddCookieToGroup(pTempCookie, iGroupType, bMixedMode))
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK; // we allow to paste
|
|
}
|
|
|
|
|
|
|
|
// given an LDAP path, it returns
|
|
// the LDAP path and the class of the container
|
|
// e.g. given "LDAP://foo.com/cn=a,cn=b,..."
|
|
// it returns "LDAP://foo.com/cn=b,..." and "b_class"
|
|
|
|
HRESULT GetContainerLdapPathAndClass(IN LPCWSTR lpszLdapPath,
|
|
OUT BSTR* pbstrSourceContainerPath,
|
|
OUT BSTR* pbstrSourceContainerClass)
|
|
{
|
|
if (*pbstrSourceContainerPath != NULL)
|
|
{
|
|
::SysFreeString(*pbstrSourceContainerPath);
|
|
*pbstrSourceContainerPath = NULL;
|
|
}
|
|
if (*pbstrSourceContainerClass != NULL)
|
|
{
|
|
::SysFreeString(*pbstrSourceContainerClass);
|
|
*pbstrSourceContainerClass = NULL;
|
|
}
|
|
|
|
// remove leaf element from path
|
|
CPathCracker pathCracker;
|
|
HRESULT hr = pathCracker.Set((LPWSTR)lpszLdapPath, ADS_SETTYPE_FULL);
|
|
RETURN_IF_FAILED(hr);
|
|
hr = pathCracker.RemoveLeafElement();
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
CComBSTR bstrParentLdapPath;
|
|
hr = pathCracker.Retrieve(ADS_FORMAT_X500, pbstrSourceContainerPath);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
// now try to bind and determine the class of the object
|
|
CComPtr<IADs> spParentIADs;
|
|
hr = DSAdminOpenObject(*pbstrSourceContainerPath,
|
|
IID_IADs,
|
|
(void **)&spParentIADs,
|
|
TRUE /*bServer*/);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
CComBSTR bstrParentClass;
|
|
hr = spParentIADs->get_Class(pbstrSourceContainerClass);
|
|
RETURN_IF_FAILED(hr);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// given an LDAP path, it returns
|
|
// the DN of the container
|
|
// e.g. given "LDAP://foo.com/cn=a,cn=b,..."
|
|
// it returns "cn=b,..."
|
|
|
|
HRESULT GetContainerDN(IN LPCWSTR lpszLdapPath,
|
|
OUT BSTR* pbstrSourceContainerDN)
|
|
{
|
|
if (*pbstrSourceContainerDN != NULL)
|
|
{
|
|
::SysFreeString(*pbstrSourceContainerDN);
|
|
*pbstrSourceContainerDN = NULL;
|
|
}
|
|
CPathCracker pathCracker;
|
|
HRESULT hr = pathCracker.Set((LPWSTR)lpszLdapPath, ADS_SETTYPE_FULL);
|
|
RETURN_IF_FAILED(hr);
|
|
hr = pathCracker.RemoveLeafElement();
|
|
RETURN_IF_FAILED(hr);
|
|
return pathCracker.Retrieve(ADS_FORMAT_X500_DN, pbstrSourceContainerDN);
|
|
}
|
|
|
|
|
|
void CDSEvent::_Paste(
|
|
IN CUINode* pUINode, // paste target (container)
|
|
IN IDataObject* pPasteData, // paste argument data object
|
|
OUT LPDATAOBJECT* ppCutDataObj // data object to return for a cut operation
|
|
)
|
|
{
|
|
TRACE(L"CDSEvent::_Paste()\n");
|
|
|
|
ASSERT(pUINode != NULL);
|
|
ASSERT(pUINode->IsContainer());
|
|
TRACE(L"MMCN_PASTE on %s\n", pUINode->GetName());
|
|
|
|
if (ppCutDataObj == NULL)
|
|
{
|
|
//
|
|
// We only support copy in the Saved Queries tree
|
|
//
|
|
pUINode->Paste(pPasteData, m_pComponentData, NULL);
|
|
return;
|
|
}
|
|
|
|
TRACE(L"ppCutDataObj != NULL, cut\n");
|
|
*ppCutDataObj = NULL;
|
|
|
|
if (!IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
//
|
|
// Delegate the paste for non DS nodes to the node itself
|
|
//
|
|
pUINode->Paste(pPasteData, m_pComponentData, ppCutDataObj);
|
|
return;
|
|
}
|
|
|
|
// it is a DS object, extract the cookie
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
ASSERT(pCookie != NULL);
|
|
TRACE(L"MMCN_PASTE on %s\n",pCookie->GetPath());
|
|
|
|
|
|
CObjectNamesFormatCracker objectNamesFormatPaste;
|
|
HRESULT hr = objectNamesFormatPaste.Extract(pPasteData);
|
|
|
|
if (!objectNamesFormatPaste.HasData() || (objectNamesFormatPaste.GetCount() < 1))
|
|
{
|
|
// we have something that does not contain the
|
|
// data format for DS operations
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
UINT nPasteCount = objectNamesFormatPaste.GetCount();
|
|
#ifdef DBG
|
|
// see what we are pasting
|
|
for (UINT kTest=0; kTest<nPasteCount; kTest++)
|
|
{
|
|
TRACE(L"Pasting = %s\n", objectNamesFormatPaste.GetName(kTest));
|
|
}
|
|
#endif
|
|
|
|
// short circuit if the source container
|
|
// is the same as this container (drop onto itself)
|
|
CComBSTR bstrContainerDN;
|
|
hr = GetContainerDN(objectNamesFormatPaste.GetName(0), &bstrContainerDN);
|
|
if (FAILED(hr))
|
|
{
|
|
// something is really bad here...
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
if (_wcsicmp(pCookie->GetPath(), bstrContainerDN) == 0)
|
|
{
|
|
TRACE(L"Dropping on the same container, short circuiting\n");
|
|
return;
|
|
}
|
|
|
|
// make sure all items have the same server in the LDAP path
|
|
if (!AllObjectsHaveTheSameServerName(
|
|
m_pComponentData->GetBasePathsInfo()->GetServerName(),
|
|
&objectNamesFormatPaste))
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
// we do not allow drops on users,
|
|
// but we do allow drops on computers
|
|
// NTRAID#NTBUG9-342116-2001/05/07-sburns
|
|
if ((_wcsicmp(pCookie->GetClass(), L"user") == 0) ||
|
|
#ifdef INETORGPERSON
|
|
(_wcsicmp(pCookie->GetClass(), L"inetOrgPerson") == 0))
|
|
#endif
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if it is a group, dropping means adding to group
|
|
if (_wcsicmp(pCookie->GetClass(), L"group") == 0)
|
|
{
|
|
_PasteAddToGroup(dynamic_cast<CDSUINode*>(pUINode), &objectNamesFormatPaste, ppCutDataObj);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We also want the internal clipboard format so that we can change the path of
|
|
// object(s) that was/were the source of the move
|
|
//
|
|
CInternalFormatCracker ifc;
|
|
hr = ifc.Extract(pPasteData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_PasteDoMove(dynamic_cast<CDSUINode*>(pUINode), &objectNamesFormatPaste, &ifc, ppCutDataObj);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The move can succeed without the internal clipboard format but if the source
|
|
// was from a saved query then it will not be updated with the new path.
|
|
//
|
|
_PasteDoMove(dynamic_cast<CDSUINode*>(pUINode), &objectNamesFormatPaste, NULL, ppCutDataObj);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void CDSEvent::_PasteDoMove(CDSUINode* pTargetUINode,
|
|
CObjectNamesFormatCracker* pObjectNamesFormatPaste,
|
|
CInternalFormatCracker* pInternalFC,
|
|
LPDATAOBJECT* ppCutDataObj)
|
|
{
|
|
//
|
|
// Get the UI source node
|
|
//
|
|
CUINode* pSourceNode = NULL;
|
|
if (pInternalFC != NULL)
|
|
{
|
|
pSourceNode = pInternalFC->GetCookie()->GetParent();
|
|
}
|
|
|
|
//
|
|
// Get the actual source containers from the DS
|
|
// There can be more than one source node especially if the move is from a
|
|
// Saved Query so make a list of all the parents
|
|
//
|
|
CUINodeList possibleMovedObjectList;
|
|
|
|
for (UINT idx = 0; idx < pObjectNamesFormatPaste->GetCount(); idx++)
|
|
{
|
|
CUINode* pTempChildNode = NULL;
|
|
|
|
CString szDN;
|
|
StripADsIPath(pObjectNamesFormatPaste->GetName(idx), szDN);
|
|
if (m_pComponentData->FindUINodeByDN(m_pComponentData->GetRootNode(),
|
|
szDN,
|
|
&pTempChildNode))
|
|
{
|
|
if (pTempChildNode != NULL)
|
|
{
|
|
possibleMovedObjectList.AddTail(pTempChildNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
// bind to the first item in the paste selection and
|
|
// try to get to the container object
|
|
|
|
CComBSTR bstrSourceContainerPath;
|
|
CComBSTR bstrSourceContainerClass;
|
|
|
|
HRESULT hr = GetContainerLdapPathAndClass(pObjectNamesFormatPaste->GetName(0),
|
|
&bstrSourceContainerPath,
|
|
&bstrSourceContainerClass);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
// create a data object to specify the source container
|
|
// the objects are moved from
|
|
CComPtr<IDataObject> spDataObjectContainer;
|
|
hr = CDSNotifyHandlerTransaction::BuildTransactionDataObject(
|
|
bstrSourceContainerPath,
|
|
bstrSourceContainerClass,
|
|
TRUE /*bContainer*/,
|
|
m_pComponentData,
|
|
&spDataObjectContainer);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
CMultiselectMoveHandler moveHandler(m_pComponentData, m_hwnd, NULL);
|
|
hr = moveHandler.Initialize(spDataObjectContainer,
|
|
pObjectNamesFormatPaste,
|
|
pInternalFC);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
CString szTargetContainer;
|
|
m_pComponentData->GetBasePathsInfo()->ComposeADsIPath(szTargetContainer, pTargetUINode->GetCookie()->GetPath());
|
|
|
|
moveHandler.Move(szTargetContainer);
|
|
|
|
*ppCutDataObj = NULL;
|
|
CUINodeList nodesMoved;
|
|
|
|
// -----------------------------------------------------------------
|
|
// code to update the views if the extension says it moved items
|
|
//
|
|
TRACE(_T("Command: returned from extension commdand\n"));
|
|
|
|
if (pSourceNode != NULL &&
|
|
IS_CLASS(*pSourceNode, CDSUINode))
|
|
{
|
|
for (UINT index = 0; index < pInternalFC->GetCookieCount(); index ++)
|
|
{
|
|
CUINode* pUINode = pInternalFC->GetCookie(index);
|
|
|
|
// make sure the node moved is of the right type: for the time
|
|
// being we just deal with DS objects
|
|
if (!IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
ASSERT(FALSE); // should not get here
|
|
continue;
|
|
}
|
|
CDSCookie* pCookie = GetDSCookieFromUINode(pUINode);
|
|
|
|
if (pUINode->GetExtOp() & OPCODE_MOVE)
|
|
{
|
|
if (pTargetUINode == NULL)
|
|
{
|
|
// get the parent from the first node
|
|
// assume that all have the same parent
|
|
CUINode* pPossibleTargetNode = NULL;
|
|
m_pComponentData->FindParentCookie(pCookie->GetPath(), &pPossibleTargetNode);
|
|
if (pPossibleTargetNode != NULL)
|
|
{
|
|
pTargetUINode = dynamic_cast<CDSUINode*>(pPossibleTargetNode);
|
|
}
|
|
}
|
|
|
|
if (pUINode->IsContainer())
|
|
{
|
|
HSCOPEITEM ItemID = 0, ParentItemID = 0;
|
|
ItemID = pUINode->GetFolderInfo()->GetScopeItem();
|
|
if (pSourceNode == NULL)
|
|
{
|
|
// do it once for the first node, all the same
|
|
hr = m_pComponentData->m_pScope->GetParentItem (ItemID,
|
|
&ParentItemID,
|
|
(MMC_COOKIE *)&pSourceNode);
|
|
}
|
|
|
|
// delete the scope item in MMC
|
|
hr = m_pComponentData->m_pScope->DeleteItem(ItemID, TRUE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(_T("DeleteItem failed on %lx (%s).\n"),
|
|
ItemID, pUINode->GetName());
|
|
}
|
|
TRACE(_T("Move postprocessing - deleted scope node: %x (%s)\n"),
|
|
ItemID, pUINode->GetName());
|
|
#endif
|
|
if (pSourceNode)
|
|
{
|
|
pSourceNode->GetFolderInfo()->RemoveNode(pUINode);
|
|
}
|
|
|
|
//
|
|
// Remove all children and mark it as unexpanded so that it will be expanded
|
|
// when selected
|
|
//
|
|
pUINode->GetFolderInfo()->DeleteAllContainerNodes();
|
|
pUINode->GetFolderInfo()->DeleteAllLeafNodes();
|
|
pUINode->GetFolderInfo()->ReSetExpanded();
|
|
|
|
if ((pTargetUINode) && pTargetUINode->GetFolderInfo()->IsExpanded())
|
|
{
|
|
pUINode->ClearParent();
|
|
pTargetUINode->GetFolderInfo()->AddNode(pUINode);
|
|
hr = m_pComponentData->_AddScopeItem(pUINode, pTargetUINode->GetFolderInfo()->GetScopeItem());
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
TRACE(_T("AddItem failed on %lx (%s).\n"),
|
|
ItemID, pUINode->GetName());
|
|
}
|
|
TRACE(_T("Move postprocessing - added scope node: %s\n"),
|
|
pUINode->GetName());
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This object was created during the enumeration of the source container.
|
|
// Since the target container hasn't been expanded yet we can just throw
|
|
// this node away and it will be recreated if the target node ever gets
|
|
// expanded
|
|
//
|
|
delete pUINode;
|
|
pUINode = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// not a container
|
|
if ((pTargetUINode) &&
|
|
(pTargetUINode->GetFolderInfo()->IsExpanded()))
|
|
{
|
|
pUINode->ClearParent();
|
|
pTargetUINode->GetFolderInfo()->AddNode(pUINode);
|
|
}
|
|
|
|
//
|
|
// If the folder is not select (like on cut/paste)
|
|
// the FindItemByLParam() in UpdateAllViews will fail
|
|
// and the node will not be removed from the UI.
|
|
// So just remove it from the node list of the source
|
|
// container.
|
|
//
|
|
if (pSourceNode && m_pSelectedFolderNode != pSourceNode)
|
|
{
|
|
pSourceNode->GetFolderInfo()->RemoveNode(pUINode);
|
|
}
|
|
nodesMoved.AddTail(pUINode);
|
|
}
|
|
if (pUINode)
|
|
{
|
|
pUINode->SetExtOp(NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (pSourceNode != NULL &&
|
|
IS_CLASS(*pSourceNode, CSavedQueryNode))
|
|
{
|
|
//
|
|
// Refresh the target node so that we get new cookies
|
|
// for all the moved objects. It would just be too
|
|
// difficult to do a deep copy of the cookies in the
|
|
// saved query tree
|
|
//
|
|
if (pTargetUINode &&
|
|
pTargetUINode->GetFolderInfo()->IsExpanded())
|
|
{
|
|
m_pComponentData->Refresh(pTargetUINode);
|
|
}
|
|
|
|
//
|
|
// Mark the moved leaf objects with the opcode. Simply remove containers from
|
|
// the UI and the list. The move handler only marks the
|
|
// selected items, not those found using FindUINodeByDN.
|
|
//
|
|
POSITION posPossible = possibleMovedObjectList.GetHeadPosition();
|
|
while (posPossible)
|
|
{
|
|
CUINode* pPossibleMoved = possibleMovedObjectList.GetNext(posPossible);
|
|
if (pPossibleMoved)
|
|
{
|
|
if (pPossibleMoved->IsContainer())
|
|
{
|
|
HSCOPEITEM ItemID = 0;
|
|
ItemID = pPossibleMoved->GetFolderInfo()->GetScopeItem();
|
|
|
|
// delete the scope item in MMC
|
|
hr = m_pComponentData->m_pScope->DeleteItem(ItemID, TRUE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pPossibleMoved->GetParent()->GetFolderInfo()->RemoveNode(pPossibleMoved);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPossibleMoved->SetExtOp(OPCODE_MOVE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now reset the opcode for all the nodes in the saved query tree so
|
|
// that they will still show up the next time the saved query node is selected
|
|
//
|
|
for (UINT index = 0; index < pInternalFC->GetCookieCount(); index ++)
|
|
{
|
|
CUINode* pUINode = pInternalFC->GetCookie(index);
|
|
|
|
if (pUINode)
|
|
{
|
|
pUINode->SetExtOp(NULL);
|
|
}
|
|
} // for
|
|
} // IS_CLASS
|
|
|
|
|
|
if (!nodesMoved.IsEmpty())
|
|
{
|
|
m_pFrame->UpdateAllViews(NULL, (LPARAM)&nodesMoved, DS_MULTIPLE_MOVE_OCCURRED);
|
|
}
|
|
//------------------------------ends here--------------------------------------
|
|
m_pComponentData->SortResultPane(pTargetUINode);
|
|
}
|
|
|
|
void CDSEvent::_PasteAddToGroup(CDSUINode* pUINode,
|
|
CObjectNamesFormatCracker* pObjectNamesFormatPaste,
|
|
LPDATAOBJECT*)
|
|
{
|
|
if (_wcsicmp(pUINode->GetCookie()->GetClass(), L"group") != 0)
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
// get the LDAP path of the group we want to add to
|
|
CString szGroupLdapPath;
|
|
m_pComponentData->GetBasePathsInfo()->ComposeADsIPath(szGroupLdapPath,
|
|
pUINode->GetCookie()->GetPath());
|
|
AddDataObjListToGivenGroup(pObjectNamesFormatPaste,
|
|
szGroupLdapPath,
|
|
pUINode->GetCookie()->GetName(),
|
|
m_pComponentData->GetHWnd(),
|
|
m_pComponentData);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL FindDSUINodeInListByDN(IN LPCWSTR lpszDN,
|
|
IN CUINodeList* pNodeList,
|
|
OUT CDSUINode** ppNode)
|
|
{
|
|
*ppNode = NULL;
|
|
for (POSITION pos = pNodeList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CUINode* pCurrentNode = pNodeList->GetNext(pos);
|
|
CDSUINode* pCurrDSUINode = dynamic_cast<CDSUINode*>(pCurrentNode);
|
|
if (pCurrDSUINode == NULL)
|
|
{
|
|
// not a node with a cookie, just skip
|
|
continue;
|
|
}
|
|
|
|
// get the cookie from the node
|
|
if (_wcsicmp(lpszDN, pCurrDSUINode->GetCookie()->GetPath()) == 0)
|
|
{
|
|
*ppNode = pCurrDSUINode;
|
|
return TRUE;
|
|
}
|
|
}// for
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void FindListOfChildNodes(IN CDSUINode* pDSUIContainerNode,
|
|
IN CObjectNamesFormatCracker* pObjectNamesFormat,
|
|
INOUT CUINodeList* pNodesDeletedList)
|
|
{
|
|
ASSERT(pDSUIContainerNode != NULL);
|
|
ASSERT(pDSUIContainerNode->IsContainer());
|
|
|
|
// it is a DS object, extract the cookie
|
|
CDSCookie* pContainerCookie = pDSUIContainerNode->GetCookie();
|
|
ASSERT(pContainerCookie != NULL);
|
|
TRACE(L"FindListOfChildNodes(%s,...)\n",pContainerCookie->GetPath());
|
|
|
|
//for each item in the list of paths, find it into the list
|
|
// of children
|
|
CPathCracker pathCracker;
|
|
UINT nCount = pObjectNamesFormat->GetCount();
|
|
for (UINT k=0; k<nCount; k++)
|
|
{
|
|
// from the LDAP path, get the DN
|
|
HRESULT hr = pathCracker.Set((LPWSTR)pObjectNamesFormat->GetName(k), ADS_SETTYPE_FULL);
|
|
ASSERT(SUCCEEDED(hr));
|
|
CComBSTR bstrDN;
|
|
hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bstrDN);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
// find it into the lists of children
|
|
CDSUINode* pFoundNode = NULL;
|
|
if (FindDSUINodeInListByDN(bstrDN,
|
|
pDSUIContainerNode->GetFolderInfo()->GetContainerList(),
|
|
&pFoundNode))
|
|
{
|
|
ASSERT(pFoundNode != NULL);
|
|
pNodesDeletedList->AddTail(pFoundNode);
|
|
continue;
|
|
}
|
|
if (FindDSUINodeInListByDN(bstrDN,
|
|
pDSUIContainerNode->GetFolderInfo()->GetLeafList(),
|
|
&pFoundNode))
|
|
{
|
|
ASSERT(pFoundNode != NULL);
|
|
pNodesDeletedList->AddTail(pFoundNode);
|
|
continue;
|
|
}
|
|
} // for
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDSEvent::_CutOrMove(IN IDataObject* pCutOrMoveData)
|
|
{
|
|
TRACE(L"CDSEvent::_CutOrMove()\n");
|
|
|
|
if (pCutOrMoveData == NULL)
|
|
{
|
|
//
|
|
// With a single pass move operation we return a NULL data object
|
|
// but the move was still successful
|
|
//
|
|
return;
|
|
}
|
|
|
|
CInternalFormatCracker ifc;
|
|
HRESULT hr = ifc.Extract(pCutOrMoveData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Non DS nodes
|
|
//
|
|
|
|
//
|
|
// Build a list of the nodes to be deleted
|
|
//
|
|
CUINodeList nodesDeletedList;
|
|
for (UINT nCount = 0; nCount < ifc.GetCookieCount(); nCount++)
|
|
{
|
|
CUINode* pUINode = ifc.GetCookie(nCount);
|
|
if (pUINode != NULL)
|
|
{
|
|
nodesDeletedList.AddTail(pUINode);
|
|
}
|
|
}
|
|
//
|
|
// finally, delete the nodes from the UI
|
|
//
|
|
_DeleteNodeListFromUI(&nodesDeletedList);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// DS Objects
|
|
//
|
|
CObjectNamesFormatCracker objectNamesFormatCutOrMove;
|
|
hr = objectNamesFormatCutOrMove.Extract(pCutOrMoveData);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!objectNamesFormatCutOrMove.HasData() || (objectNamesFormatCutOrMove.GetCount() < 1))
|
|
{
|
|
// we have something that does not contain the
|
|
// data format for DS operations
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
// make sure all items have the same server in the LDAP path
|
|
if (!AllObjectsHaveTheSameServerName(
|
|
m_pComponentData->GetBasePathsInfo()->GetServerName(),
|
|
&objectNamesFormatCutOrMove))
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
// find the source container the objects are moved from
|
|
// (we assume they all come from the same container)
|
|
|
|
TRACE(L"GetName(0) = %s\n", objectNamesFormatCutOrMove.GetName(0));
|
|
|
|
CComBSTR bstrContainerDN;
|
|
hr = GetContainerDN(objectNamesFormatCutOrMove.GetName(0), &bstrContainerDN);
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
TRACE(L"GetContainerDN() bstrContainerDN = %s\n", bstrContainerDN);
|
|
|
|
// find the container object in the folders
|
|
// NOTICE: for the time being we ignore the query folders
|
|
CUINode* pUINode = NULL;
|
|
if (!FindCookieInSubtree(m_pComponentData->GetRootNode(),
|
|
bstrContainerDN,
|
|
m_pComponentData->QuerySnapinType(),
|
|
&pUINode))
|
|
{
|
|
// should never happen...
|
|
return;
|
|
}
|
|
|
|
// found the container node
|
|
ASSERT(pUINode != NULL);
|
|
ASSERT(pUINode->IsContainer());
|
|
|
|
if (!IS_CLASS(*pUINode, CDSUINode))
|
|
{
|
|
// we do not allow paste on non DS nodes,
|
|
// so we should never get here...
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
|
|
ASSERT(pUINode->GetFolderInfo()->IsExpanded());
|
|
|
|
// need to remove the items that are in the data object
|
|
// from the pUINode container: find the list of nodes
|
|
// to be deleted in the
|
|
CUINodeList nodesDeletedList;
|
|
FindListOfChildNodes(dynamic_cast<CDSUINode*>(pUINode),
|
|
&objectNamesFormatCutOrMove,
|
|
&nodesDeletedList);
|
|
|
|
// finally, delete the nodes from the UI
|
|
_DeleteNodeListFromUI(&nodesDeletedList);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDSEvent::HandleViewChange(LPDATAOBJECT pDataObject,
|
|
LPARAM arg,
|
|
LPARAM Action)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TRACE(_T("handle view change. action is %lx.\n"), Action);
|
|
switch (Action)
|
|
{
|
|
case DS_DELETE_OCCURRED:
|
|
{
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam(arg, &ItemID);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
break;
|
|
}
|
|
hr = m_pResultData->DeleteItem(ItemID, 0);
|
|
#ifdef DBG
|
|
if (FAILED(hr)) {
|
|
TRACE (_T("Delete Item Failed on IResultData. Item %lx, hr = %lx\n"),
|
|
ItemID, hr);
|
|
}
|
|
#endif
|
|
// this will fail for all but the first update, we don't care
|
|
hr = m_pSelectedFolderNode->GetFolderInfo()->DeleteNode(reinterpret_cast<CUINode*>(arg));
|
|
_UpdateObjectCount(FALSE);
|
|
break;
|
|
}
|
|
case DS_MULTIPLE_DELETE_OCCURRED:
|
|
{
|
|
TIMER(_T("updating result pane for mult. delete ..."));
|
|
CUINodeList* pNodesDeletedList = reinterpret_cast<CUINodeList*>(arg); // gross
|
|
|
|
for (POSITION pos = pNodesDeletedList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CUINode* pCurrNode = pNodesDeletedList->GetNext(pos);
|
|
ASSERT(pCurrNode != NULL);
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam((LPARAM)pCurrNode,
|
|
&ItemID);
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// We cannot find the item by lParam if the node is not selected so
|
|
// just delete the node from the container
|
|
//
|
|
CUIFolderInfo* pFolderInfo = pCurrNode->GetParent()->GetFolderInfo();
|
|
if (pFolderInfo != NULL)
|
|
{
|
|
hr = pFolderInfo->DeleteNode(pCurrNode);
|
|
}
|
|
continue;
|
|
}
|
|
hr = m_pResultData->DeleteItem(ItemID, 0);
|
|
|
|
CUIFolderInfo* pSelectedFolderInfo = m_pSelectedFolderNode->GetFolderInfo();
|
|
if (pSelectedFolderInfo != NULL)
|
|
{
|
|
// this will fail for all but the first update, we don't care
|
|
hr = m_pSelectedFolderNode->GetFolderInfo()->DeleteNode(pCurrNode);
|
|
}
|
|
}
|
|
_UpdateObjectCount(FALSE);
|
|
TIMER(_T("updating result pane for mult. delete, done"));
|
|
}
|
|
break;
|
|
case DS_RENAME_OCCURRED:
|
|
case DS_UPDATE_OCCURRED:
|
|
{
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam (arg, &ItemID);
|
|
if (SUCCEEDED(hr)) {
|
|
m_pResultData->UpdateItem (ItemID);
|
|
}
|
|
break;
|
|
}
|
|
case DS_MOVE_OCCURRED:
|
|
{
|
|
CDSUINode* pDSUINode = reinterpret_cast<CDSUINode*>(arg);
|
|
CDSUINode* pDSSelectedFolderNode = dynamic_cast<CDSUINode*>(m_pSelectedFolderNode);
|
|
|
|
// REVIEW_MARCOC_PORT: this is working for DS objects only
|
|
// need to generalize for all folder types
|
|
|
|
ASSERT(pDSUINode != NULL);
|
|
ASSERT(pDSSelectedFolderNode != NULL);
|
|
if ((pDSUINode == NULL) || (pDSSelectedFolderNode == NULL))
|
|
break;
|
|
|
|
// remove the result pane item
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam (arg, &ItemID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pSelectedFolderNode->GetFolderInfo()->RemoveNode(pDSUINode);
|
|
hr = m_pResultData->DeleteItem(ItemID, 0);
|
|
}
|
|
|
|
CString szParent;
|
|
hr = m_pComponentData->GetActiveDS()->GetParentDN(pDSUINode->GetCookie(), szParent);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (szParent.CompareNoCase(pDSSelectedFolderNode->GetCookie()->GetPath()) == 0)
|
|
{
|
|
_AddResultItem(pDSUINode);
|
|
|
|
m_pComponentData->SortResultPane(pDSUINode->GetParent());
|
|
_UpdateObjectCount(FALSE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DS_MULTIPLE_MOVE_OCCURRED:
|
|
{
|
|
CUINodeList* pNodesMovedList = reinterpret_cast<CUINodeList*>(arg); // gross
|
|
|
|
//
|
|
// If the selected folder is not a DS node then its probably a saved query
|
|
// in which case we just want to break because we don't want to delete the results
|
|
// of the saved query just change its path
|
|
//
|
|
CDSUINode* pDSSelectedFolderNode = dynamic_cast<CDSUINode*>(m_pSelectedFolderNode);
|
|
if (pDSSelectedFolderNode == NULL)
|
|
break;
|
|
|
|
CString ObjPath;
|
|
CString szParent = L"";
|
|
BOOL fInThisContainer = FALSE;
|
|
|
|
for (POSITION pos = pNodesMovedList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CDSUINode* pDSUINode = dynamic_cast<CDSUINode*>(pNodesMovedList->GetNext(pos));
|
|
// REVIEW_MARCOC_PORT: this is working for DS objects only
|
|
// need to generalize for all folder types
|
|
if (pDSUINode == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
break; // can't do it, should be doing it in the future
|
|
}
|
|
|
|
if (!pDSUINode->IsContainer())
|
|
{
|
|
// it s a leaf node, delete from result pane
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam ((LPARAM)pDSUINode, &ItemID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pSelectedFolderNode->GetFolderInfo()->RemoveNode(pDSUINode);
|
|
hr = m_pResultData->DeleteItem(ItemID, 0);
|
|
}
|
|
|
|
if (szParent.IsEmpty())
|
|
{
|
|
hr = m_pComponentData->GetActiveDS()->GetParentDN(pDSUINode->GetCookie(), szParent);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (szParent.CompareNoCase(pDSSelectedFolderNode->GetCookie()->GetPath()) == 0)
|
|
{
|
|
fInThisContainer = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (fInThisContainer)
|
|
{
|
|
_AddResultItem(pDSUINode);
|
|
}
|
|
}
|
|
}
|
|
_UpdateObjectCount(FALSE);
|
|
break;
|
|
}
|
|
case DS_CREATE_OCCURRED_RESULT_PANE:
|
|
case DS_CREATE_OCCURRED:
|
|
{
|
|
|
|
CUINode* pParent = NULL;
|
|
CUINode* pTmpNode = NULL;
|
|
|
|
if (pDataObject)
|
|
{
|
|
CInternalFormatCracker dobjCracker;
|
|
VERIFY(SUCCEEDED(dobjCracker.Extract(pDataObject)));
|
|
pTmpNode = dobjCracker.GetCookie();
|
|
if (Action == DS_CREATE_OCCURRED_RESULT_PANE)
|
|
{
|
|
pParent = pTmpNode->GetParent();
|
|
}
|
|
else
|
|
{
|
|
pParent = pTmpNode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pParent = m_pSelectedFolderNode;
|
|
}
|
|
if (pParent == m_pSelectedFolderNode)
|
|
{
|
|
// reset icon list, just in case it was a new type of object
|
|
m_pComponentData->FillInIconStrip (m_pRsltImageList);
|
|
|
|
//
|
|
// Add and select the new item
|
|
//
|
|
_AddResultItem(reinterpret_cast<CUINode*>(arg), FALSE);
|
|
m_pComponentData->SortResultPane(pParent);
|
|
|
|
// Must select the result node after the sort to ensure visibility
|
|
SelectResultNode(reinterpret_cast<CUINode*>(arg));
|
|
|
|
_UpdateObjectCount(FALSE);
|
|
}
|
|
else
|
|
{
|
|
pParent->GetFolderInfo()->SetSortOnNextSelect(TRUE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DS_HAVE_DATA:
|
|
{
|
|
CInternalFormatCracker dobjCracker;
|
|
VERIFY(SUCCEEDED(dobjCracker.Extract(pDataObject)));
|
|
CUINode* pContainerNode = dobjCracker.GetCookie();
|
|
if (pContainerNode == m_pSelectedFolderNode)
|
|
{
|
|
TIMER(_T("adding leaf items to view\n"));
|
|
CUINodeList* pNodeList = reinterpret_cast<CUINodeList*>(arg);
|
|
for (POSITION pos = pNodeList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CUINode* pNewUINode = pNodeList->GetNext(pos);
|
|
if (!pNewUINode->IsContainer())
|
|
{
|
|
// add to the scope pane
|
|
_AddResultItem(pNewUINode);
|
|
}
|
|
_UpdateObjectCount(FALSE);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case DS_REFRESH_REQUESTED:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
if (pUINode == m_pSelectedFolderNode) {
|
|
m_pResultData->DeleteAllRsltItems();
|
|
|
|
_UpdateObjectCount (TRUE);
|
|
}
|
|
break;
|
|
}
|
|
case DS_VERB_UPDATE:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
if (pUINode == m_pSelectedFolderNode)
|
|
{
|
|
CContextMenuVerbs* pMenuVerbs = pUINode->GetContextMenuVerbsObject(m_pComponentData);
|
|
|
|
if (pMenuVerbs == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return;
|
|
}
|
|
pMenuVerbs->LoadStandardVerbs(m_pConsoleVerb,
|
|
TRUE/*bScope*/,
|
|
TRUE /*bSelect*/,
|
|
pUINode,
|
|
pDataObject);
|
|
}
|
|
break;
|
|
}
|
|
case DS_DELAYED_EXPAND:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
ASSERT(pUINode->IsContainer());
|
|
// if (pCookie == m_pSelectedFolderNode) {
|
|
m_pFrame->Expand (pUINode->GetFolderInfo()->GetScopeItem(),
|
|
TRUE);
|
|
//}
|
|
}
|
|
break;
|
|
case DS_ICON_STRIP_UPDATE:
|
|
{
|
|
// reset icon list, just in case it was a new type of object
|
|
m_pComponentData->FillInIconStrip (m_pRsltImageList);
|
|
}
|
|
break;
|
|
|
|
case DS_IS_COOKIE_SELECTION:
|
|
{
|
|
PUINODESELECTION pUINodeSel = reinterpret_cast<PUINODESELECTION>(arg); //gross
|
|
if (pUINodeSel->IsSelection)
|
|
{
|
|
// got the snawer from some other view, just skip
|
|
break;
|
|
}
|
|
if (pUINodeSel->pUINode == m_pSelectedFolderNode)
|
|
{
|
|
// selected folder in this view
|
|
pUINodeSel->IsSelection = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// not selected in this view, but look for the parents
|
|
// of the current selection
|
|
CUINode* pParentNode = m_pSelectedFolderNode->GetParent();
|
|
while (pParentNode)
|
|
{
|
|
if (pUINodeSel->pUINode == pParentNode)
|
|
{
|
|
pUINodeSel->IsSelection = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pParentNode = pParentNode->GetParent();
|
|
}
|
|
} // while
|
|
}
|
|
} // case
|
|
break;
|
|
|
|
case DS_SORT_RESULT_PANE:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
MMC_SORT_SET_DATA* pColumnData = NULL;
|
|
TIMER(_T("sorting result pane, starting"));
|
|
CDSColumnSet* pColumnSet = pUINode->GetColumnSet(m_pComponentData);
|
|
if (pColumnSet == NULL)
|
|
break;
|
|
|
|
LPCWSTR lpszID = pColumnSet->GetColumnID();
|
|
size_t iLen = wcslen(lpszID);
|
|
|
|
// allocate enough memory for the struct and the column ID
|
|
SColumnSetID* pNodeID = (SColumnSetID*)malloc(sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
|
|
if (pNodeID != NULL)
|
|
{
|
|
memset(pNodeID, 0, sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)));
|
|
pNodeID->cBytes = static_cast<ULONG>(iLen * sizeof(WCHAR));
|
|
memcpy(pNodeID->id, lpszID, (iLen * sizeof(WCHAR)));
|
|
|
|
CComPtr<IColumnData> spColumnData;
|
|
hr = m_pFrame->QueryInterface(IID_IColumnData, (void**)&spColumnData);
|
|
if (spColumnData != NULL)
|
|
{
|
|
hr = spColumnData->GetColumnSortData(pNodeID, &pColumnData);
|
|
}
|
|
|
|
if (hr == S_OK && pColumnData != NULL)
|
|
{
|
|
m_pResultData->Sort(pColumnData->pSortData->nColIndex, pColumnData->pSortData->dwSortOptions, NULL);
|
|
CoTaskMemFree(pColumnData);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Sort by the name column ascending if the user hasn't persisted something else
|
|
//
|
|
m_pResultData->Sort(0, RSI_NOSORTICON, NULL);
|
|
}
|
|
free(pNodeID);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Sort by the name column ascending if the user hasn't persisted something else
|
|
//
|
|
m_pResultData->Sort(0, RSI_NOSORTICON, NULL);
|
|
}
|
|
break;
|
|
TIMER(_T("sorting result pane, done"));
|
|
|
|
if (pUINode != m_pSelectedFolderNode &&
|
|
pUINode->IsContainer())
|
|
{
|
|
pUINode->GetFolderInfo()->SetSortOnNextSelect(TRUE);
|
|
}
|
|
}
|
|
break;
|
|
case DS_UPDATE_VISIBLE_COLUMNS:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
if (m_bUpdateAllViewsOrigin)
|
|
{
|
|
// this message originated from this instance,
|
|
// it is handled separately
|
|
break;
|
|
}
|
|
|
|
CDSColumnSet* pColumnSet = pUINode->GetColumnSet(m_pComponentData);
|
|
if (pColumnSet == NULL)
|
|
break;
|
|
|
|
CComPtr<IColumnData> spColumnData;
|
|
hr = m_pFrame->QueryInterface(IID_IColumnData, (void**)&spColumnData);
|
|
if (spColumnData != NULL)
|
|
hr = pColumnSet->LoadFromColumnData(spColumnData);
|
|
if (FAILED(hr))
|
|
{
|
|
pColumnSet->SetAllColumnsToDefaultVisibility();
|
|
}
|
|
break;
|
|
}
|
|
case DS_UPDATE_OBJECT_COUNT:
|
|
_UpdateObjectCount(FALSE);
|
|
break;
|
|
|
|
case DS_UNSELECT_OBJECT:
|
|
{
|
|
CUINode* pUINode = reinterpret_cast<CUINode*>(arg);
|
|
if (pUINode != NULL)
|
|
{
|
|
HRESULTITEM ItemID;
|
|
hr = m_pResultData->FindItemByLParam ((LPARAM)pUINode, &ItemID);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VERIFY(SUCCEEDED(m_pResultData->ModifyItemState(0 /*unused*/,
|
|
ItemID,
|
|
0 /*not adding*/,
|
|
LVIS_FOCUSED | LVIS_SELECTED)));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
} // switch
|
|
|
|
}
|
|
|
|
void
|
|
CDSEvent::_UpdateObjectCount(BOOL fZero)
|
|
{
|
|
if (!m_pSelectedFolderNode->IsContainer())
|
|
{
|
|
ASSERT(m_pSelectedFolderNode->IsContainer());
|
|
return;
|
|
}
|
|
|
|
UINT cItems = 0;
|
|
if (!fZero)
|
|
{
|
|
CUINodeList* pclFolders = m_pSelectedFolderNode->GetFolderInfo()->GetContainerList();
|
|
CUINodeList* pclLeaves = m_pSelectedFolderNode->GetFolderInfo()->GetLeafList();
|
|
|
|
if (pclFolders && pclLeaves)
|
|
{
|
|
cItems = (UINT)(pclFolders->GetCount() + pclLeaves->GetCount());
|
|
}
|
|
}
|
|
else //set the count to 0
|
|
{
|
|
m_pSelectedFolderNode->GetFolderInfo()->SetTooMuchData(FALSE, 0);
|
|
}
|
|
|
|
CString csTemp;
|
|
if (IS_CLASS(*m_pSelectedFolderNode, CSavedQueryNode))
|
|
{
|
|
CSavedQueryNode* pSavedQueryNode = dynamic_cast<CSavedQueryNode*>(m_pSelectedFolderNode);
|
|
if (pSavedQueryNode && !pSavedQueryNode->IsValid())
|
|
{
|
|
VERIFY(csTemp.LoadString(IDS_DESCBAR_INVALID_SAVEDQUERY));
|
|
}
|
|
}
|
|
|
|
if (csTemp.IsEmpty())
|
|
{
|
|
if (m_pSelectedFolderNode->GetFolderInfo()->HasTooMuchData())
|
|
{
|
|
UINT nApprox = m_pSelectedFolderNode->GetFolderInfo()->GetApproxTotalContained();
|
|
nApprox = __max(nApprox, cItems);
|
|
|
|
csTemp.Format(IDS_DESCBAR_TOO_MUCH_DATA,
|
|
nApprox);
|
|
}
|
|
else
|
|
{
|
|
VERIFY(csTemp.LoadString(IDS_OBJECTS));
|
|
}
|
|
}
|
|
|
|
CString csDescription;
|
|
csDescription.Format (L"%d%s", cItems, csTemp);
|
|
if (m_pComponentData->m_pQueryFilter &&
|
|
m_pComponentData->m_pQueryFilter->IsFilteringActive())
|
|
{
|
|
CString csFilter;
|
|
csFilter.LoadString (IDS_FILTERING_ON);
|
|
csDescription += csFilter;
|
|
}
|
|
|
|
if (m_pResultData)
|
|
{
|
|
m_pResultData->SetDescBarText ((LPWSTR)(LPCWSTR)csDescription);
|
|
}
|
|
}
|
|
|
|
HRESULT CDSEvent::_SetColumns(CUINode* pUINode)
|
|
{
|
|
ASSERT(pUINode->IsContainer());
|
|
|
|
TRACE(_T("CDSEvent::_SetColumns on container %s\n"),
|
|
(LPWSTR)(LPCWSTR)pUINode->GetName());
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
CDSColumnSet* pColumnSet = pUINode->GetColumnSet(m_pComponentData);
|
|
if (pColumnSet == NULL)
|
|
return hr;
|
|
|
|
for (POSITION pos = pColumnSet->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CDSColumn* pColumn = (CDSColumn*)pColumnSet->GetNext(pos);
|
|
int nWidth = (pColumn->IsVisible()) ? AUTO_WIDTH : HIDE_COLUMN;
|
|
hr = m_pHeader->InsertColumn(pColumn->GetColumnNum(),
|
|
pColumn->GetHeader(),
|
|
pColumn->GetFormat(),
|
|
nWidth);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = m_pHeader->SetColumnWidth(pColumn->GetColumnNum(),
|
|
pColumn->GetWidth());
|
|
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|