CHSFLD.CPP

//////////////////////////////////////////////////////////////////////////// 
//
// CHSFLD.CPP
//
//
// Copyright 1986-1996 Microsoft Corporation. All Rights Reserved.
///////////////////////////////////////////////////////////////////////


#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <mapix.h>
#include <mapiutil.h>
#include <mapidbg.h>
#include "chsfld.h"
#include "lasterr.h"
#include "tvdlg.h"
#include "resource.h"
#include "tvstack.h"



#define cImageHeight 16
#define cImageWidth 16
#define cImages 4

//globals
LPSTR g_szAllStores = "All Message Stores";
LPSTR g_szModuleName = "Choose Folder Dialog";

//functions used only in this file
BOOL CALLBACK
ChsFldDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);

HRESULT HrGetNewName(HINSTANCE hInst, HWND hwParent, LPSTR * pszNewName);

BOOL CALLBACK
NewNameDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);


//
// HrPickFolder
//
//
STDAPI
HrPickFolder(HINSTANCE hInst, HWND hWnd, LPMAPISESSION pses, LPMAPIFOLDER * ppfld,
LPMDB *ppmdb, ULONG * pcb, LPBYTE * ppb)
{
HRESULT hr;

//Assert(hInst);

if((hWnd && !IsWindow(hWnd)) || (!pses) || (!ppfld) || (!ppmdb))
{
DebugTraceResult(HrPickFolder, E_INVALIDARG);
return E_INVALIDARG;
}

if(pcb && IsBadWritePtr(pcb, sizeof(ULONG)))
{
DebugTraceArg(HrPickFolder, "pcb not writable");
return E_INVALIDARG;
}

if(pcb && (*pcb & 0x3))
{
DebugTraceArg(HrPickFolder, "pcb not multiple of 4");
return E_INVALIDARG;
}

if(ppb && IsBadWritePtr(ppb, sizeof(LPBYTE)))
{
DebugTraceArg(HrPickFolder, "ppb not writable");
return E_INVALIDARG;
}

if(ppb && pcb && IsBadWritePtr(*ppb, *pcb))
{
DebugTraceArg(HrPickFolder, "*pcb or *ppb");
return E_INVALIDARG;
}

//////////////////////////////////////////////////////////////////////
// if you incorporate this code into you app, remove this and pass in
// the right hInst
// Start remove
hInst = GetModuleHandle("chsfld32.dll");
if(!hInst)
{
DebugTrace("GetModuleHandel failed\n");
DebugTraceResult(HrPickFolder, E_FAIL);
return E_FAIL;
}
// End remove
///////////////////////////////////////////////////////////////////////

//ULONG cb = 0;
//LPBYTE pb = NULL;

CChsFldDlg PickDlg(pses, hInst, pcb, ppb);

InitCommonControls();

hr = PickDlg.HrPick(MAKEINTRESOURCE(IDD_CFDIALOG), hWnd,
(DLGPROC)ChsFldDlgProc, ppfld, ppmdb);

/* if(SUCCEEDED(hr))
{
(*ppfld)->Release();
(*ppmdb)->Release();
}

CChsFldDlg PickDlg1(pses, hInst, pcb, ppb);

hr = PickDlg1.HrPick(MAKEINTRESOURCE(IDD_CFDIALOG), hWnd,
(DLGPROC)ChsFldDlgProc, ppfld, ppmdb);*/

// if(!hr)
// MAPIFreeBuffer(pb);

DebugTraceResult(HrPickFolder, hr);
return hr;
}


//
// CChsFldDlg::CChsFldDlg
//
inline
CChsFldDlg::CChsFldDlg(LPMAPISESSION pses, HINSTANCE hInst, ULONG * pcb,
LPBYTE * ppb) : m_lsterr(g_szModuleName)
{
Assert(pses);
Assert(hInst);

m_pses = pses;
pses->AddRef();

m_hr = hrSuccess;
m_pfld = NULL;
m_pmdb = NULL;
m_hiRoot = NULL;
m_hInst = hInst;
m_hIml = NULL;
m_hDlg = NULL;
m_hwTreeCtl = NULL;
m_pcbState = pcb;
m_ppbState = ppb;
}

