|
|
/*++
Copyright (C) 1997-1999 Microsoft Corporation
Module Name:
ctvctl.cpp
Abstract:
This module implements TreeView OCX for Device Manager snapin
Author:
William Hsieh (williamh) created
Revision History:
--*/ // CTVCtl.cpp : Implementation of the CTVCtrl OLE control class.
#include "stdafx.h"
#include <afxcmn.h>
#include "ctv.h"
#include "CTVCtl.h"
#include "resource.h"
#define GET_X_LPARAM(lParam) (int)(short)LOWORD(lParam)
#define GET_Y_LPARAM(lParam) (int)(short)HIWORD(lParam)
IMPLEMENT_DYNCREATE(CTVCtrl, COleControl)
BEGIN_INTERFACE_MAP(CTVCtrl, COleControl) INTERFACE_PART(CTVCtrl, IID_IDMTVOCX, DMTVOCX) END_INTERFACE_MAP()
const IID IID_IDMTVOCX = {0x142525f2,0x59d8,0x11d0,{0xab,0xf0,0x00,0x20,0xaf,0x6b,0x0b,0x7a}}; const IID IID_ISnapinCallback = {0x8e0ba98a,0xd161,0x11d0,{0x83,0x53,0x00,0xa0,0xc9,0x06,0x40,0xbf}};
ULONG EXPORT CTVCtrl::XDMTVOCX::AddRef() { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->ExternalAddRef(); } ULONG EXPORT CTVCtrl::XDMTVOCX::Release() { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->ExternalRelease(); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::QueryInterface( REFIID iid, void ** ppvObj ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->ExternalQueryInterface(&iid, ppvObj); }
HTREEITEM EXPORT CTVCtrl::XDMTVOCX::InsertItem( LPTV_INSERTSTRUCT pis ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->InsertItem(pis); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::DeleteItem( HTREEITEM hitem ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->DeleteItem(hitem); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::DeleteAllItems( ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->DeleteAllItems(); }
HIMAGELIST EXPORT CTVCtrl::XDMTVOCX::SetImageList( INT iImage, HIMAGELIST himl ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SetImageList(iImage, himl); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::SetItem( TV_ITEM* pitem ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SetItem(pitem); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::Expand( UINT Flags, HTREEITEM hitem ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->Expand(Flags, hitem); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::SelectItem( UINT Flags, HTREEITEM hitem ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SelectItem(Flags, hitem); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::SetStyle( DWORD dwStyle ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SetStyle(dwStyle); }
HWND EXPORT CTVCtrl::XDMTVOCX::GetWindowHandle( ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetWindowHandle(); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::GetItem( TV_ITEM* pti ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetItem(pti); }
HTREEITEM EXPORT CTVCtrl::XDMTVOCX::GetNextItem( UINT Flags, HTREEITEM htiRef ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetNextItem(Flags, htiRef); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::SelectItem( HTREEITEM hti ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SelectItem(hti); }
UINT EXPORT CTVCtrl::XDMTVOCX::GetCount( ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetCount(); }
HTREEITEM EXPORT CTVCtrl::XDMTVOCX::GetSelectedItem( ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetSelectedItem(); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::Connect( IComponent* pIComponent, MMC_COOKIE cookie ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->Connect(pIComponent, cookie); }
HRESULT EXPORT CTVCtrl::XDMTVOCX::SetActiveConnection( MMC_COOKIE cookie ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SetActiveConnection(cookie); }
MMC_COOKIE EXPORT CTVCtrl::XDMTVOCX::GetActiveConnection() { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->GetActiveConnection(); }
long EXPORT CTVCtrl::XDMTVOCX::SetRedraw(BOOL Redraw) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->SetRedraw(Redraw); }
BOOL EXPORT CTVCtrl::XDMTVOCX::EnsureVisible( HTREEITEM hitem ) { METHOD_PROLOGUE(CTVCtrl, DMTVOCX) return pThis->EnsureVisible(hitem); }
/////////////////////////////////////////////////////////////////////////////
// Message map
BEGIN_MESSAGE_MAP(CTVCtrl, COleControl) //{{AFX_MSG_MAP(CTVCtrl)
ON_WM_DESTROY() ON_WM_CONTEXTMENU() //}}AFX_MSG_MAP
ON_MESSAGE(OCM_COMMAND, OnOcmCommand) ON_MESSAGE(OCM_NOTIFY, OnOcmNotify) END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Dispatch map
BEGIN_DISPATCH_MAP(CTVCtrl, COleControl) //{{AFX_DISPATCH_MAP(CTVCtrl)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////////////////
// Event map
BEGIN_EVENT_MAP(CTVCtrl, COleControl) //{{AFX_EVENT_MAP(CTVCtrl)
// NOTE - ClassWizard will add and remove event map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_EVENT_MAP
END_EVENT_MAP()
/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid
IMPLEMENT_OLECREATE_EX(CTVCtrl, "CTREEVIEW.CTreeViewCtrl.1", 0xcd6c7868, 0x5864, 0x11d0, 0xab, 0xf0, 0, 0x20, 0xaf, 0x6b, 0xb, 0x7a)
/////////////////////////////////////////////////////////////////////////////
// Type library ID and version
IMPLEMENT_OLETYPELIB(CTVCtrl, _tlid, _wVerMajor, _wVerMinor)
/////////////////////////////////////////////////////////////////////////////
// Interface IDs
const IID BASED_CODE IID_DTV = { 0xcd6c7866, 0x5864, 0x11d0, { 0xab, 0xf0, 0, 0x20, 0xaf, 0x6b, 0xb, 0x7a}}; const IID BASED_CODE IID_DTVEvents = { 0xcd6c7867, 0x5864, 0x11d0, { 0xab, 0xf0, 0, 0x20, 0xaf, 0x6b, 0xb, 0x7a}};
/////////////////////////////////////////////////////////////////////////////
// Control type information
static const DWORD BASED_CODE _dwTVOleMisc = OLEMISC_ACTIVATEWHENVISIBLE | OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CTVCtrl, IDS_TV, _dwTVOleMisc)
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::CTVCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CTVCtrl
BOOL CTVCtrl::CTVCtrlFactory::UpdateRegistry(BOOL bRegister) { if (bRegister) { return AfxOleRegisterControlClass( AfxGetInstanceHandle(), m_clsid, m_lpszProgID, IDS_TV, IDB_TV, afxRegApartmentThreading, _dwTVOleMisc, _tlid, _wVerMajor, _wVerMinor); } else { return AfxOleUnregisterClass(m_clsid, m_lpszProgID); } }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::CTVCtrl - Constructor
CTVCtrl::CTVCtrl() { InitializeIIDs(&IID_DTV, &IID_DTVEvents);
m_nConnections = 0; m_pIComponent = NULL; m_pISnapinCallback = NULL; m_Destroyed = FALSE; }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::~CTVCtrl - Destructor
CTVCtrl::~CTVCtrl() { if (m_pISnapinCallback) { m_pISnapinCallback->Release(); }
if (m_pIComponent) { m_pIComponent->Release(); } }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::OnDraw - Drawing function
void CTVCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { DoSuperclassPaint(pdc, rcBounds); }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::DoPropExchange - Persistence support
void CTVCtrl::DoPropExchange(CPropExchange* pPX) { ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor)); COleControl::DoPropExchange(pPX); }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::OnResetState - Reset control to default state
void CTVCtrl::OnResetState() { COleControl::OnResetState(); // Resets defaults found in DoPropExchange
}
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::PreCreateWindow - Modify parameters for CreateWindowEx
BOOL CTVCtrl::PreCreateWindow(CREATESTRUCT& cs) { cs.lpszClass = _T("SysTreeView32"); //
// Turn off WS_EX_NOPARENTNOTIFY style bit so that our parents
// receive mouse clicks on our window. I do not know why MFC
// fundation class turns this on for an OCX.
//
cs.dwExStyle &= ~(WS_EX_NOPARENTNOTIFY); return COleControl::PreCreateWindow(cs); }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::IsSubclassedControl - This is a subclassed control
BOOL CTVCtrl::IsSubclassedControl() { return TRUE; }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl::OnOcmCommand - Handle command messages
LRESULT CTVCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam) { #ifdef _WIN32
WORD wNotifyCode = HIWORD(wParam); #else
WORD wNotifyCode = HIWORD(lParam); #endif
return 0; }
/////////////////////////////////////////////////////////////////////////////
// CTVCtrl message handlers
///////////////////////////////////////////////////////////////////////
///
/// Tree View functions
///
HRESULT CTVCtrl::Connect( IComponent* pIComponent, MMC_COOKIE cookie ) { HRESULT hr = S_OK;
if (0 == m_nConnections) { ASSERT(NULL == m_pIComponent);
m_pIComponent = pIComponent; m_pIComponent->AddRef(); hr = m_pIComponent->QueryInterface(IID_ISnapinCallback, reinterpret_cast<void**>(&m_pISnapinCallback) ); }
// A single snapin may have multiple nodes that uses us as result pane
// display media, therefore, we may be connected mutlple times.
// However, we get created only when MMC creates a new snapin instance.
// This means, every connection call must provide the same
// pIComponent and pConsole.
//
ASSERT(m_pIComponent == pIComponent);
if (SUCCEEDED(hr)) { m_nConnections++; hr = SetActiveConnection(cookie); } return hr; }
HRESULT CTVCtrl::SetActiveConnection( MMC_COOKIE cookie ) { m_ActiveCookie = cookie; return S_OK; }
MMC_COOKIE CTVCtrl::GetActiveConnection() { return m_ActiveCookie; }
HTREEITEM CTVCtrl::InsertItem( LPTV_INSERTSTRUCT pis ) { return (HTREEITEM)SendMessage(TVM_INSERTITEM, 0, (LPARAM)pis); }
HRESULT CTVCtrl::DeleteItem( HTREEITEM hitem ) { if (SendMessage(TVM_DELETEITEM, 0, (LPARAM)hitem)) { return S_OK; } else { return E_UNEXPECTED; } }
HRESULT CTVCtrl::DeleteAllItems() { return DeleteItem((HTREEITEM)TVI_ROOT); }
HIMAGELIST CTVCtrl::SetImageList( INT iImage, HIMAGELIST hmil ) { return (HIMAGELIST)SendMessage(TVM_SETIMAGELIST, (WPARAM)iImage, (LPARAM)hmil); }
HRESULT CTVCtrl::SetItem( TV_ITEM* pitem ) { if (SendMessage(TVM_SETITEM, 0, (LPARAM)pitem)) { return S_OK; } else { return E_UNEXPECTED; } }
HRESULT CTVCtrl::Expand( UINT Flags, HTREEITEM hitem ) { if (SendMessage(TVM_EXPAND, (WPARAM) Flags, (LPARAM)hitem)) { return S_OK; } else { return E_UNEXPECTED; } }
HRESULT CTVCtrl::SelectItem( UINT Flags, HTREEITEM hitem ) { if (SendMessage(TVM_SELECTITEM, (WPARAM)Flags, (LPARAM)hitem)) { return S_OK; } else { return E_UNEXPECTED; } }
HRESULT CTVCtrl::SetStyle( DWORD dwStyle ) { if (ModifyStyle(0, dwStyle)) { return S_OK; } else { return E_UNEXPECTED; } }
HWND CTVCtrl::GetWindowHandle( ) { return m_hWnd; }
HRESULT CTVCtrl::GetItem( TV_ITEM* pti ) { if (SendMessage(TVM_GETITEM, 0, (LPARAM)pti)) { return S_OK; } else { return E_UNEXPECTED; } }
HTREEITEM CTVCtrl::GetNextItem( UINT Flags, HTREEITEM htiRef ) { return (HTREEITEM) SendMessage(TVM_GETNEXTITEM, (WPARAM)Flags, (LPARAM)htiRef); }
HRESULT CTVCtrl::SelectItem( HTREEITEM hti ) { if (SendMessage(TVM_SELECTITEM, 0, (LPARAM) hti)) { return S_OK; } else { return S_FALSE; } }
UINT CTVCtrl::GetCount( ) { return (UINT)SendMessage(TVM_GETCOUNT, 0, 0); }
HTREEITEM CTVCtrl::HitTest( LONG x, LONG y, UINT* pFlags ) { POINT pt; pt.x = x; pt.y = y;
ScreenToClient(&pt);
TV_HITTESTINFO tvhti; tvhti.pt = pt;
HTREEITEM hti = (HTREEITEM)SendMessage(TVM_HITTEST, 0, (LPARAM)&tvhti);
if (hti && pFlags) { *pFlags = tvhti.flags; }
return hti; }
HTREEITEM CTVCtrl::GetSelectedItem( ) { return (HTREEITEM)SendMessage(TVM_GETNEXTITEM, TVGN_CARET, 0); }
HRESULT CTVCtrl::SetRedraw( BOOL Redraw ) { if (Redraw) { Invalidate(); }
return S_OK; }
BOOL CTVCtrl::EnsureVisible( HTREEITEM hitem ) { return (BOOL)SendMessage(TVM_ENSUREVISIBLE, 0, (LPARAM)hitem); }
LRESULT CTVCtrl::OnOcmNotify( WPARAM wParam, LPARAM lParam ) {
LPARAM param, arg; MMC_COOKIE cookie = 0;
HRESULT hr = S_FALSE; TV_NOTIFY_CODE NotifyCode; TV_ITEM TI;
NotifyCode = TV_NOTIFY_CODE_UNKNOWN; switch (((NMHDR*)lParam)->code) { case NM_RCLICK: case NM_RDBLCLK: case NM_CLICK: case NM_DBLCLK: NotifyCode = DoMouseNotification(((NMHDR*)lParam)->code, &cookie, &arg, ¶m); break; case TVN_KEYDOWN: TI.hItem = GetSelectedItem(); TI.mask = TVIF_PARAM; if (TI.hItem && SUCCEEDED(GetItem(&TI))) { cookie = (MMC_COOKIE)TI.lParam; NotifyCode = TV_NOTIFY_CODE_KEYDOWN; param = ((TV_KEYDOWN*)lParam)->wVKey; arg = (LPARAM)TI.hItem; } break; case NM_SETFOCUS: TI.hItem = GetSelectedItem(); TI.mask = TVIF_PARAM; if (TI.hItem && SUCCEEDED(GetItem(&TI))) { cookie = (MMC_COOKIE)TI.lParam; NotifyCode = TV_NOTIFY_CODE_FOCUSCHANGED; param = 1; arg = (LPARAM)TI.hItem; } break; case TVN_SELCHANGEDA: case TVN_SELCHANGEDW: NotifyCode = TV_NOTIFY_CODE_SELCHANGED; arg = (LPARAM)((NM_TREEVIEW*)lParam)->itemNew.hItem; cookie = (MMC_COOKIE)((NM_TREEVIEW*)lParam)->itemNew.lParam; param = (LPARAM)((NM_TREEVIEW*)lParam)->action; break; case TVN_ITEMEXPANDEDA: case TVN_ITEMEXPANDEDW: NotifyCode = TV_NOTIFY_CODE_EXPANDED; arg = (LPARAM)((NM_TREEVIEW*)lParam)->itemNew.hItem; cookie = (MMC_COOKIE)((NM_TREEVIEW*)lParam)->itemNew.lParam; param = (LPARAM)((NM_TREEVIEW*)lParam)->action; break;
default: NotifyCode = TV_NOTIFY_CODE_UNKNOWN; break; }
if (TV_NOTIFY_CODE_UNKNOWN != NotifyCode && m_pISnapinCallback) { hr = m_pISnapinCallback->tvNotify(*this, cookie, NotifyCode, arg, param); if (S_FALSE == hr) { //
// Translate RCLICK to context menu
//
if (TV_NOTIFY_CODE_RCLICK == NotifyCode) { SendMessage(WM_CONTEXTMENU, (WPARAM)m_hWnd, GetMessagePos()); hr = S_OK; }
//
// Translate Shift-F10 or VK_APPS to context menu
//
else if (TV_NOTIFY_CODE_KEYDOWN == NotifyCode && (VK_F10 == param && GetKeyState(VK_SHIFT) < 0) || (VK_APPS == param)) { RECT rect; *((HTREEITEM*)&rect) = (HTREEITEM)arg; if (SendMessage(TVM_GETITEMRECT, TRUE, (LPARAM)&rect)) { POINT pt; pt.x = (rect.left + rect.right) / 2; pt.y = (rect.top + rect.bottom) / 2; ClientToScreen(&pt); SendMessage(WM_CONTEXTMENU, (WPARAM)m_hWnd, MAKELPARAM(pt.x, pt.y)); hr = S_OK; } } } }
//
// On a TVN_KEYDOWN we should always return 0, otherwise the treeview
// control gets confused.
//
if (((NMHDR*)lParam)->code == TVN_KEYDOWN) {
hr = S_FALSE; }
ASSERT(S_OK == hr || S_FALSE == hr);
if (S_OK == hr) { return 1; }
return 0; }
TV_NOTIFY_CODE CTVCtrl::DoMouseNotification( UINT Code, MMC_COOKIE* pcookie, LPARAM* parg, LPARAM* pparam ) { DWORD MsgPos; POINT point;
ASSERT(pparam && parg && pcookie); *pparam = 0; *parg = 0; MsgPos = GetMessagePos(); point.x = GET_X_LPARAM(MsgPos); point.y = GET_Y_LPARAM(MsgPos); UINT htFlags; HTREEITEM hti = HitTest(point.x, point.y, &htFlags);
TV_NOTIFY_CODE NotifyCode = TV_NOTIFY_CODE_UNKNOWN;
if (hti && (htFlags & TVHT_ONITEM)) { TV_ITEM TI; TI.hItem = hti; TI.mask = TVIF_PARAM;
if (SUCCEEDED(GetItem(&TI))) { switch (Code) { case NM_RCLICK: NotifyCode = TV_NOTIFY_CODE_RCLICK; break; case NM_RDBLCLK: NotifyCode = TV_NOTIFY_CODE_RDBLCLK; break; case NM_CLICK: NotifyCode = TV_NOTIFY_CODE_CLICK; break; case NM_DBLCLK: NotifyCode = TV_NOTIFY_CODE_DBLCLK; break; default: NotifyCode = TV_NOTIFY_CODE_UNKNOWN; break; }
if (TV_NOTIFY_CODE_UNKNOWN != NotifyCode) { *parg = (LPARAM)hti; *pparam = htFlags; *pcookie = (MMC_COOKIE)TI.lParam; } } }
return NotifyCode; }
// OnDestroy may be called on two occasions:
// (1). We are the current active result pane window and MMC
// is destroying our parent window(MDI client). Note that
// if we are not the active result pane window, this function
// will not get called until (2).
// (2). our reference count has reached zero.
//
// When (1) happens, the snapin may be still holding reference to us
// thus, even though our window has been destroyed (2) still happens
// (unless PostNcDestory is done which MFC reset m_hWnd)and we end up
// destoying the window twice.
// So, we keep an eye on OnDestroy and do nothing after it has been called.
// We can not wait for PostNcDestroy because we have no idea when it would
// come.
//
void CTVCtrl::OnDestroy() { if (!m_Destroyed) { COleControl::OnDestroy(); m_Destroyed = TRUE; } }
void CTVCtrl::OnContextMenu(CWnd* pWnd, CPoint point) { POINT pt = point; UINT htFlags; HTREEITEM hti = HitTest(pt.x, pt.y, &htFlags);
if (hti) { TV_ITEM TI; TI.hItem = hti; TI.mask = TVIF_PARAM; if (SUCCEEDED(GetItem(&TI))) { m_pISnapinCallback->tvNotify(*this, (MMC_COOKIE)TI.lParam, TV_NOTIFY_CODE_CONTEXTMENU, (LPARAM)hti, (LPARAM)&point ); } } }
// The upmost frame window may have its own accelerator table and may take
// away certain key combinations we really need.
BOOL CTVCtrl::PreTranslateMessage(MSG* pMsg) { if (WM_KEYDOWN == pMsg->message && (VK_DELETE == pMsg->wParam || VK_RETURN == pMsg->wParam)) { OnKeyDown((UINT)pMsg->wParam, LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)); return TRUE; }
else if (WM_SYSKEYDOWN == pMsg->message && VK_F10 == pMsg->wParam && GetKeyState(VK_SHIFT) < 0) { // Shift-F10 will be translated to WM_CONTEXTMENU
OnSysKeyDown((UINT)pMsg->wParam, LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)); return TRUE; }
return COleControl::PreTranslateMessage(pMsg); }
|