1 //////////////////////////////////////////////////////////
\r
3 // Definitions for the CMyListView and ListItemData classes
\r
6 #include "ExplorerApp.h"
\r
7 #include "MyListView.h"
\r
8 #include "resource.h"
\r
10 ////////////////////////////////
\r
11 //CMyListView function definitions
\r
12 int CALLBACK CMyListView::CompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
14 UNREFERENCED_PARAMETER(lParamSort);
\r
15 ListItemData* pItem1 = (ListItemData*)lParam1;
\r
16 ListItemData* pItem2 = (ListItemData*)lParam2;
\r
18 HRESULT hr = pItem1->GetParentFolder().CompareIDs(0, pItem1->GetRelPidl(), pItem2->GetRelPidl());
\r
23 return (short)SCODE_CODE(GetScode(hr));
\r
26 CMyListView::CMyListView()
\r
31 CMyListView::~CMyListView()
\r
36 void CMyListView::DeleteItems()
\r
41 void CMyListView::DisplayFolder(CShellFolder& cParentFolder, Cpidl& cpidlFull, Cpidl& cpidlRel)
\r
43 m_cpidlCurFull = cpidlFull;
\r
44 if(cParentFolder.GetIShellFolder())
\r
45 cParentFolder.BindToObject(cpidlRel, NULL, IID_IShellFolder, m_csfCurFolder);
\r
47 m_csfCurFolder.SHGetDesktopFolder();
\r
52 void CMyListView::DoBackgroundMenu(CPoint& ptScreen)
\r
55 if(m_csfCurFolder.GetIShellFolder())
\r
58 hr = m_csfCurFolder.CreateViewObject(GetParent()->GetHwnd(), IID_IContextMenu, ccm);
\r
63 Popup.CreatePopupMenu();
\r
64 if(Popup.GetHandle())
\r
67 UINT idCmdFirst = 0;
\r
70 //find the largest ID in the menu
\r
71 while((idCmd = Popup.GetMenuItemID(i)) != (UINT)-1)
\r
73 if(idCmd > idCmdFirst)
\r
78 hr = ccm.QueryContextMenu(Popup, 0, ++idCmdFirst, (UINT)-1, CMF_NORMAL | CMF_EXPLORE);
\r
82 ccm.QueryInterface(IID_IContextMenu2, m_ccm2);
\r
84 idCmd = Popup.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
\r
85 ptScreen.x, ptScreen.y, this, NULL);
\r
89 CMINVOKECOMMANDINFO cmi = {0};
\r
90 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
\r
91 cmi.hwnd = ::GetParent(m_hWnd);
\r
92 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - idCmdFirst);
\r
93 cmi.nShow = SW_SHOWNORMAL;
\r
94 ccm.InvokeCommand(cmi);
\r
96 //The operation performed by the context menu may have
\r
97 // changed the contents of the folder, so do a refresh.
\r
108 void CMyListView::DoContextMenu(CPoint& ptScreen)
\r
110 LVHITTESTINFO lvhti;
\r
111 lvhti.pt = ptScreen;
\r
112 ScreenToClient(lvhti.pt);
\r
113 lvhti.flags = LVHT_NOWHERE;
\r
116 if(LVHT_ONITEM & lvhti.flags)
\r
118 //get the selected items
\r
119 UINT nItems = GetSelectedCount();
\r
120 std::vector<int> vItems(nItems, 0);
\r
121 int* pItemArray = &vItems.front();
\r
128 //put the item clicked on first in the list
\r
129 pItemArray[0] = lvhti.iItem;
\r
131 for(i = 1, nCurItem = -1; i < nItems; ++i)
\r
133 nCurItem = GetNextItem(nCurItem, LVNI_SELECTED);
\r
134 if(nCurItem != lvhti.iItem)
\r
135 pItemArray[i] = nCurItem;
\r
140 DoItemMenu(pItemArray, nItems, ptScreen);
\r
144 DoBackgroundMenu(ptScreen);
\r
147 void CMyListView::DoDefault(int iItem)
\r
149 LVITEM lvItem = {0};
\r
150 lvItem.mask = LVIF_PARAM;
\r
151 lvItem.iItem = iItem;
\r
153 if(GetItem(lvItem))
\r
156 ListItemData* pInfo = (ListItemData*)lvItem.lParam;
\r
157 CShellFolder csFolder;
\r
159 csFolder = pInfo->GetParentFolder();
\r
161 if (!csFolder.GetIShellFolder())
\r
162 csFolder.SHGetDesktopFolder();
\r
164 if(csFolder.GetIShellFolder())
\r
166 Cpidl* pCpidl = &(pInfo->GetRelPidl());
\r
167 hr = csFolder.GetUIObjectOf(m_hWnd, 1, pCpidl, IID_IContextMenu, 0, ccm);
\r
172 Popup.CreatePopupMenu();
\r
173 if(Popup.GetHandle())
\r
175 hr = ccm.QueryContextMenu(Popup, 0, 1, 0x7fff, CMF_DEFAULTONLY | CMF_EXPLORE);
\r
179 UINT idCmd = Popup.GetMenuItemID(0);
\r
180 if(idCmd && (idCmd != (UINT)-1))
\r
182 //determine if the item is a folder
\r
183 ULONG ulAttr = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;
\r
184 csFolder.GetAttributesOf(1, pInfo->GetRelPidl(), ulAttr);
\r
186 if ((ulAttr & SFGAO_HASSUBFOLDER) || (ulAttr &SFGAO_FOLDER))
\r
188 GetExplorerApp().GetMainFrame().GetTreeView()->SelectFromListView(pInfo->GetFullPidl());
\r
192 CMINVOKECOMMANDINFO cmi = {0};
\r
193 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
\r
194 cmi.hwnd = ::GetParent(m_hWnd);
\r
195 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - 1);
\r
196 cmi.nShow = SW_SHOWNORMAL;
\r
197 ccm.InvokeCommand(cmi);
\r
207 void CMyListView::DoDisplay()
\r
212 if(m_csfCurFolder.GetIShellFolder())
\r
214 HCURSOR hCur = ::LoadCursor(NULL, IDC_WAIT);
\r
215 hCur = ::SetCursor(hCur);
\r
217 //turn redawing off in the ListView. This will speed things up as we add items
\r
218 SendMessage(WM_SETREDRAW, FALSE, 0);
\r
220 EnumObjects(m_csfCurFolder, m_cpidlCurFull);
\r
221 SortItems(CompareProc, 0);
\r
223 //turn redawing back on
\r
224 SendMessage(WM_SETREDRAW, TRUE, 0);
\r
229 void CMyListView::DoItemMenu(LPINT piItems, UINT cbItems, CPoint& ptScreen)
\r
231 std::vector<Cpidl> vpidl(cbItems);
\r
232 Cpidl* pidlArray = &vpidl.front();
\r
234 for(UINT i = 0; i < cbItems; ++i)
\r
236 LVITEM lvItem = {0};
\r
237 lvItem.mask = LVIF_PARAM;
\r
238 lvItem.iItem = piItems[i];
\r
239 if(GetItem(lvItem))
\r
241 ListItemData* pInfo = (ListItemData*)lvItem.lParam;
\r
242 pidlArray[i] = pInfo->GetRelPidl();
\r
246 if(pidlArray[0].GetPidl())
\r
251 if(m_csfCurFolder.GetIShellFolder())
\r
253 hr = m_csfCurFolder.GetUIObjectOf(m_hWnd, cbItems, pidlArray, IID_IContextMenu, 0, ccm);
\r
258 Popup.CreatePopupMenu();
\r
259 if(Popup.GetHandle())
\r
261 hr = ccm.QueryContextMenu(Popup, 0, 1, 0x7fff, CMF_NORMAL | CMF_EXPLORE);
\r
264 ccm.QueryInterface(IID_IContextMenu2, m_ccm2);
\r
266 idCmd = Popup.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
\r
267 ptScreen.x, ptScreen.y, this, NULL);
\r
271 CMINVOKECOMMANDINFO cmi = {0};
\r
272 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
\r
273 cmi.hwnd = ::GetParent(m_hWnd);
\r
274 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - 1);
\r
275 cmi.nShow = SW_SHOWNORMAL;
\r
276 ccm.InvokeCommand(cmi);
\r
278 // The operation performed by the context menu may have changed
\r
279 // the contents of the folder, so do a refresh.
\r
291 LRESULT CMyListView::OnNotifyReflect(WPARAM, LPARAM lParam)
\r
293 LPNMHDR pnmh = (LPNMHDR) lParam;
\r
300 ::GetCursorPos(&ptScreen);
\r
301 DoContextMenu(ptScreen);
\r
304 case LVN_GETDISPINFO:
\r
306 NMLVDISPINFO* pdi = (NMLVDISPINFO*)lParam;
\r
307 ListItemData* pItem = (ListItemData*)pdi->item.lParam;
\r
309 //do we need to supply the text?
\r
310 if(pdi->item.mask & LVIF_TEXT)
\r
312 TCHAR szFileName[MAX_PATH];
\r
313 GetFullFileName(pItem->GetFullPidl().GetPidl(), szFileName);
\r
315 ULONG ulAttr = SFGAO_CANDELETE | SFGAO_FOLDER;
\r
316 pItem->GetParentFolder().GetAttributesOf(1, pItem->GetRelPidl(), ulAttr);
\r
320 //A trick to help get a quick response from CreateFile
\r
321 if (ulAttr & SFGAO_CANDELETE)
\r
322 hFile = ::CreateFile (szFileName, 0, FILE_SHARE_READ, NULL,
\r
323 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
\r
325 hFile = ::CreateFile (szFileName, 0, FILE_SHARE_READ, NULL,
\r
326 0, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
\r
329 int nMaxLength = sizeof(szText)/sizeof(szText[0])-1;
\r
331 switch(pdi->item.iSubItem)
\r
335 SHFILEINFO sfi = {0};
\r
336 //get the display name of the item
\r
337 if (pItem->GetFullPidl().SHGetFileInfo(0, sfi, SHGFI_PIDL | SHGFI_DISPLAYNAME))
\r
338 ::lstrcpyn(pdi->item.pszText, sfi.szDisplayName, pdi->item.cchTextMax -1);
\r
345 //report the size files and not folders
\r
346 if ((hFile != INVALID_HANDLE_VALUE)&&(~ulAttr & SFGAO_FOLDER))
\r
348 GetFileSizeText(hFile, szSize);
\r
349 ::lstrcpyn(pdi->item.pszText, szSize, nMaxLength -1);
\r
352 ::lstrcpy(pdi->item.pszText, _T(""));
\r
357 SHFILEINFO sfi = {0};
\r
358 if(pItem->GetFullPidl().SHGetFileInfo(0, sfi, SHGFI_PIDL | SHGFI_TYPENAME))
\r
359 ::lstrcpyn(pdi->item.pszText, sfi.szTypeName, pdi->item.cchTextMax -1);
\r
364 if (hFile != INVALID_HANDLE_VALUE)
\r
366 GetLastWriteTime(hFile, szText);
\r
367 ::lstrcpyn(pdi->item.pszText, szText, nMaxLength -1);
\r
370 ::lstrcpy(pdi->item.pszText, _T(""));
\r
374 if (hFile != INVALID_HANDLE_VALUE)
\r
375 ::CloseHandle(hFile);
\r
378 //do we need to supply the unselected image?
\r
379 if(pdi->item.mask & LVIF_IMAGE)
\r
381 SHFILEINFO sfi = {0};
\r
383 //get the unselected image for this item
\r
384 if(pItem->GetFullPidl().SHGetFileInfo(0, sfi, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON))
\r
385 pdi->item.iImage = sfi.iIcon;
\r
393 //get the item that has the focus
\r
394 int nItem = (int)::SendMessage(m_hWnd, LVM_GETNEXTITEM, (WPARAM) -1, (LPARAM) MAKELPARAM (LVNI_FOCUSED, 0));
\r
404 void CMyListView::EnumObjects(CShellFolder& cPFolder, Cpidl& cpidlParent)
\r
408 int grFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
\r
409 if ( GetExplorerApp().GetMainFrame().GetShowHidden() )
\r
410 grFlags |= SHCONTF_INCLUDEHIDDEN;
\r
412 if(SUCCEEDED(cPFolder.EnumObjects(NULL, grFlags, cEnum)))
\r
414 ULONG ulFetched = 1;
\r
417 //enumerate the item's PIDLs
\r
418 while(S_OK == (cEnum.Next(1, cpidlRel, ulFetched)) && ulFetched)
\r
420 LVITEM lvItem = {0};
\r
423 //fill in the TV_ITEM structure for this item
\r
424 lvItem.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
\r
426 //Store a pointer to the ListItemData in the lParam and m_pItems
\r
427 ListItemData* pItem = new ListItemData(cpidlParent, cpidlRel, cPFolder);
\r
428 lvItem.lParam = (LPARAM)pItem;
\r
429 m_pItems.push_back(pItem);
\r
431 //text and images are done on a callback basis
\r
432 lvItem.pszText = LPSTR_TEXTCALLBACK;
\r
433 lvItem.iImage = I_IMAGECALLBACK;
\r
435 //determine if the item's icon characteristics
\r
436 ulAttr = SFGAO_SHARE | SFGAO_LINK | SFGAO_GHOSTED;
\r
437 cPFolder.GetAttributesOf(1, cpidlRel, ulAttr);
\r
439 if(ulAttr & SFGAO_SHARE)
\r
441 lvItem.mask |= LVIF_STATE;
\r
442 lvItem.stateMask |= LVIS_OVERLAYMASK;
\r
443 lvItem.state |= INDEXTOOVERLAYMASK(1); //1 is the index for the shared overlay image
\r
446 if (ulAttr & SFGAO_LINK)
\r
448 lvItem.mask |= LVIF_STATE;
\r
449 lvItem.stateMask |= LVIS_OVERLAYMASK;
\r
450 lvItem.state |= INDEXTOOVERLAYMASK(2); //2 is the index for the link overlay image
\r
453 if(ulAttr & SFGAO_GHOSTED)
\r
455 lvItem.mask |= LVIF_STATE;
\r
456 lvItem.stateMask |= LVIS_CUT;
\r
457 lvItem.state |= LVIS_CUT;
\r
460 InsertItem(lvItem);
\r
466 BOOL CMyListView::GetFileSizeText(HANDLE hFile, LPTSTR lpszSize)
\r
468 DWORD dwFileSizeLo;
\r
469 DWORD dwFileSizeHi;
\r
472 CString strPreFormat;
\r
473 CString strPostFormat;
\r
475 dwFileSizeLo = ::GetFileSize (hFile, &dwFileSizeHi);
\r
476 ldwSize = ((DWORDLONG) dwFileSizeHi)<<32 | dwFileSizeLo;
\r
477 strPreFormat.Format(_T("%d"), ((1023 + ldwSize)>>10));
\r
479 //Convert our number string using Locale information
\r
480 ::GetNumberFormat(LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, strPreFormat, NULL, strPostFormat.GetBuffer(nMaxSize), nMaxSize);
\r
481 strPostFormat.ReleaseBuffer();
\r
483 //Get our decimal point character from Locale information
\r
484 int nBuffLen = ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, NULL, 0 );
\r
485 assert(nBuffLen > 0);
\r
487 ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, Decimal.GetBuffer(nBuffLen), nBuffLen);
\r
488 Decimal.ReleaseBuffer();
\r
490 //Truncate at the "decimal" point
\r
491 int nPos = strPostFormat.Find(Decimal);
\r
493 strPostFormat = strPostFormat.Left(nPos);
\r
495 strPostFormat += _T(" KB");
\r
496 lstrcpyn(lpszSize, strPostFormat, nMaxSize);
\r
500 BOOL CMyListView::GetLastWriteTime(HANDLE hFile, LPTSTR lpszString)
\r
502 FILETIME ftCreate, ftAccess, ftWrite;
\r
503 SYSTEMTIME stLocal;
\r
508 // Retrieve the file times for the file.
\r
509 if (!::GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
\r
512 // Convert the last-write time to local time.
\r
513 ::FileTimeToLocalFileTime(&ftWrite, &ftLocal);
\r
514 ::FileTimeToSystemTime(&ftLocal, &stLocal);
\r
516 // Build a string showing the date and time with regional settings.
\r
517 ::GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &stLocal, NULL, szDate, 31);
\r
518 ::GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &stLocal, NULL, szTime, 31);
\r
520 ::lstrcpy(lpszString, szDate);
\r
521 ::lstrcat(lpszString, _T(" "));
\r
522 ::lstrcat(lpszString, szTime);
\r
527 HIMAGELIST CMyListView::GetImageList(BOOL bLarge)
\r
530 return m_hLargeImageList;
\r
532 return m_hSmallImageList;
\r
535 void CMyListView::OnInitialUpdate()
\r
537 //Set the image lists
\r
538 SendMessage(LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM) GetImageList(TRUE));
\r
539 SendMessage(LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) GetImageList(FALSE));
\r
541 //Set up the colmns for report mode
\r
543 LVCOLUMN lvc = {0};
\r
544 lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
\r
545 int iColumns = 4; //Number of columns
\r
546 int ColSize[4] = {150, 70, 100, 120}; // width of columns in pixels
\r
548 for (int iCol = 0; iCol < iColumns; iCol++)
\r
550 lvc.iSubItem = iCol;
\r
551 lvc.pszText = szText;
\r
552 lvc.cx = ColSize[iCol];
\r
554 if (1 == iCol) lvc.fmt = LVCFMT_RIGHT; // right-aligned column
\r
555 else lvc.fmt = LVCFMT_LEFT; //left-aligned column
\r
557 ::LoadString(GetApp()->GetInstanceHandle(), IDS_COLUMN1 + iCol,
\r
558 szText, sizeof(szText)/sizeof(szText[0]));
\r
560 InsertColumn(iCol, lvc);
\r
563 //Set initial the view style as report
\r
567 void CMyListView::PreCreate(CREATESTRUCT &cs)
\r
569 cs.style = WS_TABSTOP | WS_CHILD | WS_VISIBLE | LVS_AUTOARRANGE |
\r
570 LVS_ICON | LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
\r
571 cs.dwExStyle = WS_EX_CLIENTEDGE;
\r
574 void CMyListView::SetImageLists()
\r
578 // Get the system image list
\r
579 m_hLargeImageList = (HIMAGELIST)::SHGetFileInfo(_T("C:\\"), 0, &sfi,
\r
580 sizeof(SHFILEINFO), SHGFI_SYSICONINDEX);
\r
582 m_hSmallImageList = (HIMAGELIST)::SHGetFileInfo(_T("C:\\"), 0, &sfi,
\r
583 sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
\r
586 void CMyListView::ViewLargeIcons()
\r
588 DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
\r
589 SetWindowLongPtr(GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_ICON );
\r
592 void CMyListView::ViewSmallIcons()
\r
594 DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
\r
595 SetWindowLongPtr(GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_SMALLICON);
\r
598 void CMyListView::ViewList()
\r
600 DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
\r
601 SetWindowLongPtr(GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_LIST);
\r
604 void CMyListView::ViewReport()
\r
606 DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE);
\r
607 SetWindowLongPtr(GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | LVS_REPORT);
\r
610 LRESULT CMyListView::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
\r
616 case WM_MEASUREITEM:
\r
617 case WM_INITMENUPOPUP:
\r
619 if(m_ccm2.GetIContextMenu2())
\r
620 m_ccm2.HandleMenuMsg(uMsg, wParam, lParam);
\r
626 m_csfCurFolder.Delete();
\r
630 return WndProcDefault(uMsg, wParam, lParam);
\r
633 ///////////////////////////////////
\r
634 //ListItemData function definitions
\r
635 CMyListView::ListItemData::ListItemData(Cpidl& cpidlParent, Cpidl& cpidlRel, CShellFolder& cParentFolder)
\r
637 m_cParentFolder = cParentFolder;
\r
638 m_cpidlFull = cpidlParent + cpidlRel;
\r
639 m_cpidlRel = cpidlRel;
\r
642 CMyListView::ListItemData::~ListItemData()
\r