//
// CChsFldDlg::~CChsFldDlg
//
CChsFldDlg::~CChsFldDlg()
{
UlRelease(m_pses);
UlRelease(m_pfld);
UlRelease(m_pmdb);

if(m_hIml)
ImageList_Destroy(m_hIml);
}


//
// CChsFldDlg::SetFolder
//
// Store the folder chosen by the user
//
inline void CChsFldDlg::SetFolder(LPMAPIFOLDER pfld, LPMDB pmdb)
{
UlRelease(m_pfld);

m_pfld = pfld;

if(pfld)
pfld->AddRef();

UlRelease(m_pmdb);

m_pmdb = pmdb;
if(pmdb)
pmdb->AddRef();
}


//
// CChsFldDlg::HrPick
//
// The outmost method.
//
HRESULT CChsFldDlg::HrPick(LPCTSTR lpTemplateName, HWND hWnd,
DLGPROC pfnDlgProc, LPMAPIFOLDER * ppfld, LPMDB *ppmdb)
{
if(-1 == DialogBoxParam(m_hInst, lpTemplateName, hWnd, pfnDlgProc, (LPARAM) this))
{
DebugTraceSc(CChsDldDlg::HrPick, MAPI_E_NOT_ENOUGH_MEMORY);
return MAPI_E_NOT_ENOUGH_MEMORY;
}

//m_hr is set inside the dialog
if(HR_SUCCEEDED(m_hr))
{
Assert(m_pfld);
m_pfld->AddRef();
*ppfld = m_pfld;

Assert(m_pmdb);
m_pmdb->AddRef();
*ppmdb = m_pmdb;
}

return m_hr;
}

//
// CChsFldDlg::HrInitTree
//
// Called from WM_INITDIALOG. Opens all message stores in the profile and
// puts the IPM subtrees in the tree control
//
HRESULT CChsFldDlg::HrInitTree(HWND hDlg, HWND hwTreeCtl)
{
HRESULT hr;
LPSPropValue pval = NULL;
LPTVNODE pNode = NULL;
HTREEITEM hiRoot = NULL;
HICON hIcon = NULL;

Assert(hDlg);
Assert(hwTreeCtl);

m_hwTreeCtl = hwTreeCtl;
m_hDlg = hDlg;


//
// Set up the image list
//
m_hIml = ImageList_Create(cImageWidth, cImageHeight, ILC_MASK,
cImages, 0);
if(!m_hIml)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}

hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_ALLSTORES));
m_iIconAllStores = ImageList_AddIcon(m_hIml, hIcon);

hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_ROOTFLD));
m_iIconRootFld = ImageList_AddIcon(m_hIml, hIcon);

hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_OPENFLD));
m_iIconOpenFld = ImageList_AddIcon(m_hIml, hIcon);

hIcon = LoadIcon(m_hInst, MAKEINTRESOURCE(IDI_CLSDFLD));
m_iIconClsdFld = ImageList_AddIcon(m_hIml, hIcon);

if(ImageList_GetImageCount(m_hIml) < cImages)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}


TreeView_SetImageList(hwTreeCtl, m_hIml, TVSIL_NORMAL);

//
// create the root tree node
// (fake a GetProps)
//
hr = MAPIAllocateBuffer(nhtProps * sizeof(SPropValue),
(LPVOID *)&pval);
if(hr)
{
m_lsterr.HrSetLastError(hr);
m_lsterr.ShowError(hDlg);

goto err;
}


ZeroMemory(pval, nhtProps * sizeof(SPropValue));

//Set proptags to make CNode constructor happy
pval[iEID].ulPropTag = PR_ENTRYID;
pval[iDispName].ulPropTag = PR_DISPLAY_NAME;
pval[iDispName].Value.lpszA = g_szAllStores;
pval[iSubfldrs].ulPropTag = PR_SUBFOLDERS;

hr = HrCreateNode(pval, nhtProps, NULL, &pNode);
if(hr)
goto err;

Assert(pNode);

pval = NULL; //will be freed in ~CTVNode

hiRoot = AddOneItem(NULL, TVI_ROOT, m_iIconAllStores, m_iIconAllStores,
hwTreeCtl, pNode, 1);
if(!hiRoot)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}

pNode->SetKidsLoaded(TRUE);

m_hiRoot = hiRoot;

//
// Put the IPM subtrees of all the message stores in
//
hr = HrLoadRoots();
if(HR_FAILED(hr))
goto err;

