1 //*******************************************************************************
4 // You may use this source code, compile or redistribute it as part of your application
5 // for free. You cannot redistribute it as a part of a software development
6 // library without the agreement of the author. If the sources are
7 // distributed along with the application, you should leave the original
8 // copyright notes in the source code without any changes.
9 // This code can be used WITHOUT ANY WARRANTIES at your own risk.
11 // For the latest updates to this code, check this site:
12 // http://www.masmex.com
15 // Copyright(C) 2000 Philip Oldaker <email: philip@masmex.com>
16 //*******************************************************************************
18 // IEShellTreeCtrl.cpp : implementation file
20 #include "IEShellTreeCtrl.h"
21 #include "cbformats.h"
22 #include "UIMessages.h"
28 static char THIS_FILE[] = __FILE__;
31 /////////////////////////////////////////////////////////////////////////////
34 CIEShellTreeCtrl::CIEShellTreeCtrl()
40 m_bRefreshAllowed = true;
41 m_bNotifyParent = false;
42 // Turn off WM_DROPFILES
46 CIEShellTreeCtrl::~CIEShellTreeCtrl()
50 void CIEShellTreeCtrl::ShellExecute(HTREEITEM hItem,LPCTSTR pszVerb)
53 ZeroMemory(&si,sizeof(si));
54 si.cbSize = sizeof(si);
55 si.hwnd = GetSafeHwnd();
57 si.lpIDList = (LPVOID)GetPathPidl(hItem);
58 si.fMask = SEE_MASK_INVOKEIDLIST;
64 void CIEShellTreeCtrl::RefreshComboBox(LPTVITEMDATA lptvid)
68 ::PostMessage(m_hComboWnd,WM_APP_CB_IE_POPULATE,(WPARAM)lptvid->lpifq,0);
72 void CIEShellTreeCtrl::SetNotificationObject(bool bNotify)
75 CreateFileChangeThreads(GetSafeHwnd());
80 void CIEShellTreeCtrl::UpOneLevel(HTREEITEM hItem)
84 hItem = GetSelectedItem();
88 HTREEITEM hParentItem = GetParentItem(hItem);
90 Select(hParentItem,TVGN_CARET);
93 void CIEShellTreeCtrl::DestroyThreads()
95 if (m_nThreadCount == 0)
97 for (UINT i=0;i < m_nThreadCount; i++)
98 m_event[i].SetEvent();
99 ::WaitForMultipleObjects (m_nThreadCount, m_hThreads, TRUE, INFINITE);
100 for (i=0; i < m_nThreadCount; i++)
101 delete m_pThreads[i];
105 void CIEShellTreeCtrl::CreateFileChangeThreads(HWND hwnd)
109 TCHAR szDrives[MAX_PATH];
110 DWORD dwSize = sizeof(szDrives)/sizeof(TCHAR);
111 DWORD dwChars = GetLogicalDriveStrings(dwSize,szDrives);
112 if (dwChars == 0 || dwChars > dwSize)
114 TRACE(_T("Warning: CreateFileChangeThreads failed in GetLogicalDriveStrings\n"));
119 LPCTSTR pszDrives=szDrives;
120 while (*pszDrives != '\0')
123 nType = ::GetDriveType(sDrive);
124 if (nType == DRIVE_FIXED || nType == DRIVE_REMOTE || nType == DRIVE_RAMDISK)
126 CreateFileChangeThread(sDrive,hwnd);
128 #if 1 // bugfix by mad79
129 pszDrives=pszDrives+sDrive.GetLength()+1;
131 pszDrives = _tcsninc(pszDrives,sDrive.GetLength()+1);
136 void CIEShellTreeCtrl::CreateFileChangeThread(const CString& sPath,HWND hwnd)
138 if (m_nThreadCount >= MAX_THREADS)
140 PDC_THREADINFO pThreadInfo = new DC_THREADINFO; // Thread will delete
141 pThreadInfo->sPath = sPath;
142 pThreadInfo->hEvent = m_event[m_nThreadCount].m_hObject;
143 pThreadInfo->pTreeCtrl = this;
145 CWinThread* pThread = AfxBeginThread (ThreadFunc, pThreadInfo,
146 THREAD_PRIORITY_IDLE);
148 pThread->m_bAutoDelete = FALSE;
149 m_hThreads[m_nThreadCount] = pThread->m_hThread;
150 m_pThreads[m_nThreadCount++] = pThread;
153 HTREEITEM CIEShellTreeCtrl::SearchSiblings(HTREEITEM hItem,LPITEMIDLIST pidlAbs)
155 LPTVITEMDATA pItem = NULL;
156 HTREEITEM hChildItem = GetChildItem(hItem);
157 HTREEITEM hFoundItem;
160 pItem = (LPTVITEMDATA)GetItemData(hChildItem);
161 if (GetShellPidl().ComparePidls(NULL,pItem->lpifq,pidlAbs))
163 hFoundItem = SearchSiblings(hChildItem,pidlAbs);
166 hChildItem = GetNextSiblingItem(hChildItem);
171 HTREEITEM CIEShellTreeCtrl::ExpandMyComputer(LPITEMIDLIST pidlAbs)
173 HTREEITEM hItem=NULL;
176 LPITEMIDLIST pidlMyComputer=NULL;
177 LPITEMIDLIST pidlFirst=GetShellPidl().CopyItemID(pidlAbs);
178 SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer);
179 if (GetShellPidl().ComparePidls(NULL,pidlMyComputer,pidlFirst))
181 hItem = ExpandPidl(pidlMyComputer);
184 GetShellPidl().FreePidl(pidlMyComputer);
186 GetShellPidl().FreePidl(pidlFirst);
190 HTREEITEM CIEShellTreeCtrl::ExpandPidl(LPITEMIDLIST pidlAbs)
192 HTREEITEM hItem = SearchSiblings(GetRootItem(),pidlAbs);
195 Expand(hItem,TVE_EXPAND);
200 HTREEITEM CIEShellTreeCtrl::FindPidl(LPITEMIDLIST pidlAbs,BOOL bSelect)
202 HTREEITEM hItem = NULL;
204 hItem = GetRootItem();
206 hItem = SearchSiblings(GetRootItem(),pidlAbs);
207 if (bSelect && hItem != GetSelectedItem())
210 SelectionChanged(hItem,GetItemData(hItem));
215 HTREEITEM CIEShellTreeCtrl::FindItem (HTREEITEM hItem, const CString& strTarget)
217 while (hItem != NULL)
219 if (GetItemText (hItem) == strTarget)
221 hItem = GetNextSiblingItem (hItem);
226 UINT CIEShellTreeCtrl::DeleteChildren (HTREEITEM hItem)
229 HTREEITEM hChild = GetChildItem (hItem);
231 while (hChild != NULL)
233 HTREEITEM hNextItem = GetNextSiblingItem (hChild);
241 void CIEShellTreeCtrl::Init()
243 ModifyStyle(0,TVS_EDITLABELS);
244 CIEFolderTreeCtrl::Init();
247 void CIEShellTreeCtrl::Refresh()
249 SetRefreshAllowed(false);
250 CIEFolderTreeCtrl::Refresh();
251 SetRefreshAllowed(true);
254 bool CIEShellTreeCtrl::DragEnter(CDD_OleDropTargetInfo *pInfo)
256 HTREEITEM hItem = pInfo->GetTreeItem();
259 LPTVITEMDATA ptvid = (LPTVITEMDATA)GetItemData(hItem);
263 return m_ShellDragDrop.DragEnter(pInfo,ptvid->lpsfParent,ptvid->lpi);
266 bool CIEShellTreeCtrl::DragLeave(CDD_OleDropTargetInfo *pInfo)
268 return m_ShellDragDrop.DragLeave(pInfo);
271 bool CIEShellTreeCtrl::DragOver(CDD_OleDropTargetInfo *pInfo)
273 pInfo->SetDropEffect(DROPEFFECT_NONE);
275 HTREEITEM hItem = pInfo->GetTreeItem();
278 LPTVITEMDATA ptvid = (LPTVITEMDATA)GetItemData(hItem);
282 return m_ShellDragDrop.DragOver(pInfo,ptvid->lpsfParent,ptvid->lpi);
285 bool CIEShellTreeCtrl::DragDrop(CDD_OleDropTargetInfo *pInfo)
287 HTREEITEM hItem = pInfo->GetTreeItem();
290 LPTVITEMDATA ptvid = (LPTVITEMDATA)GetItemData(hItem);
294 return m_ShellDragDrop.DragDrop(pInfo,ptvid->lpsfParent,ptvid->lpi);
297 DROPEFFECT CIEShellTreeCtrl::DoDragDrop(NM_TREEVIEW* pNMTreeView,COleDataSource *pOleDataSource)
299 if (pNMTreeView->itemNew.hItem == GetRootItem())
300 return DROPEFFECT_NONE;
303 HTREEITEM hParentItem = GetParentItem(pNMTreeView->itemNew.hItem);
304 LPTVITEMDATA ptvid = (LPTVITEMDATA)GetItemData(pNMTreeView->itemNew.hItem);
305 LPTVITEMDATA ptvid_parent = (LPTVITEMDATA)GetItemData(hParentItem);
307 ASSERT(ptvid_parent);
308 if (GetShellPidl().IsDesktopFolder(ptvid->lpsfParent))
309 sl.AddPidl(GetShellPidl().GetEmptyPidl());
311 sl.AddPidl(ptvid_parent->lpifq);
312 sl.AddPidl(ptvid->lpi);
316 pidl.SHPidlToPathEx(ptvid->lpifq,sPath);
317 cf_hdrop.AddDropPoint(CPoint(pNMTreeView->ptDrag),FALSE);
318 cf_hdrop.AddFileName(sPath);
320 cf_text.SetString(sPath);
321 CWDClipboardData::Instance()->SetData(pOleDataSource,&cf_text,CWDClipboardData::e_cfString);
322 CWDClipboardData::Instance()->SetData(pOleDataSource,&cf_hdrop,CWDClipboardData::e_cfHDROP);
323 CWDClipboardData::Instance()->SetData(pOleDataSource,&sl,CWDClipboardData::e_cfShellIDList);
324 return GetShellPidl().GetDragDropAttributes(ptvid);
327 bool CIEShellTreeCtrl::EndLabelEdit(HTREEITEM hItem,LPCTSTR pszText)
329 LPTVITEMDATA plvit = (LPTVITEMDATA)GetItemData(hItem);
332 GetShellPidl().SHPidlToPathEx(plvit->lpifq,sFromPath);
334 sToPath.Replace(GetItemText(hItem),pszText);
336 TCHAR szFrom[MAX_PATH+1];
337 TCHAR szTo[MAX_PATH+1];
338 ZeroMemory(szFrom,sizeof(szFrom));
339 lstrcpy(szFrom,sFromPath);
340 ZeroMemory(szTo,sizeof(szTo));
341 lstrcpy(szTo,sToPath);
342 ZeroMemory(&shf,sizeof(shf));
343 shf.hwnd = GetSafeHwnd();
344 shf.wFunc = FO_RENAME;
352 AfxMessageBox(sMess);
354 if (SHFileOperation(&shf) == 0)
356 SetRefreshAllowed(false);
360 bool CIEShellTreeCtrl::SHMoveFile(HTREEITEM hSrcItem,HTREEITEM hDestItem)
362 LPTVITEMDATA plvit_src = (LPTVITEMDATA)GetItemData(hSrcItem);
363 LPTVITEMDATA plvit_dest = (LPTVITEMDATA)GetItemData(hDestItem);
366 GetShellPidl().SHPidlToPathEx(plvit_src->lpifq,sFromPath);
367 GetShellPidl().SHPidlToPathEx(plvit_dest->lpifq,sToPath);
369 TCHAR szFrom[MAX_PATH+1];
370 TCHAR szTo[MAX_PATH+1];
371 ZeroMemory(szFrom,sizeof(szFrom));
372 lstrcpy(szFrom,sFromPath);
373 ZeroMemory(szTo,sizeof(szTo));
374 lstrcpy(szTo,sToPath);
375 ZeroMemory(&shf,sizeof(shf));
376 shf.hwnd = GetSafeHwnd();
380 return SHFileOperation(&shf) == 0 ? true : false;
383 BOOL CIEShellTreeCtrl::TransferItem(HTREEITEM hitemDrag, HTREEITEM hitemDrop)
385 return SHMoveFile(hitemDrag,hitemDrop) ? TRUE : FALSE;
388 bool CIEShellTreeCtrl::GetFolderInfo(HTREEITEM hItem,CString &sPath,CString &sName)
390 LPTVITEMDATA lptvid = (LPTVITEMDATA)GetItemData(hItem);
393 GetShellPidl().SHPidlToPathEx(lptvid->lpifq,sPath,NULL,SHGDN_NORMAL);
394 GetShellPidl().GetDisplayName(lptvid->lpi,sName);
398 bool CIEShellTreeCtrl::LoadFolderItems(LPCTSTR pszPath)
400 return LoadItems(pszPath);
403 CRefresh *CIEShellTreeCtrl::CreateRefreshObject(HTREEITEM hItem,LPARAM lParam)
405 CRefreshShellFolder *pRefresh=NULL;
408 pRefresh = new CRefreshShellFolder(hItem,lParam);
413 bool CIEShellTreeCtrl::Expanding(NM_TREEVIEW *nmtvw)
415 if ((nmtvw->itemNew.state & TVIS_EXPANDEDONCE) || nmtvw->itemNew.hItem == GetRootItem())
418 LPSHELLFOLDER pFolder=NULL;
419 CUIListCtrlData *pData = (CUIListCtrlData*)nmtvw->itemNew.lParam;
421 ASSERT_KINDOF(CUIListCtrlData,pData);
422 LPTVITEMDATA lptvid=(LPTVITEMDATA)pData->GetExtData();
425 HRESULT hr=lptvid->lpsfParent->BindToObject(lptvid->lpi,
426 0, IID_IShellFolder,(LPVOID*)&pFolder);
430 if (!AddItems(nmtvw->itemNew.hItem,pFolder))
431 SetButtonState(nmtvw->itemNew.hItem);
435 // prevent from expanding
439 BEGIN_MESSAGE_MAP(CIEShellTreeCtrl, CIEFolderTreeCtrl)
440 //{{AFX_MSG_MAP(CIEShellTreeCtrl)
441 ON_MESSAGE(WM_SETMESSAGESTRING,OnSetmessagestring)
442 ON_MESSAGE(WM_APP_CB_IE_HIT_ENTER,OnAppCbIeHitEnter)
443 // NOTE - the ClassWizard will add and remove mapping macros here.
446 ON_MESSAGE(WM_APP_POPULATE_TREE,OnAppPopulateTree)
447 ON_MESSAGE(WM_APP_CB_IE_SEL_CHANGE,OnCBIESelChange)
448 ON_MESSAGE(WM_APP_DIR_CHANGE_EVENT,OnAppDirChangeEvent)
452 /////////////////////////////////////////////////////////////////////////////
453 // CIEShellTreeCtrl message handlers
454 BOOL CIEShellTreeCtrl::OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult )
456 if ((message == WM_MEASUREITEM || message == WM_DRAWITEM && wParam == 0) || message == WM_INITMENUPOPUP)
460 return GetShellPidl().HandleMenuMsg(m_hWnd,m_lptvid->lpsfParent, m_lptvid->lpi,
461 message,wParam,lParam);
464 return CIEFolderTreeCtrl::OnWndMsg(message, wParam, lParam, pResult );
467 void CIEShellTreeCtrl::PreSubclassWindow()
469 CIEFolderTreeCtrl::PreSubclassWindow();
470 CreateFileChangeThreads(GetSafeHwnd());
473 /////////////////////////////////////////////////////////////////////////
474 // Thread function for detecting file system changes
475 UINT CIEShellTreeCtrl::ThreadFunc (LPVOID pParam)
477 ///////////////////////////////
478 PDC_THREADINFO pThreadInfo = (PDC_THREADINFO) pParam;
479 HANDLE hEvent = pThreadInfo->hEvent;
480 CIEShellTreeCtrl *pTreeCtrl = pThreadInfo->pTreeCtrl;
481 HWND hWnd = pTreeCtrl->GetSafeHwnd();
482 TCHAR szPath[MAX_PATH];
483 lstrcpy(szPath,pThreadInfo->sPath);
485 ////////////////////////////////////
487 // Get a handle to a file change notification object.
488 TRACE(_T("Creating directory thread handler for %s\n"),szPath);
489 HANDLE hDirChange = ::FindFirstChangeNotification (szPath,TRUE,FILE_NOTIFY_CHANGE_DIR_NAME);
491 // Return now if ::FindFirstChangeNotification failed.
492 if (hDirChange == INVALID_HANDLE_VALUE)
494 const int nHandles=2;
495 HANDLE aHandles[nHandles];
496 aHandles[0] = hDirChange;
497 aHandles[1] = hEvent;
498 BOOL bContinue = TRUE;
500 // Sleep until a file change notification wakes this thread or
501 // m_event becomes set indicating it's time for the thread to end.
504 TRACE(_T("TreeControl waiting for %u multiple objects\n"),nHandles);
505 DWORD dw = ::WaitForMultipleObjects (nHandles, aHandles, FALSE, INFINITE);
506 if (dw - WAIT_OBJECT_0 == 0)
507 { // Respond to a change notification.
508 ::FindNextChangeNotification (hDirChange);
509 TRACE(_T("-- Directory notify event was fired in CIEShellTreeCtrl --\n"));
510 if (pTreeCtrl->RefreshAllowed())
512 ::PostMessage (hWnd, WM_APP_DIR_CHANGE_EVENT,0,0);
516 TRACE(_T("but not sending as refresh disallowed\n"));
517 pTreeCtrl->SetRefreshAllowed(true);
518 TRACE(_T("Refresh is now allowed\n"));
521 else if(dw - WAIT_OBJECT_0 == 1)
524 TRACE(_T("Directory Notify Thread was signalled to stop\n"));
528 // Close the file change notification handle and return.
529 ::FindCloseChangeNotification (hDirChange);
530 TRACE(_T("Directory Notify Thread is ending\n"));
534 void CIEShellTreeCtrl::OnDestroy()
537 CIEFolderTreeCtrl::OnDestroy();
540 LRESULT CIEShellTreeCtrl::OnAppDirChangeEvent(WPARAM wParam, LPARAM lParam)
542 if (!RefreshAllowed())
546 GetParent()->SendMessage(WM_APP_UPDATE_ALL_VIEWS,(WPARAM)HINT_SHELL_DIR_CHANGED,(LPARAM)(LPCTSTR)GetRootPath());
550 LRESULT CIEShellTreeCtrl::OnCBIESelChange(WPARAM wParam,LPARAM lParam)
552 LPITEMIDLIST pidl = (LPITEMIDLIST)wParam;
553 ExpandMyComputer(pidl);
555 GetShellPidl().FreePidl(pidl);
560 // Selection has changed
561 void CIEShellTreeCtrl::UpdateEvent(LPARAM lHint,CObject *pHint)
563 // TODO: Add your specialized code here and/or call the base class
564 // Notify the combo box
565 const CRefreshShellFolder *pRefresh = static_cast<CRefreshShellFolder*>(pHint);
566 LPTVITEMDATA lptvid = reinterpret_cast<LPTVITEMDATA>(pRefresh->GetItemData());
569 RefreshComboBox(lptvid);
570 // Notify list control
573 ::SendMessage(m_hListWnd,WM_APP_UPDATE_ALL_VIEWS,(WPARAM)lHint,(LPARAM)pHint);
576 // or let base class handle it
577 CUITreeCtrl::UpdateEvent(lHint,pHint);
580 LRESULT CIEShellTreeCtrl::OnAppPopulateTree(WPARAM wParam, LPARAM lParam)
582 TRACE0("Selecting root item\n");
583 HTREEITEM hRoot = GetRootItem();
588 SelectionChanged(hRoot,GetItemData(hRoot));
590 LPTVITEMDATA lptvid = reinterpret_cast<LPTVITEMDATA>(GetItemData(hRoot));
591 RefreshComboBox(lptvid);
596 void CIEShellTreeCtrl::DeleteKey(HTREEITEM hItem)
598 // TODO: Add your specialized code here and/or call the base class
600 ZeroMemory(&shf,sizeof(shf));
601 TCHAR szFrom[MAX_PATH+1];
602 ZeroMemory(szFrom,sizeof(szFrom)*sizeof(TCHAR));
603 lstrcpy(szFrom,GetPathName(hItem));
604 shf.hwnd = GetSafeHwnd();
605 shf.wFunc = FO_DELETE;
607 shf.fFlags = GetKeyState(VK_SHIFT) < 0 ? 0 : FOF_ALLOWUNDO;
608 SHFileOperation(&shf);
611 void CIEShellTreeCtrl::DoubleClick(HTREEITEM hItem)
613 // TODO: Add your specialized code here and/or call the base class
617 void CIEShellTreeCtrl::GoBack(HTREEITEM hItem)
619 // TODO: Add your specialized code here and/or call the base class
623 void CIEShellTreeCtrl::ShowPopupMenu(HTREEITEM hItem,CPoint point)
625 // TODO: Add your specialized code here and/or call the base class
628 CUITreeCtrl::ShowPopupMenu(hItem,point);
630 // TODO: Add your control notification handler code here
631 m_lptvid = (LPTVITEMDATA)GetItemData(hItem);
633 GetShellPidl().PopupTheMenu(m_hWnd,m_lptvid->lpsfParent, &m_lptvid->lpi, 1, &point);
636 void CIEShellTreeCtrl::ShowProperties(HTREEITEM hItem)
638 // TODO: Add your specialized code here and/or call the base class
639 ShellExecute(hItem,_T("properties"));
642 LRESULT CIEShellTreeCtrl::OnAppCbIeHitEnter(WPARAM wParam, LPARAM lParam)
646 LPCTSTR pszPath = (LPCTSTR)lParam;
647 LPITEMIDLIST pidl=NULL;
648 GetShellPidl().SHPathToPidlEx(pszPath,&pidl,NULL);
651 int nCount = GetShellPidl().GetCount(pidl);
652 LPITEMIDLIST pidlPart=NULL;
653 LPITEMIDLIST pidlFull=NULL;
654 HTREEITEM hItem=NULL;
656 for(int i=0;i < nCount;i++)
658 pidlPart=GetShellPidl().CopyItemID(pidl,i+1);
661 pidlFull = GetShellPidl().ConcatPidl(pidlFull,pidlPart);
662 hItem = ExpandPidl(pidlFull);
665 GetShellPidl().FreePidl(pidlPart);
668 if (hItem && GetShellPidl().ComparePidls(NULL,pidl,pidlFull))
670 Select(hItem,TVGN_CARET);
675 sMess.Format(_T("%s was not found"),pszPath);
676 AfxMessageBox(sMess,MB_ICONSTOP);
679 GetShellPidl().FreePidl(pidl);
681 GetShellPidl().FreePidl(pidlFull);
686 LRESULT CIEShellTreeCtrl::OnSetmessagestring(WPARAM wParam, LPARAM lParam)
689 return GetParent()->SendMessage(WM_SETMESSAGESTRING,wParam,lParam);