Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

736 lines
14 KiB

  1. //*******************************************************************************************
  2. //
  3. // Filename : SFVWnd.cpp
  4. //
  5. // Implementation file for CSFVDropSource and CSFViewDlg
  6. //
  7. // Copyright (c) 1994 - 1996 Microsoft Corporation. All rights reserved
  8. //
  9. //*******************************************************************************************
  10. #include "Pch.H"
  11. #include "SFVWnd.H"
  12. #include "ThisDll.h"
  13. #include "Resource.H"
  14. class CSFVDropSource : public CUnknown, public IDropSource
  15. {
  16. public:
  17. CSFVDropSource() : m_grfInitialKeyState(0) {}
  18. virtual ~CSFVDropSource();
  19. STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
  20. STDMETHODIMP_(ULONG) AddRef();
  21. STDMETHODIMP_(ULONG) Release();
  22. STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);
  23. STDMETHODIMP GiveFeedback(DWORD dwEffect);
  24. private:
  25. DWORD m_grfInitialKeyState;
  26. } ;
  27. CSFVDropSource::~CSFVDropSource()
  28. {
  29. }
  30. STDMETHODIMP CSFVDropSource::QueryInterface(REFIID riid, LPVOID * ppvObj)
  31. {
  32. static const IID *apiid[] = { &IID_IDropSource, NULL };
  33. LPUNKNOWN aobj[] = { (IDropSource *)this };
  34. return(QIHelper(riid, ppvObj, apiid, aobj));
  35. }
  36. STDMETHODIMP_(ULONG) CSFVDropSource::AddRef()
  37. {
  38. return(AddRefHelper());
  39. }
  40. STDMETHODIMP_(ULONG) CSFVDropSource::Release()
  41. {
  42. return(ReleaseHelper());
  43. }
  44. STDMETHODIMP CSFVDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
  45. {
  46. if (fEscapePressed)
  47. {
  48. return(DRAGDROP_S_CANCEL);
  49. }
  50. // initialize ourself with the drag begin button
  51. if (m_grfInitialKeyState == 0)
  52. {
  53. m_grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
  54. }
  55. if (!(grfKeyState & m_grfInitialKeyState))
  56. {
  57. return(DRAGDROP_S_DROP);
  58. }
  59. return(S_OK);
  60. }
  61. STDMETHODIMP CSFVDropSource::GiveFeedback(DWORD dwEffect)
  62. {
  63. return(DRAGDROP_S_USEDEFAULTCURSORS);
  64. }
  65. // Note that the OLESTR gets freed, so don't try to use it later
  66. BOOL StrRetToStr(LPSTR szOut, UINT uszOut, LPSTRRET pStrRet, LPCITEMIDLIST pidl)
  67. {
  68. switch (pStrRet->uType)
  69. {
  70. case STRRET_OLESTR:
  71. {
  72. CSafeMalloc sm;
  73. OleStrToStrN(szOut, uszOut, pStrRet->pOleStr, -1);
  74. sm.Free(pStrRet->pOleStr);
  75. break;
  76. }
  77. case STRRET_CSTR:
  78. lstrcpyn(szOut, pStrRet->cStr, uszOut);
  79. break;
  80. case STRRET_OFFSET:
  81. if (pidl)
  82. {
  83. lstrcpyn(szOut, ((LPCSTR)&pidl->mkid)+pStrRet->uOffset, uszOut);
  84. break;
  85. }
  86. // Fall through
  87. default:
  88. if (uszOut)
  89. {
  90. *szOut = '\0';
  91. }
  92. return(FALSE);
  93. }
  94. return(TRUE);
  95. }
  96. static void _PrettyMenu(HMENU hm)
  97. {
  98. BOOL bSeparated = TRUE;
  99. int i;
  100. for (i=GetMenuItemCount(hm)-1; i>0; --i)
  101. {
  102. if (CSFViewDlg::IsMenuSeparator(hm, i))
  103. {
  104. if (bSeparated)
  105. {
  106. DeleteMenu(hm, i, MF_BYPOSITION);
  107. }
  108. bSeparated = TRUE;
  109. }
  110. else
  111. {
  112. bSeparated = FALSE;
  113. }
  114. }
  115. // The above loop does not handle the case of many separators at
  116. // the beginning of the menu
  117. while (CSFViewDlg::IsMenuSeparator(hm, 0))
  118. {
  119. DeleteMenu(hm, 0, MF_BYPOSITION);
  120. }
  121. }
  122. static HMENU _LoadPopupMenu(UINT id, UINT uSubMenu)
  123. {
  124. HMENU hmParent = LoadMenu(g_ThisDll.GetInstance(), MAKEINTRESOURCE(id));
  125. if (!hmParent)
  126. {
  127. return(NULL);
  128. }
  129. HMENU hmPopup = GetSubMenu(hmParent, 0);
  130. RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION);
  131. DestroyMenu(hmParent);
  132. return(hmPopup);
  133. }
  134. void CSFViewDlg::InitDialog()
  135. {
  136. m_cList.Init(GetDlgItem(m_hDlg, IDC_LISTVIEW), GetDlgItem(m_hDlg, IDC_LISTBOX),
  137. IDI_GENERIC);
  138. SetWindowLong(m_cList, GWL_EXSTYLE,
  139. GetWindowLong(m_cList, GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
  140. m_hrOLE = OleInitialize(NULL);
  141. }
  142. LRESULT CSFViewDlg::BeginDrag()
  143. {
  144. if (!OleInited())
  145. {
  146. return(0);
  147. }
  148. // Get the dwEffect from the selection.
  149. ULONG dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
  150. GetAttributesFromItem(&dwEffect, SVGIO_SELECTION);
  151. // Just in case
  152. dwEffect &= DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
  153. if (!dwEffect)
  154. {
  155. return(0);
  156. }
  157. LPDATAOBJECT pdtobj;
  158. if (FAILED(GetUIObjectFromItem(IID_IDataObject, (LPVOID*)&pdtobj, SVGIO_SELECTION)))
  159. {
  160. return(0);
  161. }
  162. CEnsureRelease erData(pdtobj);
  163. CSFVDropSource *pcsrc = new CSFVDropSource;
  164. if (!pcsrc)
  165. {
  166. return(0);
  167. }
  168. pcsrc->AddRef();
  169. CEnsureRelease erCDSrc((IDropSource*)pcsrc);
  170. IDropSource *pdsrc;
  171. HRESULT hres = pcsrc->QueryInterface(IID_IDropSource, (LPVOID*)&pdsrc);
  172. if (FAILED(hres))
  173. {
  174. return(0);
  175. }
  176. CEnsureRelease erPDSrc(pdsrc);
  177. DoDragDrop(pdtobj, pdsrc, dwEffect, &dwEffect);
  178. return(0);
  179. }
  180. BOOL CSFViewDlg::Notify(LPNMHDR pNotify)
  181. {
  182. LPNM_LISTVIEW pNotifyLV = (LPNM_LISTVIEW)pNotify;
  183. LV_DISPINFO *pNotifyDI = (LV_DISPINFO *)pNotify;
  184. LPTBNOTIFY pNotifyTB = (LPTBNOTIFY)pNotify;
  185. LPTOOLTIPTEXT pNotifyTT = (LPTOOLTIPTEXT)pNotify;
  186. switch(pNotify->code)
  187. {
  188. case TTN_NEEDTEXT:
  189. m_psfv->GetCommandHelpText(pNotifyTT->hdr.idFrom, pNotifyTT->szText,
  190. sizeof(pNotifyTT->szText), TRUE);
  191. break;
  192. case TBN_BEGINDRAG:
  193. m_psfv->OnMenuSelect(pNotifyTB->iItem, 0, 0);
  194. break;
  195. case LVN_DELETEITEM:
  196. m_psfv->m_cMalloc.Free((LPITEMIDLIST)pNotifyLV->lParam);
  197. break;
  198. case LVN_GETDISPINFO:
  199. if (pNotifyDI->item.iSubItem == 0)
  200. {
  201. LPCITEMIDLIST pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam;
  202. if (pNotifyDI->item.mask & LVIF_TEXT)
  203. {
  204. STRRET strret;
  205. if (FAILED(m_psfv->m_psf->GetDisplayNameOf(pidl, 0, &strret)))
  206. {
  207. lstrcpyn(pNotifyDI->item.pszText, "", pNotifyDI->item.cchTextMax);
  208. }
  209. else
  210. {
  211. StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax,
  212. &strret, pidl);
  213. }
  214. }
  215. if (pNotifyDI->item.mask & LVIF_IMAGE)
  216. {
  217. // Get the image
  218. pNotifyDI->item.iImage = m_cList.GetIcon(m_psfv->m_psf, pidl);
  219. }
  220. pNotifyDI->item.mask |= LVIF_DI_SETITEM;
  221. }
  222. else if (pNotifyDI->item.mask & LVIF_TEXT)
  223. {
  224. SFVCB_GETDETAILSOF_DATA gdo;
  225. gdo.pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam;
  226. if (m_psfv->CallCB(SFVCB_GETDETAILSOF, pNotifyDI->item.iSubItem, (LPARAM)&gdo)
  227. == S_OK)
  228. {
  229. StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax,
  230. &gdo.str, gdo.pidl);
  231. }
  232. }
  233. break;
  234. case LVN_COLUMNCLICK:
  235. m_psfv->ColumnClick(pNotifyLV->iSubItem);
  236. break;
  237. case LVN_ITEMCHANGED:
  238. // We only care about STATECHANGE messages
  239. if (!(pNotifyLV->uChanged & LVIF_STATE))
  240. {
  241. // If the text is changed, we need to flush the cached
  242. // context menu.
  243. if (pNotifyLV->uChanged & LVIF_TEXT)
  244. {
  245. m_psfv->ReleaseSelContextMenu();
  246. }
  247. break;
  248. }
  249. // tell commdlg that selection may have changed
  250. m_psfv->OnStateChange(CDBOSC_SELCHANGE);
  251. // The rest only cares about SELCHANGE messages
  252. if ((pNotifyLV->uNewState^pNotifyLV->uOldState) & LVIS_SELECTED)
  253. {
  254. m_psfv->ReleaseSelContextMenu();
  255. }
  256. break;
  257. case LVN_BEGINDRAG:
  258. case LVN_BEGINRDRAG:
  259. return(BeginDrag());
  260. case NM_RETURN:
  261. case NM_DBLCLK:
  262. ContextMenu((DWORD)-1, TRUE);
  263. break;
  264. default:
  265. return(FALSE);
  266. }
  267. return(TRUE);
  268. }
  269. void CSFViewDlg::ContextMenu(DWORD dwPos, BOOL bDoDefault)
  270. {
  271. int idDefault = -1;
  272. int nInsert;
  273. UINT fFlags = 0;
  274. POINT pt;
  275. // Find the selected item
  276. int iItem = ListView_GetNextItem(m_cList, -1, LVNI_SELECTED);
  277. if (dwPos == (DWORD) -1)
  278. {
  279. if (iItem != -1)
  280. {
  281. RECT rc;
  282. int iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED|LVNI_SELECTED);
  283. if (iItemFocus == -1)
  284. {
  285. iItemFocus = iItem;
  286. }
  287. // Note that ListView_GetItemRect returns client coordinates
  288. ListView_GetItemRect(m_cList, iItemFocus, &rc, LVIR_ICON);
  289. pt.x = (rc.left+rc.right)/2;
  290. pt.y = (rc.top+rc.bottom)/2;
  291. }
  292. else
  293. {
  294. pt.x = pt.y = 0;
  295. }
  296. MapWindowPoints(m_cList, HWND_DESKTOP, &pt, 1);
  297. }
  298. else
  299. {
  300. pt.x = GET_X_LPARAM(dwPos);
  301. pt.y = GET_Y_LPARAM(dwPos);
  302. }
  303. CMenuTemp cmContext(CreatePopupMenu());
  304. if (!(HMENU)cmContext)
  305. {
  306. // There should be an error message here
  307. return;
  308. }
  309. LPCONTEXTMENU pcm = NULL;
  310. if (iItem == -1)
  311. {
  312. // No selected item; use the background context menu
  313. nInsert = -1;
  314. CMenuTemp cmMerge(_LoadPopupMenu(MENU_SFV, 0));
  315. if (!(HMENU)cmMerge)
  316. {
  317. // There should be an error message here
  318. return;
  319. }
  320. Cab_MergeMenus(cmContext, cmMerge, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, 0);
  321. m_psfv->MergeArrangeMenu(cmContext);
  322. m_psfv->InitViewMenu(cmContext);
  323. if (FAILED(m_psfv->m_psf->CreateViewObject(m_psfv->m_hwndMain, IID_IContextMenu,
  324. (LPVOID *)&pcm)))
  325. {
  326. pcm = NULL;
  327. }
  328. }
  329. else
  330. {
  331. nInsert = 0;
  332. pcm = m_psfv->GetSelContextMenu();
  333. }
  334. CEnsureRelease erPCM(pcm);
  335. if (pcm)
  336. {
  337. if (m_psfv->m_psb)
  338. {
  339. // Determine whether we are in Explorer mode
  340. HWND hwnd = NULL;
  341. m_psfv->m_psb->GetControlWindow(FCW_TREE, &hwnd);
  342. if (hwnd)
  343. {
  344. fFlags |= CMF_EXPLORE;
  345. }
  346. }
  347. pcm->QueryContextMenu(cmContext, nInsert,
  348. SFV_CONTEXT_FIRST, SFV_CONTEXT_LAST, fFlags);
  349. // If this is the common dialog browser, we need to make the
  350. // default command "Select" so that double-clicking (which is
  351. // open in common dialog) makes sense.
  352. if (m_psfv->IsInCommDlg() && iItem!=-1)
  353. {
  354. // make sure this is an item
  355. CMenuTemp cmSelect(_LoadPopupMenu(MENU_SFV, 1));
  356. Cab_MergeMenus(cmContext, cmSelect, 0, 0, (UINT)-1, MM_ADDSEPARATOR);
  357. SetMenuDefaultItem(cmContext, 0, MF_BYPOSITION);
  358. }
  359. idDefault = GetMenuDefaultItem(cmContext, MF_BYCOMMAND, 0);
  360. }
  361. _PrettyMenu(cmContext);
  362. int idCmd;
  363. if (bDoDefault)
  364. {
  365. if (idDefault < 0)
  366. {
  367. return;
  368. }
  369. idCmd = idDefault;
  370. }
  371. else
  372. {
  373. idCmd = TrackPopupMenu(cmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  374. pt.x, pt.y, 0, m_psfv->m_cView, NULL);
  375. }
  376. if ((idCmd==idDefault) && m_psfv->OnDefaultCommand()==S_OK)
  377. {
  378. // commdlg browser ate the default command
  379. }
  380. else if (idCmd == 0)
  381. {
  382. // No item selected
  383. }
  384. else
  385. {
  386. m_psfv->OnCommand(pcm, GET_WM_COMMAND_MPS(idCmd, 0, 0));
  387. }
  388. }
  389. BOOL CSFViewDlg::RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
  390. {
  391. switch(uMsg)
  392. {
  393. case WM_INITDIALOG:
  394. InitDialog();
  395. break;
  396. case WM_DESTROY:
  397. m_cList.DeleteAllItems();
  398. if (OleInited())
  399. {
  400. OleUninitialize();
  401. m_hrOLE = E_UNEXPECTED;
  402. }
  403. break;
  404. case WM_NOTIFY:
  405. SetWindowLong(m_hDlg, DWL_MSGRESULT, Notify((LPNMHDR)lParam));
  406. break;
  407. case WM_INITMENUPOPUP:
  408. m_psfv->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
  409. break;
  410. case WM_COMMAND:
  411. m_psfv->OnCommand(NULL, wParam, lParam);
  412. break;
  413. case WM_CONTEXTMENU:
  414. ContextMenu(lParam);
  415. break;
  416. case WM_SIZE:
  417. SetWindowPos(m_cList, NULL, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
  418. SWP_NOZORDER|SWP_SHOWWINDOW);
  419. break;
  420. case WM_MENUSELECT:
  421. m_psfv->OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam),
  422. GET_WM_MENUSELECT_FLAGS(wParam, lParam),
  423. GET_WM_MENUSELECT_HMENU(wParam, lParam));
  424. break;
  425. default:
  426. return(FALSE);
  427. }
  428. return(TRUE);
  429. }
  430. UINT CSFViewDlg::CharWidth()
  431. {
  432. HDC hdc = GetDC(m_cList);
  433. SelectFont(hdc, FORWARD_WM_GETFONT(m_cList, SendMessage));
  434. SIZE siz;
  435. GetTextExtentPoint(hdc, "0", 1, &siz);
  436. ReleaseDC(m_cList, hdc);
  437. return(siz.cx);
  438. }
  439. int CSFViewDlg::AddObject(LPCITEMIDLIST pidl)
  440. {
  441. LV_ITEM item;
  442. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  443. item.iItem = 0x7fff; // add at end
  444. item.iSubItem = 0;
  445. item.iImage = I_IMAGECALLBACK;
  446. item.pszText = LPSTR_TEXTCALLBACK;
  447. item.lParam = (LPARAM)pidl;
  448. return(m_cList.InsertItem(&item));
  449. }
  450. LPCITEMIDLIST CSFViewDlg::GetPIDL(int iItem)
  451. {
  452. LV_ITEM item;
  453. item.mask = LVIF_PARAM;
  454. item.iItem = iItem;
  455. item.iSubItem = 0;
  456. item.lParam = 0;
  457. if (iItem != -1)
  458. {
  459. ListView_GetItem(m_cList, &item);
  460. }
  461. return((LPCITEMIDLIST)item.lParam);
  462. }
  463. UINT CSFViewDlg::GetItemPIDLS(LPCITEMIDLIST apidl[], UINT cItemMax, UINT uItem)
  464. {
  465. // We should put the focused one at the top of the list.
  466. int iItem = -1;
  467. int iItemFocus = -1;
  468. UINT cItem = 0;
  469. UINT uType;
  470. switch (uItem)
  471. {
  472. case SVGIO_SELECTION:
  473. // special case for faster search
  474. if (!cItemMax)
  475. {
  476. return ListView_GetSelectedCount(m_cList);
  477. }
  478. iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED);
  479. uType = LVNI_SELECTED;
  480. break;
  481. case SVGIO_ALLVIEW:
  482. // special case for faster search
  483. if (!cItemMax)
  484. {
  485. return ListView_GetItemCount(m_cList);
  486. }
  487. uType = LVNI_ALL;
  488. break;
  489. default:
  490. return(0);
  491. }
  492. while((iItem=ListView_GetNextItem(m_cList, iItem, uType)) != -1)
  493. {
  494. if (cItem < cItemMax)
  495. {
  496. // Check if the item is the focused one or not.
  497. if (iItem == iItemFocus)
  498. {
  499. // Yes, put it at the top.
  500. apidl[cItem] = apidl[0];
  501. apidl[0] = GetPIDL(iItem);
  502. }
  503. else
  504. {
  505. // No, put it at the end of the list.
  506. apidl[cItem] = GetPIDL(iItem);
  507. }
  508. }
  509. cItem++;
  510. }
  511. return cItem;
  512. }
  513. HRESULT CSFViewDlg::GetItemObjects(LPCITEMIDLIST **ppidl, UINT uItem)
  514. {
  515. UINT cItems = GetItemPIDLS(NULL, 0, uItem);
  516. LPCITEMIDLIST * apidl;
  517. if (ppidl != NULL)
  518. {
  519. *ppidl = NULL;
  520. if (cItems == 0)
  521. {
  522. return(ResultFromShort(0)); // nothing allocated...
  523. }
  524. apidl = new LPCITEMIDLIST[cItems];
  525. if (!apidl)
  526. {
  527. return(E_OUTOFMEMORY);
  528. }
  529. *ppidl = apidl;
  530. cItems = GetItemPIDLS(apidl, cItems, uItem);
  531. }
  532. return(ResultFromShort(cItems));
  533. }
  534. HRESULT CSFViewDlg::GetUIObjectFromItem(REFIID riid, LPVOID * ppv, UINT uItem)
  535. {
  536. LPCITEMIDLIST * apidl;
  537. HRESULT hres = GetItemObjects(&apidl, uItem);
  538. UINT cItems = ShortFromResult(hres);
  539. if (FAILED(hres))
  540. {
  541. return(hres);
  542. }
  543. if (!cItems)
  544. {
  545. return(E_INVALIDARG);
  546. }
  547. hres = m_psfv->m_psf->GetUIObjectOf(m_psfv->m_hwndMain, cItems, apidl, riid, 0, ppv);
  548. delete apidl;
  549. return hres;
  550. }
  551. HRESULT CSFViewDlg::GetAttributesFromItem(ULONG *pdwAttr, UINT uItem)
  552. {
  553. LPCITEMIDLIST * apidl;
  554. HRESULT hres = GetItemObjects(&apidl, uItem);
  555. UINT cItems = ShortFromResult(hres);
  556. if (FAILED(hres))
  557. {
  558. return(hres);
  559. }
  560. if (!cItems)
  561. {
  562. return(E_INVALIDARG);
  563. }
  564. hres = m_psfv->m_psf->GetAttributesOf(cItems, apidl, pdwAttr);
  565. delete apidl;
  566. return hres;
  567. }
  568. BOOL CSFViewDlg::IsMenuSeparator(HMENU hm, int i)
  569. {
  570. MENUITEMINFO mii;
  571. mii.cbSize = sizeof(MENUITEMINFO);
  572. mii.fMask = MIIM_TYPE;
  573. mii.cch = 0; // WARNING: We MUST initialize it to 0!!!
  574. if (!GetMenuItemInfo(hm, i, TRUE, &mii))
  575. {
  576. return(FALSE);
  577. }
  578. if (mii.fType & MFT_SEPARATOR)
  579. {
  580. return(TRUE);
  581. }
  582. return(FALSE);
  583. }