(void)HrRestoreTreeState();

err:
MAPIFreeBuffer(pval);

DebugTraceResult(CChsFldDlg::HrInitTree, hr);
return hr;
}


//
// CChsFldDlg::HrLoadRoots
//
HRESULT CChsFldDlg::HrLoadRoots(void)
{
HRESULT hr;
LPMAPITABLE ptblMStrs = NULL;
UINT ind;
LPSRowSet pRows = NULL;
static SSortOrderSet sosName;

sosName.cSorts = 1;
sosName.cCategories = 0;
sosName.cExpanded = 0;
sosName.aSort[0].ulPropTag = PR_DISPLAY_NAME;
sosName.aSort[0].ulOrder = TABLE_SORT_ASCEND;


//Get Message Store Table
hr = m_pses->GetMsgStoresTable(0, &ptblMStrs);
if(hr)
{
m_lsterr.HrSetLastError(hr, m_pses);
m_lsterr.ShowError(m_hDlg);

goto err;
}

//For each msg store insert a node corresponding to PR_IPM_SUBTREE

hr = HrQueryAllRows(ptblMStrs, (LPSPropTagArray) &spthtProps, NULL,
&sosName, 0, &pRows);

if(HR_FAILED(hr))
goto err;

if(0 == pRows->cRows) //$ No stores
{
MessageBox(m_hDlg,
"No message stores in the profile",
g_szModuleName,
MB_OK);
hr = E_FAIL;
}

for(ind = 0; ind < pRows->cRows; ++ind)
{
LPSPropValue pval = pRows->aRow[ind].lpProps;
Assert(pRows->aRow[ind].cValues == nhtProps);
Assert(pval[iEID].ulPropTag == PR_ENTRYID);

pval[iSubfldrs].ulPropTag = PR_SUBFOLDERS;
pval[iSubfldrs].Value.b = TRUE;

//pval is consumed by this function
hr = HrInsertRoot(pval);
pRows->aRow[ind].cValues = 0;
pRows->aRow[ind].lpProps = NULL;
if(FAILED(hr))
goto err;

}



err:
FreeProws(pRows);
UlRelease(ptblMStrs);

DebugTraceResult(CChsFldDlg::HrLoadRoots, hr);
return hr;
}

//
// CChsFldDlg::HrInsertRoot
//
// Put the IPM subtree of the msg store in the tree control
// pval is consumed
//
HRESULT CChsFldDlg::HrInsertRoot(LPSPropValue pval)
{
HRESULT hr;
HTREEITEM hItem;


Assert(m_hiRoot);


LPTVNODE pNode = NULL;
hr = HrCreateNode(pval, nhtProps, NULL, &pNode);
if(hr)
{
MAPIFreeBuffer(pval);
goto err;
}

Assert(pNode);
pval = NULL;


hItem = AddOneItem(m_hiRoot, TVI_LAST, m_iIconRootFld, m_iIconRootFld,
m_hwTreeCtl, pNode, 1);
if(!hItem)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}


err:

DebugTraceResult(CChsFldDlg::HrInsertRoots, hr);
return hr;
}


//
// CChsFldDlg::HrSaveTreeState
//
// Save expand - collapse state of the tree control
//
HRESULT CChsFldDlg::HrSaveTreeState(void)
{
HRESULT hr;

if(!m_pcbState || !m_ppbState)
return hrSuccess;

MAPIFreeBuffer(*m_ppbState);
*m_ppbState = NULL;
*m_pcbState = 0;

hr = HrSaveTreeStateEx(FALSE, m_pcbState, NULL);
if(hr)
goto err;

DebugTrace("ChsFld: size of state data: %ld\n", *m_pcbState);

hr = HrSaveTreeStateEx(TRUE, m_pcbState, m_ppbState);

err:
DebugTraceResult(CChsFldDlg::HrSaveTreeState, hr);
return hr;
}

