This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
@@ -0,0 +1,219 @@
// ShellContextMenu.cpp: implementation of the CShellContextMenu class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShellContextMenu.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
BOOL CShellContextMenu::InvokeCommand(UINT nCmdID)
{
if (!IsValid() || !IsCommandInRange(nCmdID))
return FALSE;
CMINVOKECOMMANDINFO cmi;
ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO));
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.hwnd = m_pOwner->GetSafeHwnd();
cmi.lpVerb = MAKEINTRESOURCE(nCmdID - m_nCmdFirstID);
cmi.nShow = SW_SHOWNORMAL;
return (NOERROR == m_pCtxMenu->InvokeCommand(&cmi));
}
BOOL CShellContextMenu::FillMenu(CMenu *pMenu, UINT nStartIndex, UINT nCmdFirstID, UINT nCmdLastID, UINT nFlags)
{
if (!IsValid())
return FALSE;
m_nCmdFirstID = nCmdFirstID;
m_nCmdLastID = nCmdLastID;
UINT nDefID = pMenu->GetDefaultItem(GMDI_USEDISABLED, TRUE);
HRESULT hr = m_pCtxMenu->QueryContextMenu(pMenu->GetSafeHmenu(),
nStartIndex, m_nCmdFirstID, m_nCmdLastID, nFlags);
if (nFlags & CMF_NODEFAULT)
pMenu->SetDefaultItem(nDefID, TRUE);
if (FAILED(hr))
{
m_nCmdLastID = m_nCmdFirstID = 0;
return FALSE;
}
m_nCmdLastID = m_nCmdFirstID + HRESULT_CODE(hr) - 1;
return TRUE;
}
BOOL CShellContextMenu::HandleMenuMessage(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pLResult)
{
if (!IsValid())
return FALSE;
switch (message)
{
case WM_MENUSELECT:
{
// look for a parent frame, to update its status bar
if (m_pOwner == NULL)
return FALSE;
CFrameWnd* pFrame = m_pOwner->GetTopLevelFrame();
if (pFrame == NULL)
return FALSE;
// provide status bar text
*pLResult = 0;
CString sStatusText;
UINT uItem = (UINT) LOWORD(wParam);
UINT fuFlags = (UINT) HIWORD(wParam);
if (fuFlags == 0xFFFF) // menu was closed
return FALSE;
// get the first item of submenu, if popup item
if (fuFlags & MF_POPUP)
uItem = ::GetMenuItemID(::GetSubMenu((HMENU)lParam, uItem), 0);
// check if it's a valid item
if (!(fuFlags & MF_SEPARATOR)
&& (uItem == 0 || !IsCommandInRange(uItem)))
return FALSE;
// get description
if (!(fuFlags & MF_SEPARATOR))
GetCommandDescription(uItem, sStatusText);
// update status text, even if empty
pFrame->SetMessageText(sStatusText);
return TRUE;
}
break;
case WM_INITMENUPOPUP: // initialize sub-menus
case WM_MEASUREITEM: // handle owner-draw items
case WM_DRAWITEM:
{
// prepare result in case we handle the message
// (if we don't, it will be discarded)
*pLResult = TRUE;
if (message == WM_INITMENUPOPUP)
*pLResult = 0;
// check if it's a menu message
if (message == WM_MEASUREITEM
&& ((LPMEASUREITEMSTRUCT)lParam)->CtlType != ODT_MENU)
return FALSE;
if (message == WM_DRAWITEM
&& ((LPDRAWITEMSTRUCT)lParam)->CtlType != ODT_MENU)
return FALSE;
// these messages need interface version 2
SContextMenu2Ptr pCtxMenu2(m_pCtxMenu);
if (!pCtxMenu2.IsValid())
return FALSE;
return (NOERROR == pCtxMenu2->HandleMenuMsg(message, wParam, lParam));
}
}
return FALSE;
}
BOOL CShellContextMenu::GetCommandDescription(UINT nCmdID, CString& rText)
{
if (!IsValid() || !IsCommandInRange(nCmdID))
return FALSE;
// make a buffer large enough to hold either a Unicode or Ansi
// string and initialize it to zeroes, because some shell
// extensions return success even when they don't copy anything
// to the buffer. Conversion is achieved by a CString object.
WCHAR szHelpText[MAX_PATH];
ZeroMemory(szHelpText, sizeof(WCHAR)*MAX_PATH);
// try with Unicode first (it seems Explorer does so)
HRESULT hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
GCS_HELPTEXTW, NULL, (LPSTR)szHelpText, MAX_PATH);
if (*(LPWSTR)szHelpText != 0) // buffer was used
rText = (LPWSTR)szHelpText;
else
{
// otherwise try with Ansi
hr = m_pCtxMenu->GetCommandString(nCmdID - m_nCmdFirstID,
GCS_HELPTEXTA, NULL, (LPSTR)szHelpText, MAX_PATH);
if (*(LPSTR)szHelpText != 0) // buffer was used
rText = (LPSTR)szHelpText;
}
return SUCCEEDED(hr);
}
BOOL CShellContextMenu::Create(IShellFolder *pParentFolder, LPCITEMIDLIST pidl)
{
m_pCtxMenu = SContextMenuPtr(pParentFolder, pidl);
return m_pCtxMenu.IsValid();
}
// a NULL pointer to unsubclass
void CShellContextMenu::SetOwner(CWnd *pOwner)
{
if (m_pOwner != NULL || pOwner == NULL)
UnsubclassOwner();
m_pOwner = pOwner;
if (m_pOwner != NULL)
SubclassOwner();
}
const LPCTSTR SH_CTXMENU_OBJ = _T("ShellContextMenu");
void CShellContextMenu::SubclassOwner()
{
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
m_oldWndProc = (WNDPROC)::GetWindowLong(*m_pOwner, GWL_WNDPROC);
::SetProp(*m_pOwner, SH_CTXMENU_OBJ, (HANDLE)this);
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)WindowProc);
}
LRESULT CALLBACK CShellContextMenu::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CShellContextMenu *pThis =
(CShellContextMenu*)::GetProp(hwnd, SH_CTXMENU_OBJ);
LRESULT lResult;
if (pThis->HandleMenuMessage(message, wParam, lParam, &lResult))
return lResult; // success, no default processing
if (message == WM_DESTROY)
pThis->UnsubclassOwner();
return ::CallWindowProc(pThis->m_oldWndProc, hwnd, message, wParam, lParam);
}
void CShellContextMenu::UnsubclassOwner()
{
ASSERT(::IsWindow(m_pOwner->GetSafeHwnd()));
::RemoveProp(*m_pOwner, SH_CTXMENU_OBJ);
::SetWindowLong(*m_pOwner, GWL_WNDPROC, (LONG)m_oldWndProc);
m_pOwner = NULL;
}
@@ -0,0 +1,102 @@
// ShellContextMenu.h: interface for the CShellContextMenu class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_SHELLCONTEXTMENU_H__B690453B_A97A_4D33_A6A5_0A0BDE5EB1A4__INCLUDED_)
#define AFX_SHELLCONTEXTMENU_H__B690453B_A97A_4D33_A6A5_0A0BDE5EB1A4__INCLUDED_
#include <shlobj.h>
#include "ShellWrappers.h"
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CShellContextMenu
{
protected:
SContextMenuPtr m_pCtxMenu;
UINT m_nCmdFirstID;
UINT m_nCmdLastID;
CWnd* m_pOwner; // NULL means no owner (no subclassing in act)
WNDPROC m_oldWndProc;
public:
BOOL Create(IShellFolder* pParentFolder, LPCITEMIDLIST pidl);
CShellContextMenu();
virtual ~CShellContextMenu();
BOOL InvokeCommand(UINT nCmdID);
BOOL FillMenu(CMenu *pMenu, UINT nStartIndex = 0, UINT nCmdFirstID = 1, UINT nCmdLastID = 0x7FFF, UINT nFlags = 0);
BOOL HandleMenuMessage(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pLResult);
BOOL GetCommandDescription(UINT nCmdID, CString& rText);
BOOL IsValid();
// commands
void GetCommandRange(UINT& nCmdFirstID, UINT& nCmdLastID);
BOOL IsCommandInRange(UINT nCmdID);
// owner
void SetOwner(CWnd *pOwner);
CWnd* GetOwner();
protected:
void SubclassOwner();
void UnsubclassOwner();
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
};
// inline functions
inline CShellContextMenu::CShellContextMenu()
{
m_nCmdFirstID = 0;
m_nCmdLastID = 0;
m_pOwner = NULL;
m_oldWndProc = NULL;
}
inline CShellContextMenu::~CShellContextMenu()
{
if (m_pOwner != NULL)
UnsubclassOwner();
}
inline BOOL CShellContextMenu::IsValid()
{
return (m_pCtxMenu.IsValid());
}
inline void CShellContextMenu::GetCommandRange(UINT &nCmdFirstID, UINT &nCmdLastID)
{
nCmdFirstID = m_nCmdFirstID;
nCmdLastID = m_nCmdLastID;
}
inline BOOL CShellContextMenu::IsCommandInRange(UINT nCmdID)
{
return IsValid() && (nCmdID >= m_nCmdFirstID && nCmdID <= m_nCmdLastID);
}
inline CWnd* CShellContextMenu::GetOwner()
{
return m_pOwner;
}
#endif // !defined(AFX_SHELLCONTEXTMENU_H__B690453B_A97A_4D33_A6A5_0A0BDE5EB1A4__INCLUDED_)
@@ -0,0 +1,231 @@
// ShellPidl.cpp: implementation of the CShellPidl class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <afxpriv.h>
#include "ShellPidl.h"
//////////////////////////////////////////////////////////////////////
// Static Functions
// does not include terminating zero
// NULL objects get a zero length
UINT CShellPidl::ILGetLength(LPCITEMIDLIST pidl)
{
if (pidl == NULL)
return 0;
UINT length = 0, cb;
do
{
cb = pidl->mkid.cb;
pidl = (LPCITEMIDLIST)((LPBYTE)pidl + cb);
length += cb;
}
while (cb != 0);
return length;
}
LPCITEMIDLIST CShellPidl::ILGetNext(LPCITEMIDLIST pidl)
{
if (pidl == NULL)
return NULL;
// Get the size of the specified item identifier.
int cb = pidl->mkid.cb;
// If the size is zero, it is the end of the list.
if (cb == 0)
return NULL;
// Add cb to pidl (casting to increment by bytes).
pidl = (LPCITEMIDLIST)((LPBYTE)pidl + cb);
// Return NULL if we reached the terminator, or a pidl otherwise.
return (pidl->mkid.cb == 0) ? NULL : pidl;
}
LPCITEMIDLIST CShellPidl::ILGetLast(LPCITEMIDLIST pidl)
{
LPCITEMIDLIST pidlLast = pidl, tmp = ILGetNext(pidl);
while (tmp != NULL)
{
pidlLast = tmp;
tmp = ILGetNext(tmp);
}
return pidlLast;
}
LPITEMIDLIST CShellPidl::ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
// check arguments
if (pidl1 == NULL && pidl2 == NULL)
return NULL;
// Get the size of the resulting item identifier list
UINT cb1 = ILGetLength(pidl1);
UINT cb2 = ILGetLength(pidl2);
// Allocate a new item identifier list
SMallocPtr pMalloc;
LPITEMIDLIST pidlNew = (LPITEMIDLIST)pMalloc->Alloc(cb1 + cb2 + sizeof(USHORT));
LPITEMIDLIST pidlEnd = pidlNew;
if (pidlNew != NULL)
{
// Copy the first item identifier list and terminating 0
if (cb1 > 0)
{
CopyMemory(pidlEnd, pidl1, cb1);
pidlEnd = (LPITEMIDLIST)((LPBYTE)pidlEnd + cb1);
}
// Copy the second item identifier list and terminating 0
if (cb2 > 0)
{
CopyMemory(pidlEnd, pidl2, cb2);
pidlEnd = (LPITEMIDLIST)((LPBYTE)pidlEnd + cb2);
}
// Append a terminating zero.
pidlEnd->mkid.cb = 0;
}
return pidlNew;
}
LPITEMIDLIST CShellPidl::ILCloneFirst(LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlNew = NULL;
if (pidl != NULL)
{
// Get the size of the first item identifier.
int cb = pidl->mkid.cb;
// Allocate a new item identifier list.
SMallocPtr pMalloc;
pidlNew = (LPITEMIDLIST)pMalloc->Alloc(cb + sizeof(USHORT));
if (pidlNew != NULL)
{
// Copy the specified item identifier.
CopyMemory(pidlNew, pidl, cb);
// Append a terminating zero.
((LPITEMIDLIST)((LPBYTE)pidlNew + cb))->mkid.cb = 0;
}
}
return pidlNew;
}
LPITEMIDLIST CShellPidl::ILCloneParent(LPCITEMIDLIST pidl)
{
// Get the size of the parent item identifier.
UINT cb = (UINT)ILGetLast(pidl) - (UINT)pidl;
SMallocPtr pMalloc;
LPITEMIDLIST pidlNew = (LPITEMIDLIST)pMalloc->Alloc(cb + sizeof(USHORT));
if (pidlNew != NULL)
{
// Copy the specified item identifier.
CopyMemory(pidlNew, pidl, cb);
// Append a terminating zero.
((LPITEMIDLIST)((LPBYTE)pidlNew + cb))->mkid.cb = 0;
}
return pidlNew;
}
LPITEMIDLIST CShellPidl::ILFromPath(LPCTSTR pszPath, HWND hOwner)
{
USES_CONVERSION;
ULONG count = 0;
LPITEMIDLIST pidlNew = NULL;
SDesktopFolderPtr pDesktopFolder;
pDesktopFolder->ParseDisplayName(hOwner, NULL,
T2OLE(pszPath), &count, &pidlNew, NULL);
return pidlNew;
}
//////////////////////////////////////////////////////////////////////
// Member Functions
// debug
#ifdef _DEBUG
void CShellPidl::Dump(CDumpContext& dc) const
{
dc << "addr = " << (void*)m_pObj << "\n";
if (m_pObj == NULL)
return;
dc << "[structure]\n";
LPCITEMIDLIST pidl = m_pObj;
while (pidl != NULL)
{
dc << " " << pidl->mkid.cb;
pidl = ILGetNext(pidl);
}
dc << "\n";
}
#endif //_DEBUG
int CShellPidl::CompareItemID(LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)
{
if(pidl1 == pidl2)
return 0;
if(!pidl1 || !pidl2)
return -1;
if(GetItemIDSize(pidl1) != GetItemIDSize(pidl2))
return -1;
return memcmp(pidl1,pidl2,GetItemIDSize(pidl1));
}
int CShellPidl::GetItemIDSize(LPCITEMIDLIST pidl)
{
if(!pidl)
return 0;
int nSize = 0;
while(pidl -> mkid.cb)
{
nSize += pidl -> mkid.cb;
pidl = (LPCITEMIDLIST)(((LPBYTE)pidl) + pidl -> mkid.cb);
}
return nSize;
}
LPITEMIDLIST CShellPidl::GetObject()
{
return this -> m_pObj;
}
int CShellPidl::CompareItemID(int nSpecialFolder)
{
CShellPidl compareID(nSpecialFolder);
return CompareItemID(m_pObj, compareID);
}
int CShellPidl::CompareItemID(LPITEMIDLIST cmpid)
{
return CompareItemID(m_pObj, cmpid);
}
@@ -0,0 +1,182 @@
// ShellPidl.h: interface for the CShellPidl class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_SHELLPIDL_H__98F90381_3F3A_4DF6_948D_B9BB960E7A96__INCLUDED_)
#define AFX_SHELLPIDL_H__98F90381_3F3A_4DF6_948D_B9BB960E7A96__INCLUDED_
#include <shlobj.h>
#include "ShellWrappers.h"
#include <shlwapi.h>
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CShellPidl : public TSharedObject<ITEMIDLIST>
{
protected:
static LPCITEMIDLIST ILGetLast(LPCITEMIDLIST pidl);
// manage PIDLs
static UINT ILGetLength(LPCITEMIDLIST pidl);
static LPCITEMIDLIST ILGetNext(LPCITEMIDLIST pidl);
static LPITEMIDLIST ILCombine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
static LPITEMIDLIST ILClone(LPCITEMIDLIST pidl);
static LPITEMIDLIST ILCloneFirst(LPCITEMIDLIST pidl);
static LPITEMIDLIST ILCloneParent(LPCITEMIDLIST pidl);
static LPITEMIDLIST ILFromPath(LPCTSTR pszPath, HWND hOwner = NULL);
public:
int CompareItemID(LPITEMIDLIST);
int CompareItemID(int);
LPITEMIDLIST GetObject();
int CompareItemID(LPCITEMIDLIST,LPCITEMIDLIST);
CShellPidl(LPCTSTR pszPath, HWND hOwner = NULL);
CShellPidl(UINT nSpecialFolder, HWND hOwner = NULL);
CShellPidl(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRel);
CShellPidl(LPCITEMIDLIST pidl);
CShellPidl();
virtual ~CShellPidl();
int GetIconIndex(UINT uFlags = SHGFI_SMALLICON) const;
BOOL IsRoot() const;
void Combine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
void CloneLastParent(LPCITEMIDLIST pidl);
void CloneLastChild(LPCITEMIDLIST pidl);
void CloneFirstParent(LPCITEMIDLIST pidl);
void CloneFirstChild(LPCITEMIDLIST pidl);
LPCITEMIDLIST GetFirstChild();
LPCITEMIDLIST GetLastChild();
CString GetPath() const;
// debug support
#ifdef _DEBUG
void Dump(CDumpContext& dc) const;
#endif //_DEBUG
private:
int GetItemIDSize(LPCITEMIDLIST);
};
//////////////////////////////////////////////////////////////////////
// Inline Member Functions
inline CShellPidl::CShellPidl()
{
}
inline CShellPidl::~CShellPidl()
{
}
inline CShellPidl::CShellPidl(LPCITEMIDLIST pidl)
{
m_pObj = ILClone(pidl);
}
inline CShellPidl::CShellPidl(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRel)
{
m_pObj = ILCombine(pidlParent, pidlRel);
}
inline CShellPidl::CShellPidl(UINT nSpecialFolder, HWND hOwner)
{
SHGetSpecialFolderLocation(hOwner, nSpecialFolder, &m_pObj);
}
inline CShellPidl::CShellPidl(LPCTSTR pszPath, HWND hOwner)
{
m_pObj = ILFromPath(pszPath, hOwner);
}
inline BOOL CShellPidl::IsRoot() const
{
return (m_pObj != NULL) && (m_pObj->mkid.cb == 0);
}
inline CString CShellPidl::GetPath() const
{
CString path;
BOOL bSuccess = SHGetPathFromIDList(m_pObj, path.GetBuffer(MAX_PATH));
path.ReleaseBuffer();
if (!bSuccess)
path.Empty();
return path;
}
inline int CShellPidl::GetIconIndex(UINT uFlags) const
{
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
uFlags |= SHGFI_PIDL | SHGFI_SYSICONINDEX;
SHGetFileInfo((LPCTSTR)m_pObj, 0, &sfi, sizeof(SHFILEINFO), uFlags);
return sfi.iIcon;
}
inline void CShellPidl::Combine(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
Destroy();
Attach(ILCombine(pidl1, pidl2));
}
// get the first ancestor
inline void CShellPidl::CloneFirstParent(LPCITEMIDLIST pidl)
{
Destroy();
Attach(ILCloneFirst(pidl));
}
inline void CShellPidl::CloneFirstChild(LPCITEMIDLIST pidl)
{
Destroy();
Attach(ILClone(ILGetNext(pidl)));
}
inline LPCITEMIDLIST CShellPidl::GetFirstChild()
{
return ILGetNext(m_pObj);
}
// get the immediate parent
inline void CShellPidl::CloneLastParent(LPCITEMIDLIST pidl)
{
Destroy();
Attach(ILCloneParent(pidl));
}
inline void CShellPidl::CloneLastChild(LPCITEMIDLIST pidl)
{
Destroy();
Attach(ILClone(ILGetLast(pidl)));
}
inline LPCITEMIDLIST CShellPidl::GetLastChild()
{
return ILGetLast(m_pObj);
}
//////////////////////////////////////////////////////////////////////
// Inline Static Functions
inline LPITEMIDLIST CShellPidl::ILClone(LPCITEMIDLIST pidl)
{
return ILCombine(NULL, pidl);
}
#endif // !defined(AFX_SHELLPIDL_H__98F90381_3F3A_4DF6_948D_B9BB960E7A96__INCLUDED_)
@@ -0,0 +1,62 @@
// ShellString.cpp: implementation of the CShellString class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShellString.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
void CShellString::Destroy()
{
SMallocPtr pMalloc;
if (m_str.uType == STRRET_WSTR && m_str.pOleStr != NULL)
{
pMalloc->Free(m_str.pOleStr);
m_str.pOleStr = NULL;
}
ZeroMemory(&m_str, sizeof(STRRET));
}
STRRET* CShellString::GetPointer(LPCITEMIDLIST pidl, UINT uDesiredType)
{
// reset the object
Destroy();
// init object
m_pidl = pidl;
ASSERT(m_pidl.IsValid());
m_str.uType = uDesiredType;
return &m_str;
}
CShellString::operator CString() const
{
CString string;
switch (m_str.uType)
{
case STRRET_WSTR:
string = m_str.pOleStr;
break;
case STRRET_CSTR:
string = m_str.cStr;
break;
case STRRET_OFFSET:
string = ((LPBYTE)(LPCITEMIDLIST)m_pidl + m_str.uOffset);
}
return string;
}
@@ -0,0 +1,60 @@
// ShellString.h: interface for the CShellString class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_SHELLSTRING_H__1E9F6E1A_5076_4264_8747_713C53BAD6FE__INCLUDED_)
#define AFX_SHELLSTRING_H__1E9F6E1A_5076_4264_8747_713C53BAD6FE__INCLUDED_
#include <shlobj.h>
#include "ShellWrappers.h"
#include "ShellPidl.h"
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CShellString
{
protected:
CShellPidl m_pidl;
STRRET m_str;
public:
CShellString();
virtual ~CShellString();
// obtain an empty object by pointer
STRRET* GetPointer(LPCITEMIDLIST pidl, UINT uDesiredType = STRRET_WSTR);
operator CString() const;
protected:
void Destroy();
};
// inline functions
inline CShellString::CShellString()
{
ZeroMemory(&m_str, sizeof(STRRET));
}
inline CShellString::~CShellString()
{
Destroy();
}
#endif // !defined(AFX_SHELLSTRING_H__1E9F6E1A_5076_4264_8747_713C53BAD6FE__INCLUDED_)
@@ -0,0 +1,414 @@
// ShellTreeCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ShellTreeCtrl.h"
#include ".\shelltreectrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CShellTreeCtrl
CShellTreeCtrl::CShellTreeCtrl()
{
m_nCallbackMask = 0;
}
CShellTreeCtrl::~CShellTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CShellTreeCtrl, CWaitingTreeCtrl)
//{{AFX_MSG_MAP(CShellTreeCtrl)
ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnDeleteItem)
ON_NOTIFY_REFLECT(TVN_GETDISPINFO, OnGetDispInfo)
//}}AFX_MSG_MAP
// ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnTvnSelchanged)
// ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnTvnItemexpanded)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CShellTreeCtrl message handlers
BOOL CShellTreeCtrl::PopulateItem(HTREEITEM hParent)
{
if (hParent == TVI_ROOT)
{
// not handled yet, do nothing in Release builds
ASSERT(FALSE);
return TRUE;
}
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hParent);
if (pData == NULL)
return TRUE; // invalid shell item, ignore it silently
// get parent pidl
ASSERT(pData->IsValid());
CShellPidl& pidlParent = pData->pidlAbs;
if (!EnumFolderItems(hParent, pidlParent, pData->nFlags))
return TRUE; // failed, won't try anymore!
// TODO: change this method!!
// do not check for children if parent is a removable media
// (just try: if it's a filesystem object, it has a path)
TCHAR path[MAX_PATH];
if (SHGetPathFromIDList(pidlParent, path))
{
path[3] = 0;
UINT type = GetDriveType(path);
if (type != DRIVE_FIXED)
return FALSE;
}
return TRUE;
}
void CShellTreeCtrl::PreSubclassWindow()
{
InitializeControl();
CWaitingTreeCtrl::PreSubclassWindow();
}
void CShellTreeCtrl::InitializeControl()
{
// Attach to the system image list
CShellPidl pidl((UINT)CSIDL_DESKTOP, m_hWnd);
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
HIMAGELIST hSysImageList = (HIMAGELIST) SHGetFileInfo((LPCTSTR)(LPCITEMIDLIST)pidl,
0, &sfi, sizeof(SHFILEINFO), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
//TreeView_SetImageList(m_hWnd, hSysImageList, TVSIL_NORMAL);
// postpone imagelist attaching
// (seems it doesn't like a sendmessage when dynamically created
// maybe because it has not received the WM_CREATE message yet?)
PostMessage(TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hSysImageList);
}
void CShellTreeCtrl::AddRootFolderContent(LPCITEMIDLIST pidlFolder, UINT nFlags)
{
if (pidlFolder == NULL)
{
CShellPidl pidl((UINT)CSIDL_DESKTOP, m_hWnd);
InsertSubItem(TVI_ROOT, m_pDesktopFolder, NULL, pidl, nFlags);
//HTREEITEM root = GetRootItem();
//Expand(root, TVE_EXPAND);
//AfxMessageBox(GetItemText(GetChildItem(root)));
//PopulateItem(GetRootItem());
//Expand(GetRootItem(), 1);
return;
}
SetRedraw(FALSE);
EnumFolderItems(TVI_ROOT, pidlFolder, nFlags);
SetRedraw(TRUE);
}
int CALLBACK CShellTreeCtrl::CompareFunc(LPARAM lParam1,
LPARAM lParam2, LPARAM /*lParamSort*/)
{
TVITEMDATA* pData1 = (TVITEMDATA*)lParam1;
TVITEMDATA* pData2 = (TVITEMDATA*)lParam2;
ASSERT(pData1->IsValid() && pData2->IsValid());
// TODO: parent folders should be checked some day
SShellFolderPtr pParentFolder = pData2->pParentFolder;
HRESULT hr = pParentFolder->CompareIDs(0,
pData1->pidlAbs.GetLastChild(),
pData2->pidlAbs.GetLastChild() );
if (FAILED(hr))
return 0; // error, don't sort
short ret = (short)HRESULT_CODE(hr);
if (ret < 0)
return -1;
if (ret > 0)
return 1;
return 0;
}
void CShellTreeCtrl::FillItem(TVITEM& item)
{
DWORD dwAttributes;
// get item data
TVITEMDATA* pData = (TVITEMDATA*)item.lParam;
ASSERT(pData->IsValid());
// get a relative pidl
LPCITEMIDLIST pidlRel = pData->pidlAbs.GetLastChild();
if (item.mask & TVIF_TEXT)
{
// get display name
CString sName;
CShellString str;
if (pData->nFlags & STCF_SHOWPATH)
{
// use an absolute or relative path, if possible
sName = pData->pidlAbs.GetPath();
if (!sName.IsEmpty() && !(pData->nFlags & STCF_SHOWFULLNAME))
sName = sName.Right(sName.ReverseFind(_T('\\')));
}
if (sName.IsEmpty())
{
// use a global or contextual displayname
DWORD uDisplayFlags = SHGDN_INFOLDER;
if (pData->nFlags & STCF_SHOWFULLNAME)
uDisplayFlags = SHGDN_NORMAL;
// pData->pParentFolder->GetDisplayNameOf(pidlRel, uDisplayFlags
// | SHGDN_INCLUDE_NONFILESYS, str.GetPointer(pidlRel));
pData->pParentFolder->GetDisplayNameOf(pidlRel, uDisplayFlags, str.GetPointer(pidlRel));
sName = str; // copy to string
}
// set item text
lstrcpyn(item.pszText, (LPCTSTR)sName, item.cchTextMax);
}
if (item.mask & (TVIF_IMAGE | TVIF_SELECTEDIMAGE))
{
// get some attributes
dwAttributes = SFGAO_FOLDER | SFGAO_LINK | SFGAO_SHARE | SFGAO_GHOSTED;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
// set correct icon
if (dwAttributes & SFGAO_GHOSTED)
{
item.mask |= LVIF_STATE;
item.stateMask |= LVIS_CUT;
item.state |= LVIS_CUT;
}
if (dwAttributes & SFGAO_SHARE)
{
item.mask |= LVIF_STATE;
item.state &= ~LVIS_OVERLAYMASK;
item.state |= INDEXTOOVERLAYMASK(1);
item.stateMask |= LVIS_OVERLAYMASK;
}
else if (dwAttributes & SFGAO_LINK)
{
item.mask |= LVIF_STATE;
item.state &= ~LVIS_OVERLAYMASK;
item.state |= INDEXTOOVERLAYMASK(2);
item.stateMask |= LVIS_OVERLAYMASK;
}
if (item.mask & TVIF_IMAGE)
{
item.iImage = pData->pidlAbs.GetIconIndex(SHGFI_SMALLICON);
item.iSelectedImage = item.iImage;
}
if ((item.mask & TVIF_SELECTEDIMAGE)
&& (dwAttributes & SFGAO_FOLDER))
{
item.iSelectedImage = pData->pidlAbs.GetIconIndex(SHGFI_SMALLICON
|SHGFI_OPENICON);
}
}
if (item.mask & TVIF_CHILDREN)
{
// get some attributes
dwAttributes = SFGAO_FOLDER;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
// get children
item.cChildren = 0;
if (dwAttributes & SFGAO_FOLDER)
{
if (pData->nFlags & STCF_INCLUDEFILES)
item.cChildren = 1;
else if (dwAttributes & SFGAO_REMOVABLE)
item.cChildren = 1;
else
{
dwAttributes = SFGAO_HASSUBFOLDER;
pData->pParentFolder->GetAttributesOf(1, &pidlRel, &dwAttributes);
item.cChildren = (dwAttributes & SFGAO_HASSUBFOLDER) ? 1 : 0;
}
}
}
}
void CShellTreeCtrl::OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult)
{
TVITEM& item = ((LPNMTREEVIEW)pNMHDR)->itemOld;
// free item data, ignore invalid shell items
if (item.lParam != 0)
delete (TVITEMDATA*)item.lParam;
*pResult = 0;
}
void CShellTreeCtrl::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
TVITEM& item = ((LPNMTVDISPINFO)pNMHDR)->item;
// use the provided buffer for text
FillItem(item);
*pResult = 0;
}
CShellPidl CShellTreeCtrl::GetItemIDList(HTREEITEM hItem)
{
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hItem);
if (pData != NULL)
{
ASSERT(pData->IsValid());
return pData->pidlAbs;
}
return CShellPidl(); // invalid pidl
}
void CShellTreeCtrl::InsertSubItem(HTREEITEM hParent, LPSHELLFOLDER pParentFolder, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, UINT nFlags)
{
TVINSERTSTRUCT tvis;
ZeroMemory(&tvis, sizeof(TVINSERTSTRUCT));
tvis.hParent = hParent;
tvis.hInsertAfter = TVI_LAST;
// provide a buffer for the item text
TCHAR szText[MAX_PATH];
tvis.item.pszText = szText;
tvis.item.cchTextMax = MAX_PATH;
// used fields
const UINT nTVIFlags = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE
| TVIF_CHILDREN | TVIF_PARAM;
// prepare item data
TVITEMDATA* pData = new TVITEMDATA;
pData->pidlAbs.Combine(pidlParent, pidl);
pData->pParentFolder = pParentFolder;
pData->nFlags = nFlags;
// set item data
ASSERT(pData->IsValid());
tvis.item.lParam = (LPARAM)pData;
// fill with pidl, text, icons and children - handle callbacks
tvis.item.mask = nTVIFlags & ~m_nCallbackMask;
FillItem(tvis.item);
if (m_nCallbackMask & TVIF_IMAGE)
tvis.item.iImage = I_IMAGECALLBACK;
if (m_nCallbackMask & TVIF_SELECTEDIMAGE)
tvis.item.iSelectedImage = I_IMAGECALLBACK;
if (m_nCallbackMask & TVIF_TEXT)
tvis.item.pszText = LPSTR_TEXTCALLBACK;
if (m_nCallbackMask & TVIF_CHILDREN)
tvis.item.cChildren = I_CHILDRENCALLBACK;
tvis.item.mask |= nTVIFlags;
// then insert new item
InsertItem(&tvis);
}
void CShellTreeCtrl::AddRootItem(LPCITEMIDLIST pidlRoot, UINT nFlags)
{
// not needed if pParentFolder is an argument
CShellPidl pidlParent;
pidlParent.CloneLastParent(pidlRoot);
SShellFolderPtr pParentFolder(m_pDesktopFolder, pidlParent);
InsertSubItem(TVI_ROOT, pParentFolder, NULL, pidlRoot, nFlags);
}
BOOL CShellTreeCtrl::EnumFolderItems(HTREEITEM hParent, LPCITEMIDLIST pidlParent, UINT nFlags)
{
// get parent shell folder
SShellFolderPtr pParentFolder(m_pDesktopFolder, pidlParent);
// not a valid folder object
if (!pParentFolder.IsValid())
return FALSE; // failed!
// enum child pidls
SEnumIDListPtr pEnumIDList(pParentFolder, SHCONTF_FOLDERS
| ((nFlags & STCF_INCLUDEFILES) ? SHCONTF_NONFOLDERS : 0)
| ((nFlags & STCF_INCLUDEHIDDEN) ? SHCONTF_INCLUDEHIDDEN : 0), m_hWnd);
if (pEnumIDList.IsValid())
{
SetPopulationCount(0);
CShellPidl pidl;
while (NOERROR == pEnumIDList->Next(1, pidl.GetPointer(), NULL))
{
// add child item, inherit some flags (inclusion)
InsertSubItem(hParent, pParentFolder, pidlParent, pidl,
nFlags & STCF_INCLUDEMASK);
// notify progress
IncreasePopulation();
}
}
if (GetPopulationCount() > 0)
{
// sort items
TVSORTCB tvscb;
tvscb.hParent = hParent;
tvscb.lpfnCompare = CompareFunc;
// tvscb.lParam = 0; // not meaningful yet
SortChildrenCB(&tvscb);
}
// notify progress
SetPopulationCount(1,1);
// success!
return TRUE;
}
BOOL CShellTreeCtrl::GetItemContextMenu(HTREEITEM hItem, CShellContextMenu& rCtxMenu)
{
TVITEMDATA* pData = (TVITEMDATA*)GetItemData(hItem);
if (!pData->IsValid())
return FALSE;
return rCtxMenu.Create(pData->pParentFolder,
pData->pidlAbs.GetLastChild());
}
void CShellTreeCtrl::SetCallbackMask(UINT nMask)
{
m_nCallbackMask = nMask &
(TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN);
}
UINT CShellTreeCtrl::GetCallbackMask()
{
return m_nCallbackMask;
}
@@ -0,0 +1,117 @@
#if !defined(AFX_SHELLTREECTRL_H__98BDBB7B_E2C3_4145_A5D5_693274C6B99B__INCLUDED_)
#define AFX_SHELLTREECTRL_H__98BDBB7B_E2C3_4145_A5D5_693274C6B99B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// ShellTreeCtrl.h : header file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "WaitingTreeCtrl.h"
#include "ShellPidl.h"
#include "ShellString.h"
#include "ShellContextMenu.h"
/////////////////////////////////////////////////////////////////////////////
// CShellTreeCtrl window
// item flags
#define STCF_DEFAULT 0x00
#define STCF_INCLUDEFILES 0x01
#define STCF_INCLUDEHIDDEN 0x02
#define STCF_INCLUDEALL (STCF_INCLUDEFILES|STCF_INCLUDEHIDDEN)
#define STCF_INCLUDEMASK 0x0F
#define STCF_SHOWFULLNAME 0x10
#define STCF_SHOWPATH 0x20
#define STCF_SHOWFULLPATH (STCF_SHOWPATH|STCF_SHOWFULLNAME)
#define STCF_SHOWMASK 0xF0
//#define STCF_DEFERLINKS 0x100
class CShellTreeCtrl : public CWaitingTreeCtrl
{
private:
struct TVITEMDATA
{
SShellFolderPtr pParentFolder;
CShellPidl pidlAbs;
UINT nFlags;
BOOL IsValid()
{
return (this != NULL)
&& pParentFolder.IsValid() && pidlAbs.IsValid();
}
};
SDesktopFolderPtr m_pDesktopFolder;
UINT m_nCallbackMask;
private:
// generic
void InitializeControl();
BOOL EnumFolderItems(HTREEITEM hParent, LPCITEMIDLIST pidlParent, UINT nFlags);
void InsertSubItem(HTREEITEM hParent, LPSHELLFOLDER pParentFolder, LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, UINT nFlags);
void FillItem(TVITEM& item);
static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
protected:
virtual BOOL PopulateItem(HTREEITEM hParent);
// Construction
public:
CShellTreeCtrl();
virtual ~CShellTreeCtrl();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CShellTreeCtrl)
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
// Implementation
public:
UINT GetCallbackMask();
void SetCallbackMask(UINT nMask);
void AddRootItem(LPCITEMIDLIST pidl, UINT nFlags = STCF_DEFAULT);
void AddRootFolderContent(LPCITEMIDLIST pidl, UINT nFlags = STCF_DEFAULT);
CShellPidl GetItemIDList(HTREEITEM hItem);
BOOL GetItemContextMenu(HTREEITEM hItem, CShellContextMenu &rCtxMenu);
// Generated message map functions
protected:
//{{AFX_MSG(CShellTreeCtrl)
afx_msg void OnDeleteItem(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_SHELLTREECTRL_H__98BDBB7B_E2C3_4145_A5D5_693274C6B99B__INCLUDED_)
@@ -0,0 +1,217 @@
// SmartInterfacePtr.h: interface for the TInterfacePtr class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_SHELLWRAPPERS_H__B0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_)
#define AFX_SHELLWRAPPERS_H__B0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_
#include <shlobj.h>
#include "SmartInterfacePtr.h"
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class SMallocPtr : public TStaticInterfacePtr<IMalloc>
{
public:
SMallocPtr()
{
if (m_pIface == NULL)
SHGetMalloc(&m_pIface);
else
m_pIface->AddRef();
}
};
class SDesktopFolderPtr : public TStaticInterfacePtr<IShellFolder>
{
public:
SDesktopFolderPtr()
{
if (m_pIface == NULL)
SHGetDesktopFolder(&m_pIface);
else
m_pIface->AddRef();
}
};
class SShellFolderPtr : public TInterfacePtr<IShellFolder>
{
public:
SShellFolderPtr() {}
SShellFolderPtr(IShellFolder* pFolder) { Copy(pFolder); }
SShellFolderPtr(IShellFolder* pParentFolder, LPCITEMIDLIST pidlRel)
{
if (pidlRel != NULL && pidlRel->mkid.cb == 0)
{
// it's a root element, copy the parent folder
Copy(pParentFolder);
}
else
{
pParentFolder->BindToObject(pidlRel,
NULL, IID_IShellFolder, (LPVOID*)&m_pIface);
}
}
SShellFolderPtr(LPCITEMIDLIST pidlAbs)
{
SDesktopFolderPtr pDesktopFolder;
if (pidlAbs != NULL && pidlAbs->mkid.cb == 0)
{
// it's a root element, copy the parent folder
Copy(pDesktopFolder);
}
else
{
pDesktopFolder->BindToObject(pidlAbs,
NULL, IID_IShellFolder, (LPVOID*)&m_pIface);
}
}
};
class SEnumIDListPtr : public TInterfacePtr<IEnumIDList>
{
public:
SEnumIDListPtr(IShellFolder* pParentFolder, UINT nFlags, HWND hOwner = NULL)
{
pParentFolder->EnumObjects(hOwner, nFlags, &m_pIface);
}
};
class SContextMenuPtr : public TInterfacePtr<IContextMenu>
{
public:
SContextMenuPtr() {}
SContextMenuPtr(IShellFolder* pParentFolder, LPCITEMIDLIST pidlRel, HWND hOwner = NULL)
{
pParentFolder->GetUIObjectOf(hOwner, 1, &pidlRel,
IID_IContextMenu, NULL, (LPVOID*)&m_pIface);
}
};
class SContextMenu2Ptr : public TInterfacePtr<IContextMenu2>
{
public:
SContextMenu2Ptr(IContextMenu* pIface)
{
if (pIface != NULL)
pIface->QueryInterface(IID_IContextMenu2, (LPVOID*)&m_pIface);
}
};
// templates
template <class TYPE>
class TSharedObject
{
public:
~TSharedObject()
{ Destroy(); }
protected:
TYPE* m_pObj;
TSharedObject()
{ m_pObj = NULL; }
TSharedObject(const TSharedObject& shobj)
{ Copy(shobj); }
void Copy(const TSharedObject& shobj);
public:
operator const TYPE * () const
{ return m_pObj; }
const TSharedObject& operator = (const TSharedObject& shobj)
{
Destroy();
Copy(shobj);
return *this;
}
BOOL IsValid() const
{ return m_pObj != NULL; }
void Attach(TYPE* pObj)
{
ASSERT(m_pObj == NULL); // can't attach two times!
m_pObj = pObj;
}
TYPE* Detach()
{
TYPE* ret = m_pObj;
m_pObj = NULL;
return ret;
}
// obtain a void object by pointer
TYPE** GetPointer()
{
Destroy();
return &m_pObj;
}
#ifdef _DEBUG
void DumpRaw(CDumpContext& dc) const
{
dc << "addr = " << (void*)m_pObj << "\n";
if (m_pObj == NULL)
return;
SMallocPtr pMalloc;
ULONG size = pMalloc->GetSize((void*)m_pObj);
cd << "[hex dump]\n";
dc.HexDump(".", (BYTE*)m_pObj, (int)size, 16);
dc << "\n";
}
#endif //_DEBUG
protected:
void Destroy()
{
SMallocPtr pMalloc;
pMalloc->Free(Detach());
}
};
template <class TYPE>
void TSharedObject<TYPE>::Copy(const TSharedObject& shobj)
{
m_pObj = NULL;
if (shobj.m_pObj == NULL)
return;
// obj to copy must have been allocated this way
SMallocPtr pMalloc;
ULONG size = (ULONG)pMalloc->GetSize((void*)shobj.m_pObj);
ASSERT(size > 0);
if (shobj.m_pObj != NULL)
{
m_pObj = (TYPE*)pMalloc->Alloc(size);
CopyMemory(m_pObj, shobj.m_pObj, size);
}
}
#endif //!defined(AFX_SHELLWRAPPERS_H__B0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_)
@@ -0,0 +1,126 @@
// SmartInterfacePtr.h: interface for the TInterfacePtr class.
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_SMARTINTERFACEPTR_H__A0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_)
#define AFX_SMARTINTERFACEPTR_H__A0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
template <class TYPE>
class TInterfacePtr
{
public:
~TInterfacePtr()
{ Free(); }
BOOL IsValid() const
{ return m_pIface != NULL; }
TYPE * operator -> ()
{ return m_pIface; }
const TYPE * operator -> () const
{ return m_pIface; }
operator TYPE * ()
{ return m_pIface; }
protected:
TYPE * m_pIface;
// must derive from this class
TInterfacePtr()
{ m_pIface = NULL; }
TInterfacePtr(const TInterfacePtr& obj)
{ Copy(obj.m_pIface); }
const TInterfacePtr& operator = (const TInterfacePtr& obj)
{
Free();
Copy(obj.m_pIface);
return *this;
}
void Copy(TYPE * pIface);
void Free();
};
template <class TYPE>
inline void TInterfacePtr<TYPE>::Copy(TYPE * pIface)
{
m_pIface = pIface;
if (m_pIface != NULL)
m_pIface->AddRef();
}
template <class TYPE>
inline void TInterfacePtr<TYPE>::Free()
{
if (m_pIface != NULL)
{
m_pIface->Release();
m_pIface = NULL;
}
}
// static storage
template <class TYPE>
class TStaticInterfacePtr
{
public:
~TStaticInterfacePtr()
{ Free(); }
BOOL IsValid() const
{ return m_pIface != NULL; }
TYPE * operator -> ()
{ return m_pIface; }
const TYPE * operator -> () const
{ return m_pIface; }
operator TYPE * ()
{ return m_pIface; }
protected:
static TYPE * m_pIface;
TStaticInterfacePtr() {}
// cannot copy - use another instance
TStaticInterfacePtr(const TStaticInterfacePtr&) {}
const TStaticInterfacePtr& operator = (const TStaticInterfacePtr&) {}
void Free();
};
template <class TYPE>
TYPE * TStaticInterfacePtr<TYPE>::m_pIface = NULL;
template <class TYPE>
inline void TStaticInterfacePtr<TYPE>::Free()
{
if (m_pIface != NULL)
if (0 == m_pIface->Release())
m_pIface = NULL;
}
#endif // !defined(AFX_SMARTINTERFACEPTR_H__A0E1BBE7_A5D8_4A80_9A36_ED5553FE74F6__INCLUDED_)
@@ -0,0 +1,450 @@
// WaitingTreeCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "WaitingTreeCtrl.h"
#pragma warning(push)
#pragma warning(disable: 4201)
#include <mmsystem.h>
#pragma warning(pop)
#pragma comment(lib, "winmm.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl
CWaitingTreeCtrl::CWaitingTreeCtrl()
{
m_bDrawSnapshot = FALSE;
m_sWaitMsg = _T("Loading...");
m_bShowWaitMsg = FALSE;
m_hIconMsg = NULL; // default: blank icon
m_nTimerDelay = 0; // default: no timer
m_hRedrawEvent = NULL;
m_hTimerEvent = NULL;
m_hThread = NULL;
}
CWaitingTreeCtrl::~CWaitingTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CWaitingTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CWaitingTreeCtrl)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemExpanded)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl message handlers
void CWaitingTreeCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (pNMTreeView->action & TVE_EXPAND)
PreExpandItem(pNMTreeView->itemNew.hItem);
*pResult = 0;
}
void CWaitingTreeCtrl::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (pNMTreeView->action & TVE_EXPAND)
ExpandItem(pNMTreeView->itemNew.hItem);
else if (pNMTreeView->action & TVE_COLLAPSE)
{
if (WantsRefresh(pNMTreeView->itemNew.hItem))
{
// delete child items
DeleteChildren(pNMTreeView->itemNew.hItem);
}
}
*pResult = 0;
}
void CWaitingTreeCtrl::PreAnimation(HTREEITEM hItemMsg)
{
UNREFERENCED_PARAMETER(hItemMsg);
}
void CWaitingTreeCtrl::PostAnimation()
{
}
void CWaitingTreeCtrl::DoAnimation(BOOL bTimerEvent, int iMaxSteps, int iStep)
{
UNREFERENCED_PARAMETER(bTimerEvent);
UNREFERENCED_PARAMETER(iMaxSteps);
UNREFERENCED_PARAMETER(iStep);
}
int CWaitingTreeCtrl::GetPopulationCount(int *piMaxSubItems)
{
if (piMaxSubItems != NULL)
*piMaxSubItems = m_iItemCount;
return m_iItemIndex;
}
void CWaitingTreeCtrl::SetPopulationCount(int iMaxSubItems, int iFirstSubItem)
{
m_iItemCount = iMaxSubItems;
m_iItemIndex = iFirstSubItem;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::UpdatePopulation(int iSubItems)
{
m_iItemIndex = iSubItems;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::IncreasePopulation(int iSubItemsToAdd)
{
m_iItemIndex += iSubItemsToAdd;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::SetAnimationDelay(UINT nMilliseconds)
{
// if greater than zero, periodic DoAnimation() will be called
m_nTimerDelay = nMilliseconds;
}
DWORD WINAPI CWaitingTreeCtrl::AnimationThreadProc(LPVOID pThis)
{
CWaitingTreeCtrl* me = (CWaitingTreeCtrl*)pThis;
HANDLE events[2] = { me->m_hTimerEvent, me->m_hRedrawEvent };
while (!me->m_bAbortAnimation)
{
DWORD wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
if (me->m_bAbortAnimation || wait == WAIT_FAILED)
break;
if (wait == WAIT_OBJECT_0) // timer event
me->DoAnimation(TRUE, me->m_iItemCount, me->m_iItemIndex);
else // redraw event
me->DoAnimation(FALSE, me->m_iItemCount, me->m_iItemIndex);
}
return 0;
}
void CWaitingTreeCtrl::StartAnimation()
{
// user-defined setup
PreAnimation(m_hItemMsg);
// animation can go
m_bAbortAnimation = FALSE;
// automatic reset events, signaled
m_hTimerEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
m_hRedrawEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
// start animation thread
DWORD dwThreadID = 0;
m_hThread = CreateThread(NULL, 0, AnimationThreadProc, this,
THREAD_PRIORITY_HIGHEST, &dwThreadID);
// setup timer, if specified
if (m_nTimerDelay > 0)
m_nTimerID = (UINT)timeSetEvent(m_nTimerDelay, 5, (LPTIMECALLBACK)m_hTimerEvent,
0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
}
void CWaitingTreeCtrl::StopAnimation()
{
// stop and destroy timer
timeKillEvent(m_nTimerID);
// signal thread to terminate
m_bAbortAnimation = TRUE;
SetEvent(m_hRedrawEvent); // make sure it can see the signal
// wait thread termination
WaitForSingleObject(m_hThread, INFINITE);
// clean up
CloseHandle(m_hTimerEvent);
m_hTimerEvent = NULL;
CloseHandle(m_hRedrawEvent);
m_hRedrawEvent = NULL;
CloseHandle(m_hThread);
m_hThread = NULL;
// user-defined cleanup
PostAnimation();
}
void CWaitingTreeCtrl::PopulateRoot()
{
PreExpandItem(TVI_ROOT);
ExpandItem(TVI_ROOT);
// force update, don't scroll
SetRedraw(FALSE);
SCROLLINFO si;
GetScrollInfo(SB_HORZ, &si);
EnsureVisible(GetChildItem(TVI_ROOT));
SetScrollInfo(SB_HORZ, &si, FALSE);
SetRedraw();
}
void CWaitingTreeCtrl::PreExpandItem(HTREEITEM hItem)
{
if (!NeedsChildren(hItem))
{
if (WantsRefresh(hItem))
{
// delete child items before populating
DeleteChildren(hItem);
}
else
{
// doesn't want new items
m_hItemToPopulate = NULL;
return;
}
}
// if it wants new child items, go on
m_hItemToPopulate = hItem;
// fix redraw when expanded programatically
UpdateWindow();
// hide changes until it's expanded
SetRedraw(FALSE);
// add wait msg, to allow item expansion
m_hItemMsg = InsertItem(m_sWaitMsg, m_hItemToPopulate);
// zero progress
m_iItemCount = 1;
m_iItemIndex = 0;
}
void CWaitingTreeCtrl::ExpandItem(HTREEITEM hItem)
{
if (m_hItemToPopulate == NULL)
return; // just expand, doesn't want new items
ASSERT(hItem == m_hItemToPopulate); // should never fail!!!
if (m_bShowWaitMsg)
{
// display wait msg now, make sure it's visible
SetRedraw();
EnsureVisible(m_hItemMsg);
UpdateWindow();
}
// setup animation thread, call PreAnimation
StartAnimation();
// draw icon
if (m_bShowWaitMsg)
DrawUserIcon();
// delay redraw after populating
SetRedraw(FALSE);
// take a snapshot of the background
TakeSnapshot();
// del temporary item (wait msg still shown)
DeleteItem(m_hItemMsg);
// fill in with sub items
BOOL bCheckChildren = PopulateItem(hItem);
// clean up animation thread, call PostAnimation
StopAnimation();
// change parent to reflect current children number
if (hItem != TVI_ROOT)
{
TVITEM item;
item.hItem = hItem;
item.mask = TVIF_HANDLE | TVIF_CHILDREN;
item.cChildren = NeedsChildren(hItem) ? 0 : 1;
if (bCheckChildren)
SetItem(&item);
else if (item.cChildren == 0)
// restore item's plus button if no children inserted
SetItemState(hItem, 0, TVIS_EXPANDED);
}
// clean up snapshot
DestroySnapshot();
// redraw now
SetRedraw(TRUE);
// scroll like in a standard expansion
HTREEITEM hChild = GetChildItem(hItem);
while (hChild != NULL && GetFirstVisibleItem() != hItem)
{
// EnsureVisible(hChild);
hChild = GetNextSiblingItem(hChild);
}
}
BOOL CWaitingTreeCtrl::WantsRefresh(HTREEITEM hItem)
{
UNREFERENCED_PARAMETER(hItem);
// default implementation, no refresh
return FALSE;
}
BOOL CWaitingTreeCtrl::GetItemImageRect(HTREEITEM hItem, LPRECT pRect)
{
if (GetImageList(TVSIL_NORMAL) == NULL)
return FALSE; // no images
CRect rc;
// get item rect
if (!GetItemRect(hItem, &rc, TRUE))
return FALSE;
int cx = GetSystemMetrics(SM_CXSMICON);
int cy = GetSystemMetrics(SM_CYSMICON);
// move onto the icon space
int margin = (rc.Height()-cy)/2;
rc.OffsetRect(-cx-3 , margin);
rc.right = rc.left + cx; // make it square
rc.bottom = rc.top + cy; // make it square
*pRect = rc;
return TRUE;
}
void CWaitingTreeCtrl::DrawUserIcon()
{
// draw user defined icon
CRect rcIcon;
if (!GetItemImageRect(m_hItemMsg, &rcIcon))
return; // no image
// create background brush with current bg color (take rgb part only)
HBRUSH hBrush = CreateSolidBrush(GetBkColor() & 0x00FFFFFF);
CClientDC dc(this);
if (m_hIconMsg != NULL)
DrawIconEx(dc.GetSafeHdc(), rcIcon.left, rcIcon.top, m_hIconMsg,
rcIcon.Width(), rcIcon.Height(), 0, hBrush, DI_NORMAL);
else
FillRect(dc.GetSafeHdc(), &rcIcon, hBrush);
DeleteObject(hBrush);
}
void CWaitingTreeCtrl::SetWaitMessage(LPCTSTR pszText, HICON hIcon)
{
m_sWaitMsg = pszText;
m_hIconMsg = hIcon;
}
void CWaitingTreeCtrl::RefreshSubItems(HTREEITEM hParent)
{
if (hParent != TVI_ROOT && !ItemHasChildren(hParent))
return;
SetRedraw(FALSE);
DeleteChildren(hParent);
if (hParent == TVI_ROOT)
PopulateRoot();
else
{
PreExpandItem(hParent);
ExpandItem(hParent);
}
SetRedraw(TRUE);
}
inline BOOL CWaitingTreeCtrl::NeedsChildren(HTREEITEM hParent)
{
return (GetChildItem(hParent) == NULL);
}
void CWaitingTreeCtrl::DeleteChildren(HTREEITEM hParent)
{
HTREEITEM hChild = GetChildItem(hParent);
HTREEITEM hNext;
while (hChild != NULL)
{
hNext = GetNextSiblingItem(hChild);
DeleteItem(hChild);
hChild = hNext;
}
}
BOOL CWaitingTreeCtrl::OnEraseBkgnd(CDC* pDC)
{
if (!m_bDrawSnapshot)
return CTreeCtrl::OnEraseBkgnd(pDC);
DrawSnapshot(pDC);
SetEvent(m_hRedrawEvent);
return TRUE;
}
void CWaitingTreeCtrl::TakeSnapshot()
{
CClientDC dc(this);
CRect rcClient;
GetClientRect(&rcClient);
int width = rcClient.Width(), height = rcClient.Height();
// create the snapshot
CDC dcSnapshot;
dcSnapshot.CreateCompatibleDC(&dc);
m_bmpSnapshot.CreateCompatibleBitmap(&dc, width, height);
// copy the control's background
CBitmap* pOldBmp = dcSnapshot.SelectObject(&m_bmpSnapshot);
dcSnapshot.BitBlt(0, 0, width, height, &dc, 0, 0, SRCCOPY);
dcSnapshot.SelectObject(pOldBmp);
m_bDrawSnapshot = TRUE;
}
void CWaitingTreeCtrl::DrawSnapshot(CDC *pDC)
{
BITMAP bm;
m_bmpSnapshot.GetBitmap(&bm);
// prepare the snapshot
CDC dcSnapshot;
dcSnapshot.CreateCompatibleDC(pDC);
// copy to the control's background
CBitmap* pOldBmp = dcSnapshot.SelectObject(&m_bmpSnapshot);
pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcSnapshot, 0, 0, SRCCOPY);
dcSnapshot.SelectObject(pOldBmp);
}
void CWaitingTreeCtrl::DestroySnapshot()
{
m_bmpSnapshot.DeleteObject();
m_bDrawSnapshot = FALSE;
}
@@ -0,0 +1,125 @@
// WaitingTreeCtrl.h : header file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_WAITINGTREECTRL_H__80CBE29B_F1A6_41D5_9DF3_B725E73BCF0F__INCLUDED_)
#define AFX_WAITINGTREECTRL_H__80CBE29B_F1A6_41D5_9DF3_B725E73BCF0F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl window
class CWaitingTreeCtrl : public CTreeCtrl
{
private:
void DestroySnapshot();
void DrawSnapshot(CDC* pDC);
void TakeSnapshot();
HANDLE m_hThread; // secondary thread for animations
HANDLE m_hTimerEvent; // signaled at each timer period
HANDLE m_hRedrawEvent; // signaled at each population update
volatile BOOL m_bAbortAnimation; // request to terminate secondary thread
BOOL m_bDrawSnapshot; // whether to draw background during populating
CBitmap m_bmpSnapshot; // snapshot bitmap
UINT m_nTimerID; // animation timer id
UINT m_nTimerDelay; // animation timer period (ms)
CString m_sWaitMsg; // text for the wait message
HICON m_hIconMsg; // icon for the wait message
BOOL m_bShowWaitMsg; // wether to show the wait message
int m_iItemIndex; // population progress index
int m_iItemCount; // population progress max index
HTREEITEM m_hItemMsg; // wait message item
HTREEITEM m_hItemToPopulate; // item being populated
// secondary thread entry point
static DWORD WINAPI AnimationThreadProc(LPVOID pThis);
void StartAnimation(); // set up animation thread
void StopAnimation(); // animation clean up
void PreExpandItem(HTREEITEM hItem); // before expanding
void ExpandItem(HTREEITEM hItem); // after expanded
BOOL NeedsChildren(HTREEITEM hParent); // true if no child items
void DeleteChildren(HTREEITEM hParent);
void DrawUserIcon(); // draw wait message icon
// Construction
public:
CWaitingTreeCtrl();
virtual ~CWaitingTreeCtrl();
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CWaitingTreeCtrl)
public:
//}}AFX_VIRTUAL
// Implementation
public:
void RefreshSubItems(HTREEITEM hParent);
void SetWaitMessage(LPCTSTR pszText, HICON hIcon = NULL);
void ShowWaitMessage()
{
m_bShowWaitMsg = TRUE;
};
protected:
// animation functions (with timer)
void SetAnimationDelay(UINT nMilliseconds);
// animation functions (with or without timer)
virtual void PreAnimation(HTREEITEM hItemMsg);
virtual void DoAnimation(BOOL bTimerEvent, int iMaxSteps, int iStep);
virtual void PostAnimation();
BOOL GetItemImageRect(HTREEITEM hItem, LPRECT pRect);
// tree content functions
void PopulateRoot();
virtual BOOL WantsRefresh(HTREEITEM hItem);
virtual BOOL PopulateItem(HTREEITEM hParent) = 0; // must be implemented
// tree content functions (for animations without timer)
int GetPopulationCount(int *piMaxSubItems = NULL);
void SetPopulationCount(int iMaxSubItems, int iFirstSubItem = 0);
void IncreasePopulation(int iSubItemsToAdd = 1);
void UpdatePopulation(int iSubItems);
// Generated message map functions
//{{AFX_MSG(CWaitingTreeCtrl)
afx_msg void OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_WAITINGTREECTRL_H__80CBE29B_F1A6_41D5_9DF3_B725E73BCF0F__INCLUDED_)