//
// CChsFldDlg::HrSaveTreeStateEx
//
// Save expand - collapse state of the tree control
//
HRESULT CChsFldDlg::HrSaveTreeStateEx(BOOL fWrite, ULONG * pcb, LPBYTE * ppb)
{
HRESULT hr = hrSuccess;
CTIStack tiStack;
HTREEITEM hti;
LPBYTE pb = NULL;
LPBYTE pBuffer = NULL;
LONG iLevel;

if(fWrite)
{
if(*pcb == 0)
{
*ppb = NULL;
return hrSuccess;
}
else
{
hr = MAPIAllocateBuffer(*pcb, (LPVOID *) &pBuffer);
if (hr)
{
*pcb = 0;
return hr;
}
pb = pBuffer;
}
}

hti = TreeView_GetRoot(m_hwTreeCtl);
iLevel = 0;
tiStack.Push(NULL);

while(hti)
{
Assert(iLevel >= 0);

while(hti)
{
TV_ITEM tvi;

tvi.hItem = hti;
tvi.mask = TVIF_STATE | TVIF_PARAM;
tvi.lParam = 0;
tvi.state = 0;
tvi.stateMask = TVIS_EXPANDED;

if(!TreeView_GetItem(m_hwTreeCtl, &tvi))
{
hr = E_FAIL;
goto err;
}

if(tvi.state & TVIS_EXPANDED)
{
HTREEITEM htiChild = TreeView_GetChild(m_hwTreeCtl, hti);

if(htiChild)
{
LPTVNODE pNode = (LPTVNODE) tvi.lParam;
Assert(pNode);

pNode->Write(fWrite, iLevel, &pb);

HTREEITEM htiNextSibl = TreeView_GetNextSibling(m_hwTreeCtl, hti);

tiStack.Push(htiNextSibl);

hti = htiChild;
++iLevel;

continue;
}

}

hti = TreeView_GetNextSibling(m_hwTreeCtl, hti);
}

do
{
hti = tiStack.Pop();
--iLevel;

}while(!tiStack.IsEmpty() && hti == NULL);
}

Assert(iLevel == -1);

*pcb = pb - pBuffer;
if(pBuffer)
*ppb = pBuffer;

err:
DebugTraceResult(CChsFldDlg::HrSaveTreeStateEx, hr);
return hr;
}

inline LONG GetLevel(LPBYTE * ppb)
{
LONG level = *((LONG *) *ppb);

*ppb += sizeof(LONG);

return level;
}

inline ULONG GetCb(LPBYTE * ppb)
{
ULONG cb = *((ULONG *) *ppb);

*ppb += sizeof(ULONG);

return cb;
}

HTREEITEM HtiFindChild(HWND hwTreeCtl, HTREEITEM hti, ULONG cb,
LPENTRYID pbEID, CChsFldDlg *pCFDlg, LPTVNODE *ppNode)
{
HRESULT hr;
HTREEITEM htiChild;

htiChild = TreeView_GetChild(hwTreeCtl, hti);

while(htiChild)
{
TV_ITEM tvi;

tvi.hItem = htiChild;
tvi.mask = TVIF_PARAM;
tvi.lParam = 0;

if(!TreeView_GetItem(hwTreeCtl, &tvi))
return NULL;

LPTVNODE pNode = (LPTVNODE) tvi.lParam;
Assert(pNode);

ULONG ulMatch = 0;
hr = pCFDlg->Session()->CompareEntryIDs(cb, pbEID,
pNode->m_pval[iEID].Value.bin.cb,
(LPENTRYID)pNode->m_pval[iEID].Value.bin.lpb,
0, &ulMatch);
if(SUCCEEDED(hr))
{
if(ulMatch)
{
*ppNode = pNode;
return htiChild;
}
}

htiChild = TreeView_GetNextSibling(hwTreeCtl, htiChild);
}

return htiChild;
}

//
// CChsFldDlg::HrRestoreTreeState
//
HRESULT CChsFldDlg::HrRestoreTreeState(void)
{
HRESULT hr = hrSuccess;
LPBYTE pb;
LPBYTE pbMax;
CTIStack tiStack;
HTREEITEM hti;
LONG iLevel = 0;
BOOL fNodeMissing = FALSE;

if(!m_pcbState || *m_pcbState == 0)
return hrSuccess;

try //protect ourself from callers who mess with the state data
{
Assert(m_hwTreeCtl);

Assert(m_ppbState);
pb = *m_ppbState;

pbMax = pb + *m_pcbState;

hti = TreeView_GetRoot(m_hwTreeCtl);

iLevel = GetLevel(&pb);
Assert(iLevel == 0);

TreeView_Expand(m_hwTreeCtl, hti, TVE_EXPAND);

while(hti)
{
if(pb >= pbMax)
break; //done

LONG iNewLevel = GetLevel(&pb);

if(iNewLevel <= iLevel)
{
do
{
hti = tiStack.Pop();
--iLevel;
}while(iLevel >= iNewLevel);

Assert(hti);
}

if(iNewLevel > iLevel)
{
if(!fNodeMissing)
Assert(iNewLevel == iLevel + 1);

ULONG cbEID = GetCb(&pb);
LPENTRYID pbEID = (LPENTRYID)pb;
pb += Align4(cbEID);

if(iNewLevel != iLevel +1)
continue;

LPTVNODE pNodeChild = NULL;
HTREEITEM htiChild = HtiFindChild(m_hwTreeCtl, hti, cbEID, pbEID,
this, &pNodeChild);
if(htiChild)
{
fNodeMissing = FALSE;

hr = pNodeChild->HrExpand(this);
if(FAILED(hr))
goto err;

TreeView_Expand(m_hwTreeCtl, htiChild, TVE_EXPAND);

tiStack.Push(hti);

hti = htiChild;
++iLevel;

continue;
}
else
{
//Assert(FALSE); //$ handle
fNodeMissing = TRUE;
}

}
/*else
{
do
{
hti = tiStack.Pop();
--iLevel;
}while(iLevel >= iNewLevel);
}*/
}
}

catch(...)
{
DebugTrace("chsfld: Exception caught in HrRestoreTreeState\n");
hr = E_FAIL;
}

err:

/*MAPIFreeBuffer(*m_ppbState);
*m_ppbState = NULL;
*m_pcbState = 0;*/


DebugTraceResult(CChsFldDlg::HrRestoreTreeState, hr);
return hr;
}


//////////////////////////////////////////////////////////////////////////
// CTVNodeFactory

//
// CTVNodeFactory::CTVNodeFactory
//
inline CTVNodeFactory::CTVNodeFactory()
{
m_pHead = NULL;
}

//
// CTVNodeFactory::~CTVNodeFactory
//
// Destroy all created CTVNode s
CTVNodeFactory::~CTVNodeFactory()
{
while(m_pHead)
{
LPTVNODE ptemp = m_pHead;

m_pHead = m_pHead->m_pNext;

delete ptemp;
}
}


//
// CTVNodeFactory::HrCreateNode
//
// All instances of CTVNode are created through this method
//
HRESULT CTVNodeFactory::HrCreateNode(LPSPropValue pval, ULONG cVals, LPMDB pmdb,
LPTVNODE * pptvNode)
{
HRESULT hr = hrSuccess;

LPTVNODE pNode = new CTVNode(pval, cVals, pmdb);

if(!pNode)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto err;
}

Insert(pNode);

*pptvNode = pNode;

err:

DebugTraceResult(CTVNodeFactory::HrCreateNode, hr);
return hr;
}


//
// CTVNodeFactory::Insert
//
// Store all created CTVNode s so that we can destroy them when we are done
//
void CTVNodeFactory::Insert(LPTVNODE pNode)
{
pNode->m_pNext = m_pHead;
m_pHead = pNode;
}


//
// ChsFldDlgProc
//
// Dialog proc for the choose folder dialog
//
// Controls:
// IDOK "OK"
// IDCANCEL "Cancel"
// IDC_NEWFLD "New Folder"
//
BOOL CALLBACK
ChsFldDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
CChsFldDlg * pCDlg = NULL;
HWND hwTreeCtl = NULL;
int wmId;
int wmEvent;
HTREEITEM hti = NULL;
TV_ITEM tvi;

switch(msg)
{
case WM_INITDIALOG:
Assert(lParam);
pCDlg = (CChsFldDlg *)lParam;

hwTreeCtl = GetDlgItem(hDlg, IDC_TREEVIEW);
Assert(hwTreeCtl);

hr = pCDlg->HrInitTree(hDlg, hwTreeCtl);
if(HR_FAILED(hr))
{
pCDlg->SetError(hr);
EndDialog(hDlg, 1);
break;
}

SetWindowLong(hDlg, DWL_USER, (LONG)pCDlg);

break;

case WM_COMMAND:
wmId = GET_WM_COMMAND_ID(wParam, lParam);
wmEvent = GET_WM_COMMAND_CMD(wParam, lParam);

hwTreeCtl = GetDlgItem(hDlg, IDC_TREEVIEW);
Assert(hwTreeCtl);

pCDlg = (CChsFldDlg *)GetWindowLong(hDlg, DWL_USER);
Assert(pCDlg);


switch(wmId)
{
case IDOK:
switch (wmEvent)
{
case BN_CLICKED:

hti = TreeView_GetSelection(hwTreeCtl);
AssertSz(hti, "No Selection?");

tvi.hItem = hti;
tvi.mask = TVIF_PARAM;

if(TreeView_GetItem(hwTreeCtl, &tvi))
{
LPTVNODE pNode = (LPTVNODE)tvi.lParam;
Assert(pNode);

LPMAPIFOLDER pfld = NULL;
LPMDB pmdb = NULL;

hr = pNode->HrGetFolder(pCDlg, &pfld, &pmdb);
if(HR_SUCCEEDED(hr))
{
pCDlg->SetFolder(pfld, pmdb);
pfld->Release();
pmdb->Release();

hr = pCDlg->HrSaveTreeState();
}
else
{
pCDlg->SetError(hr);
}
}
else
{
pCDlg->SetError(E_FAIL);
}

EndDialog(hDlg, TRUE);

break;

default:
return FALSE;
}
break;

case IDC_NEWFLD:
switch(wmEvent)
{
case BN_CLICKED:

hti = TreeView_GetSelection(hwTreeCtl);
AssertSz(hti, "No Selection?");

tvi.hItem = hti;
tvi.mask = TVIF_PARAM;

if(TreeView_GetItem(hwTreeCtl, &tvi))
{
LPTVNODE pNode = (LPTVNODE)tvi.lParam;
Assert(pNode);
LPSTR szName = NULL;

do
{
hr = HrGetNewName(pCDlg->hInst(), hDlg, &szName);
if(HR_SUCCEEDED(hr))
{
hr = pNode->HrNewFolder(pCDlg, szName);
}
}while(hr == MAPI_E_COLLISION);

MAPIFreeBuffer(szName);
szName = NULL;

}

SetFocus(hwTreeCtl);

break;

default:
return FALSE;
}
break;

case IDCANCEL:
switch(wmEvent)
{
case BN_CLICKED:

pCDlg->SetError(MAPI_E_USER_CANCEL);

EndDialog(hDlg, TRUE);
break;

default:
return FALSE;
}
break;
}
break;

case WM_NOTIFY:
switch( ((LPNMHDR)lParam)->code)
{
case TVN_ITEMEXPANDING:
{
Assert(((LPNMHDR)lParam)->idFrom == IDC_TREEVIEW);

NM_TREEVIEW * ptntv = (NM_TREEVIEW *)lParam;

if(ptntv->action != TVE_EXPAND)
return FALSE;

//
// If the kids of this node are not loaded, load'em
LPTVNODE pNode = (LPTVNODE)ptntv->itemNew.lParam;
Assert(pNode);

hwTreeCtl = ((LPNMHDR)lParam)->hwndFrom;

pCDlg = (CChsFldDlg *)GetWindowLong(hDlg, DWL_USER);
//Assert(pCDlg);

hr = pNode->HrExpand(pCDlg);
if(HR_FAILED(hr))
{
return TRUE;
}
return FALSE;
}
break;
case TVN_GETDISPINFO:
{
Assert(((LPNMHDR)lParam)->idFrom == IDC_TREEVIEW);

//
// we don't give folder names to the tree control (to save space)
// when it wants to display an item, it asks us for the name
//
TV_DISPINFO * pdi = (TV_DISPINFO *)lParam;

if(pdi->item.mask & TVIF_TEXT)
{
pdi->item.pszText = ((LPTVNODE)pdi->item.lParam)->GetName();
return TRUE;
}
else
{
return FALSE;
}
}

break;

case TVN_SELCHANGED:
//
//Enable "OK" and "New Folder" buttons only if it is not the
//root node
//
{Assert(((LPNMHDR)lParam)->idFrom == IDC_TREEVIEW);

NM_TREEVIEW *ptntv = (NM_TREEVIEW *)lParam;

pCDlg = (CChsFldDlg *)GetWindowLong(hDlg, DWL_USER);
Assert(pCDlg);

EnableWindow(GetDlgItem(hDlg, IDOK),
!pCDlg->IsTreeRoot(ptntv->itemNew.hItem));
EnableWindow(GetDlgItem(hDlg, IDC_NEWFLD),
!pCDlg->IsTreeRoot(ptntv->itemNew.hItem));
break;
}

break;
}

break;

default:
return FALSE;
}
return TRUE;
}

//
// AddOneItem
//
// Add a node to the tree control
//
HTREEITEM AddOneItem( HTREEITEM hParent, HTREEITEM hInsAfter,
int iImage, int iImageSel, HWND hwndTree, LPTVNODE pNode, int cKids)
{
HTREEITEM hItem;
TV_INSERTSTRUCT tvIns;

tvIns.item.mask = TVIF_CHILDREN | TVIF_PARAM |TVIF_TEXT |
TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvIns.item.pszText = LPSTR_TEXTCALLBACK;
tvIns.item.cchTextMax = 0;
tvIns.item.lParam = (LPARAM)pNode;
tvIns.item.cChildren = cKids;
tvIns.item.iImage = iImage;
tvIns.item.iSelectedImage = iImageSel;

tvIns.hInsertAfter = hInsAfter;
tvIns.hParent = hParent;

// Insert the item into the tree.
hItem = TreeView_InsertItem(hwndTree, &tvIns);

pNode->SetHandle(hItem);

return (hItem);
}


//
// HrGetNewName
//
// Display dialog asking the user for a new folder name
//
// If *pszNewName is not NULL, it has to be a string allocated with
// MAPIAllocateBuffer. It will be displayed in the dialog.
// The returned string has to be freed with MAPIFreeBuffer.
//
HRESULT HrGetNewName(HINSTANCE hInst, HWND hwParent, LPSTR * pszNewName)
{
Assert(pszNewName);

int nRes = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_NEWNAME), hwParent,
NewNameDlgProc, (LPARAM)pszNewName);
if(nRes == 1)
{
return hrSuccess;
}
else
{
DebugTraceSc(HrGetNewName, E_FAIL);
return E_FAIL;
}
}


//
// NewNameDlgProc
//
// Dlg proc for the "New Name" dialog;
// If user chooses OK, return 1 from EndDialog.
//
BOOL CALLBACK
NewNameDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
int wmId;
int wmEvent;

switch(msg)
{
case WM_INITDIALOG:
{
Assert(lParam);

LPSTR * pszName = (LPSTR *)lParam;

if(*pszName)
{
SetWindowText(GetDlgItem(hDlg, IDC_NAME), *pszName);
MAPIFreeBuffer(*pszName);
*pszName = NULL;

}

SetWindowLong(hDlg, DWL_USER, (LONG)lParam);
SetFocus(GetDlgItem(hDlg, IDC_NAME));
return FALSE;
}
break;

case WM_COMMAND:
wmId = GET_WM_COMMAND_ID(wParam, lParam);
wmEvent = GET_WM_COMMAND_CMD(wParam, lParam);

switch(wmId)
{
case IDOK:
switch (wmEvent)
{
case BN_CLICKED:
{
HWND hwName = GetDlgItem(hDlg, IDC_NAME);

int cb = Edit_GetTextLength(hwName);
Assert(cb); //OK is disabled when edit control is empty

LPSTR szName = NULL;
if(!MAPIAllocateBuffer(cb + 1, (LPVOID *)&szName))
{
GetWindowText(hwName, szName, cb+1);

LPSTR * pszName = (LPSTR *)GetWindowLong(hDlg, DWL_USER);

*pszName = szName;

EndDialog(hDlg, 1);
}
else
{
EndDialog(hDlg, FALSE);
break;
}
}
break;

default:

return FALSE; 
}

break;

case IDCANCEL:
switch (wmEvent)
{
case BN_CLICKED:
EndDialog(hDlg, FALSE);
break;

default:
return FALSE;
}

break;

case IDC_NAME:
switch(wmEvent)
{
case EN_CHANGE:
Assert((HWND)lParam == GetDlgItem(hDlg, IDC_NAME));

EnableWindow(GetDlgItem(hDlg, IDOK),
Edit_GetTextLength((HWND)lParam));

break;

default:
return FALSE;
}

break;
}
default:
return FALSE;
}

return TRUE;
}