Leaked source code of windows server 2003
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.

15428 lines
498 KiB

  1. #include "shellprv.h"
  2. #include <regstr.h>
  3. #include <shellp.h>
  4. #include <htmlhelp.h>
  5. #include "ole2dup.h"
  6. #include "ids.h"
  7. #include "defview.h"
  8. #include "lvutil.h"
  9. #include "idlcomm.h"
  10. #include "filetbl.h"
  11. #include "undo.h"
  12. #include "cnctnpt.h"
  13. #include "ovrlaymn.h"
  14. #include "_security.h"
  15. #include "unicpp\dutil.h"
  16. #include "uemapp.h"
  17. #include "unicpp\deskhtm.h"
  18. #include "unicpp\dcomp.h"
  19. #include "datautil.h"
  20. #include "defvphst.h"
  21. #include <shdispid.h>
  22. #include <limits.h>
  23. #include "prop.h"
  24. #include <mshtmcid.h>
  25. #include "dvtasks.h"
  26. #include "category.h"
  27. #include "ViewState.h"
  28. #include <initguid.h>
  29. #include <guids.h>
  30. #include <CommonControls.h>
  31. #include "clsobj.h"
  32. #include <sfview.h>
  33. #include "defviewp.h"
  34. #include "shellp.h"
  35. #include "duiview.h"
  36. #include "enumidlist.h"
  37. #include "util.h"
  38. #include "foldertypes.h"
  39. #include <dpa.h>
  40. #include "views.h"
  41. #include "defcm.h"
  42. #include "contextmenu.h"
  43. // a "default" view to trick the browser into letting us delay viewmode selection
  44. // {6C6720F7-4B22-4CAA-82D6-502BB6F85A9A}
  45. DEFINE_GUID(VID_DefaultView, 0x6C6720F7L, 0x4B22, 0x4CAA, 0x82, 0xD6, 0x50, 0x2B, 0xB6, 0xF8, 0x5A, 0x9A);
  46. void DisableActiveDesktop();
  47. STDAPI_(void) CFSFolder_UpdateIcon(IShellFolder *psf, LPCITEMIDLIST pidl);
  48. STDAPI_(void) SetPositionItemsPoints(IFolderView* psfv, LPCITEMIDLIST* apidl, UINT cidl, IDataObject* pdtobj, POINT* ptDrag);
  49. void UpdateGridSizes(BOOL fDesktop, HWND hwndListview, int nWorkAreas, LPRECT prcWork, BOOL fMinimizeGutterSpace);
  50. #define ID_LISTVIEW 1
  51. #define ID_STATIC 2
  52. extern BOOL g_fDraggingOverSource;
  53. #define IsDefaultState(_dvHead) ((_dvHead).dvState.lParamSort == 0 && \
  54. (_dvHead).dvState.iDirection == 1 && \
  55. (_dvHead).dvState.iLastColumnClick == -1 && \
  56. (_dvHead).ptScroll.x == 0 && (_dvHead).ptScroll.y == 0)
  57. HMODULE g_hmodNTSHRUI = NULL;
  58. typedef struct
  59. {
  60. POINT pt;
  61. ITEMIDLIST idl;
  62. } DVITEM;
  63. //
  64. // Note that it returns NULL, if iItem is -1.
  65. //
  66. // determine if color is light or dark
  67. #define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
  68. void EnableCombinedView(CDefView *pdsv, BOOL fEnable);
  69. BOOL IsBarricadeGloballyOff();
  70. VARIANT_BOOL GetBarricadeStatus(LPCTSTR pszValueName);
  71. BOOL GetBarricadeValueNameFromPidl(LPCITEMIDLIST pidl, LPTSTR pszValueName, UINT cch);
  72. HRESULT SetBarricadeStatus(LPCTSTR pszValueName, VARIANT_BOOL bShowBarricade);
  73. // Command Strings
  74. // !! warning. Some ContextMenu handlers do not do a case-insensitive
  75. // check of the command so keep the case the same everywhere
  76. TCHAR const c_szCut[] = TEXT("cut");
  77. TCHAR const c_szCopy[] = TEXT("copy");
  78. TCHAR const c_szLink[] = TEXT("link");
  79. TCHAR const c_szProperties[] = TEXT("properties");
  80. TCHAR const c_szPaste[] = TEXT("paste");
  81. TCHAR const c_szPasteLink[] = TEXT("pastelink");
  82. TCHAR const c_szRename[] = TEXT("rename");
  83. TCHAR const c_szDelete[] = TEXT("delete");
  84. TCHAR const c_szNewFolder[] = TEXT(CMDSTR_NEWFOLDERA);
  85. char const c_szDeleteA[] = "delete";
  86. char const c_szNewFolderA[] = CMDSTR_NEWFOLDERA;
  87. char const c_szPrintA[] = "print";
  88. WCHAR const c_szPrintW[] = L"print";
  89. DWORD CDefView::_Attributes(LPCITEMIDLIST pidl, DWORD dwAttribs)
  90. {
  91. return SHGetAttributes(_pshf, pidl, dwAttribs);
  92. }
  93. // IDefViewSafety
  94. HRESULT CDefView::IsSafePage()
  95. {
  96. HRESULT hr = E_ACCESSDENIED;
  97. WCHAR wszCurrentMoniker[MAX_PATH];
  98. if (SUCCEEDED(_cFrame._GetCurrentWebViewMoniker(wszCurrentMoniker,
  99. ARRAYSIZE(wszCurrentMoniker))))
  100. {
  101. // Some previous versions of the OS put a "FILE://" in front of the template name and some didn't.
  102. // SHRegisterValidateTemplate can't handle this prefix, so skip past it.
  103. LPWSTR pszMoniker = wszCurrentMoniker;
  104. if (!StrNCmpI(pszMoniker, L"FILE://", 7))
  105. pszMoniker += 7;
  106. hr = SHRegisterValidateTemplate(pszMoniker,
  107. SHRVT_VALIDATE | SHRVT_PROMPTUSER | SHRVT_REGISTERIFPROMPTOK);
  108. }
  109. return hr;
  110. }
  111. // IDVGetEnum
  112. HRESULT CDefView::SetEnumReadyCallback(PFDVENUMREADYBALLBACK pfn, void *pvData)
  113. {
  114. _pfnEnumReadyCallback = pfn;
  115. _pvEnumCallbackData = pvData;
  116. return S_OK;
  117. }
  118. BOOL FilterOnAttributes(DWORD dwAttributes, DWORD grfEnumFlags)
  119. {
  120. if (dwAttributes & SFGAO_FOLDER)
  121. {
  122. if (!(grfEnumFlags & SHCONTF_FOLDERS))
  123. return FALSE; // item is folder but client does not want folders
  124. }
  125. else if (!(grfEnumFlags & SHCONTF_NONFOLDERS))
  126. {
  127. return FALSE; // item is file, but client only wants folders
  128. }
  129. if (!(grfEnumFlags & SHCONTF_INCLUDEHIDDEN) &&
  130. (dwAttributes & SFGAO_HIDDEN))
  131. return FALSE; // item is hidden by client wants non hidden
  132. return TRUE;
  133. }
  134. HRESULT CDefView::CreateEnumIDListFromContents(LPCITEMIDLIST pidlFolder, DWORD grfEnumFlags, IEnumIDList **ppenum)
  135. {
  136. HRESULT hr = E_FAIL;
  137. LPITEMIDLIST pidlView = _GetViewPidl();
  138. if (pidlView)
  139. {
  140. if (ILIsEqual(pidlFolder, pidlView) && (grfEnumFlags & _GetEnumFlags()) == grfEnumFlags)
  141. {
  142. LPCITEMIDLIST *apidl;
  143. UINT cItems;
  144. hr = _GetItemObjects(&apidl, SVGIO_ALLVIEW, &cItems);
  145. if (SUCCEEDED(hr))
  146. {
  147. for (UINT i = 0; i < cItems; i++)
  148. {
  149. if (!FilterOnAttributes(_Attributes(apidl[i], SFGAO_FOLDER | SFGAO_HIDDEN), grfEnumFlags))
  150. {
  151. apidl[i] = apidl[cItems - 1];
  152. cItems--;
  153. i--;
  154. }
  155. }
  156. hr = CreateIEnumIDListOnIDLists(apidl, cItems, ppenum);
  157. LocalFree(apidl);
  158. }
  159. }
  160. ILFree(pidlView);
  161. }
  162. return hr;
  163. }
  164. HRESULT CDefView::_OnDefaultCommand()
  165. {
  166. return _pcdb ? _pcdb->OnDefaultCommand(_psvOuter ? _psvOuter : this) : E_NOTIMPL;
  167. }
  168. HRESULT CDefView::_OnStateChange(UINT code)
  169. {
  170. return _pcdb ? _pcdb->OnStateChange(_psvOuter ? _psvOuter : this, code) : E_NOTIMPL;
  171. }
  172. HRESULT CDefView::_IncludeObject(LPCITEMIDLIST pidl)
  173. {
  174. if (_pcdb)
  175. return _pcdb->IncludeObject(_psvOuter ? _psvOuter : this, pidl);
  176. else
  177. {
  178. IFolderFilter *psff = _cCallback.GetISFF();
  179. return psff ? psff->ShouldShow(_pshf, NULL, pidl) : S_OK;
  180. }
  181. }
  182. HRESULT CDefView::CallCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
  183. {
  184. return _cCallback.CallCB(uMsg, wParam, lParam);
  185. }
  186. // fires dispatch events to clients (address bar, webview, etc).
  187. // this translates return values of false into "ERROR_CANCELLED"
  188. HRESULT CDefView::_FireEvent(DISPID dispid)
  189. {
  190. HRESULT hr;
  191. VARIANT varResult = {0};
  192. SHINVOKEPARAMS inv = {0};
  193. inv.dispidMember = dispid;
  194. inv.piid = &IID_NULL;
  195. inv.wFlags = DISPATCH_METHOD;
  196. inv.pvarResult = &varResult;
  197. if (SUCCEEDED(IUnknown_CPContainerInvokeIndirect(_pauto, DIID_DShellFolderViewEvents, &inv)))
  198. {
  199. if ((VT_BOOL == varResult.vt) && (VARIANT_FALSE == varResult.boolVal))
  200. {
  201. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  202. }
  203. else
  204. {
  205. hr = S_OK;
  206. }
  207. VariantClear(&varResult);
  208. }
  209. else
  210. hr = S_FALSE;
  211. return hr;
  212. }
  213. BOOL CDefView::_IsPositionedView()
  214. {
  215. return !_fGroupView && ((_fs.ViewMode == FVM_ICON) || (_fs.ViewMode == FVM_SMALLICON) ||
  216. (_fs.ViewMode == FVM_TILE) || (_fs.ViewMode == FVM_THUMBNAIL) ||
  217. (_fs.ViewMode == FVM_THUMBSTRIP));
  218. }
  219. // reposition the selected items in a listview by dx, dy
  220. void CDefView::_MoveSelectedItems(int dx, int dy, BOOL fAbsolute)
  221. {
  222. SendMessage(_hwndListview, WM_SETREDRAW, FALSE, 0);
  223. for (int i = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
  224. i >= 0;
  225. i = ListView_GetNextItem(_hwndListview, i, LVNI_SELECTED))
  226. {
  227. if (fAbsolute)
  228. {
  229. _SetItemPosition(i, dx, dy);
  230. }
  231. else
  232. {
  233. POINT pt;
  234. ListView_GetItemPosition(_hwndListview, i, &pt);
  235. pt.x += dx;
  236. pt.y += dy;
  237. _SetItemPosition(i, pt.x, pt.y);
  238. }
  239. }
  240. SendMessage(_hwndListview, WM_SETREDRAW, TRUE, 0);
  241. }
  242. void CDefView::_SameViewMoveIcons()
  243. {
  244. POINT ptDrop;
  245. BOOL fAbsolute = FALSE;
  246. // We'll use the insert mark rect (if available) to determine a drop point
  247. if (_GetInsertPoint(&ptDrop))
  248. fAbsolute = TRUE; // Move all items to this point.
  249. else
  250. {
  251. ptDrop = _ptDrop;
  252. ptDrop.x -= _ptDragAnchor.x;
  253. ptDrop.y -= _ptDragAnchor.y;
  254. LVUtil_ClientToLV(_hwndListview, &ptDrop);
  255. }
  256. ASSERT(_IsPositionedView());
  257. _MoveSelectedItems(ptDrop.x, ptDrop.y, fAbsolute);
  258. }
  259. //
  260. // This function checks if the current HTML wallpaper is the default
  261. // wallpaper and returns TRUE if so. If the wallpaper is the default wallpaper,
  262. // it reads the colors from the registry. If the colors are missing, then it
  263. // supplies the default colors.
  264. //
  265. BOOL CDefView::_GetColorsFromHTMLdoc(COLORREF *pclrTextBk, COLORREF *pclrHotlight)
  266. {
  267. // make sure the HTML document has reached ready-state interactive
  268. COLORREF clrBackground;
  269. BOOL bRet = SUCCEEDED(_cFrame._GetHTMLBackgroundColor(&clrBackground));
  270. if (bRet)
  271. {
  272. // The following are the standard colors supported on desktop
  273. const COLORREF c_VgaColorTable[] =
  274. {
  275. 0x000000, // Black
  276. 0x000080,
  277. 0x0000FF,
  278. 0x008000,
  279. 0x008080,
  280. 0x00FF00, // Green
  281. 0x00FFFF, // Yellow
  282. 0x800000,
  283. 0x800080,
  284. 0x808000,
  285. 0x808080,
  286. 0xF0CAA6,
  287. 0xF0FBFF,
  288. 0xFF0000, // Blue
  289. 0xFF00FF, // Magenta
  290. 0xFFFF00, // cobalt
  291. 0xFFFFFF // White
  292. };
  293. // Check if the given background color is a standard color.
  294. // If not, use the system background (COLOR_BACKGROUND).
  295. *pclrTextBk = GetSysColor(COLOR_BACKGROUND); // default
  296. for (int i = 0; i < ARRAYSIZE(c_VgaColorTable); i++)
  297. {
  298. if (c_VgaColorTable[i] == clrBackground)
  299. {
  300. *pclrTextBk = clrBackground; // standard, so use it
  301. break;
  302. }
  303. }
  304. if (COLORISLIGHT(*pclrTextBk))
  305. *pclrHotlight = 0x000000; //Black as hightlight color!
  306. else
  307. *pclrHotlight = 0xFFFFFF; //White as highlight color!
  308. }
  309. return bRet;
  310. }
  311. // Set the colors for the folder - taking care if it's the desktop.
  312. void CDefView::_SetFolderColors()
  313. {
  314. COLORREF clrText, clrTextBk, clrWindow;
  315. // Is this view for the desktop?
  316. if (_IsDesktop())
  317. {
  318. COLORREF clrHotlight;
  319. Shell_SysColorChange();
  320. // If we show HTML wallpaper, then get the appropriate colors too!
  321. if (_fCombinedView && _GetColorsFromHTMLdoc(&clrTextBk, &clrHotlight))
  322. {
  323. // Set the Hotlight color!
  324. ListView_SetHotlightColor(_hwndListview, clrHotlight);
  325. }
  326. else
  327. {
  328. // Yep.
  329. // Clear the background color of the desktop to make it
  330. // properly handle transparency.
  331. clrTextBk = GetSysColor(COLOR_BACKGROUND);
  332. //Reset the Hotlight color sothat the system color can be used.
  333. ListView_SetHotlightColor(_hwndListview, CLR_DEFAULT);
  334. }
  335. // set a text color that will show up over desktop color
  336. if (COLORISLIGHT(clrTextBk))
  337. clrText = 0x000000; // black
  338. else
  339. clrText = 0xFFFFFF; // white
  340. clrWindow = CLR_NONE; // Assume transparent
  341. //
  342. // if there is no wallpaper or pattern we can use
  343. // a solid color for the ListView. otherwise we
  344. // need to use a transparent ListView, this is much
  345. // slower so dont do it unless we need to.
  346. //
  347. // Don't do this optimization if USER is going to paint
  348. // some magic text on the desktop, such as
  349. //
  350. // "FailSafe" (SM_CLEANBOOT)
  351. // "Debug" (SM_DEBUG)
  352. // "Build ####" (REGSTR_PATH_DESKTOP\PaintDesktopVersion)
  353. // "Evaluation Version"
  354. //
  355. // too bad there is no SPI_GETWALLPAPER, we need to read
  356. // from WIN.INI.
  357. //
  358. TCHAR szWallpaper[128], szPattern[128];
  359. DWORD dwPaintVersion = 0;
  360. szWallpaper[0] = 0;
  361. szPattern[0] = 0;
  362. HKEY hkey;
  363. if (RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP, 0, KEY_QUERY_VALUE, &hkey) == 0)
  364. {
  365. UINT cb = sizeof(szWallpaper);
  366. SHQueryValueEx(hkey, TEXT("Wallpaper"), NULL, NULL, (LPBYTE)szWallpaper, (ULONG*)&cb);
  367. cb = sizeof(szPattern);
  368. SHQueryValueEx(hkey, TEXT("Pattern"), NULL, NULL, (LPBYTE)szPattern, (ULONG*)&cb);
  369. cb = sizeof(dwPaintVersion);
  370. SHQueryValueEx(hkey, TEXT("PaintDesktopVersion"), NULL, NULL, (LPBYTE)&dwPaintVersion, (ULONG*)&cb);
  371. // Other external criteria for painting the version
  372. //
  373. // - This is a beta version (has an expiration date)
  374. // - A test certificate is installed
  375. //
  376. if (dwPaintVersion == 0 && IsOS(OS_WIN2000ORGREATER))
  377. {
  378. #define REGSTR_PATH_LM_ROOTCERTIFICATES \
  379. TEXT("SOFTWARE\\Microsoft\\SystemCertificates\\Root\\Certificates")
  380. #define REGSTR_PATH_GPO_ROOTCERTIFICATES \
  381. TEXT("SOFTWARE\\Policies\\Microsoft\\SystemCertificates\\Root\\Certificates")
  382. #define REGSTR_KEY_TESTCERTIFICATE \
  383. TEXT("2BD63D28D7BCD0E251195AEB519243C13142EBC3")
  384. dwPaintVersion = (0 != USER_SHARED_DATA->SystemExpirationDate.QuadPart) ||
  385. SHRegSubKeyExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_LM_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE) ||
  386. SHRegSubKeyExists(HKEY_LOCAL_MACHINE, REGSTR_PATH_GPO_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE) ||
  387. SHRegSubKeyExists(HKEY_CURRENT_USER, REGSTR_PATH_GPO_ROOTCERTIFICATES TEXT("\\") REGSTR_KEY_TESTCERTIFICATE);
  388. }
  389. RegCloseKey(hkey);
  390. }
  391. if (_fCombinedView ||
  392. (GetSystemMetrics(SM_CLEANBOOT) == 0 &&
  393. GetSystemMetrics(SM_DEBUG) == 0 &&
  394. !dwPaintVersion &&
  395. (!_fHasDeskWallPaper) &&
  396. (szWallpaper[0] == 0 || szWallpaper[0] == TEXT('(')) &&
  397. (szPattern[0] == 0 || szPattern[0] == TEXT('('))))
  398. {
  399. clrWindow = GetSysColor(COLOR_BACKGROUND);
  400. }
  401. }
  402. else
  403. {
  404. // Nope.
  405. clrWindow = GetSysColor(COLOR_WINDOW);
  406. clrTextBk = clrWindow;
  407. clrText = GetSysColor(COLOR_WINDOWTEXT);
  408. if (_fs.fFlags & FWF_TRANSPARENT)
  409. {
  410. IWebBrowser2 *pwb;
  411. if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SContainerDispatch, IID_PPV_ARG(IWebBrowser2, &pwb))))
  412. {
  413. IDispatch *pdisp;
  414. if (SUCCEEDED(pwb->get_Parent(&pdisp)))
  415. {
  416. IUnknown_HTMLBackgroundColor(pdisp, &clrWindow);
  417. pdisp->Release();
  418. }
  419. pwb->Release();
  420. }
  421. }
  422. }
  423. if (!_fClassic && ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXTBACKGROUND]))
  424. clrTextBk = _crCustomColors[CRID_CUSTOMTEXTBACKGROUND];
  425. if (!_fClassic && ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXT]))
  426. clrText = _crCustomColors[CRID_CUSTOMTEXT];
  427. BOOL bChange = FALSE;
  428. if (clrWindow != ListView_GetBkColor(_hwndListview))
  429. bChange = ListView_SetBkColor(_hwndListview, clrWindow);
  430. if (clrTextBk != ListView_GetTextBkColor(_hwndListview))
  431. bChange = ListView_SetTextBkColor(_hwndListview, clrTextBk);
  432. if (clrText != ListView_GetTextColor(_hwndListview))
  433. bChange = ListView_SetTextColor(_hwndListview, clrText);
  434. if (bChange)
  435. InvalidateRect(_hwndListview, NULL, TRUE);
  436. }
  437. #define ViewRequiresColumns(x) ((x) == FVM_DETAILS || (x) == FVM_TILE)
  438. DWORD CDefView::_LVStyleFromView()
  439. {
  440. DWORD dwStyle;
  441. if (_IsDesktop())
  442. {
  443. dwStyle = LVS_NOSCROLL | LVS_ALIGNLEFT;
  444. }
  445. else
  446. {
  447. dwStyle = LVS_SHOWSELALWAYS; // make sure selection is visible
  448. }
  449. // dwStyle |= _UxGetView();
  450. // The listview view is no longer set using the window style, so the call to the
  451. // view mapping code has been commented out.
  452. // APPCOMPAT: This may be an issue, if apps are depending the exstyle bits on the listview hwnd
  453. // in defview. If so, we can set them, but we must take care to exclude any bits outside the 2bit
  454. // "view range" in the extended style (namely, tile view)
  455. if (_IsAutoArrange())
  456. dwStyle |= LVS_AUTOARRANGE;
  457. if (_fs.fFlags & FWF_SINGLESEL)
  458. dwStyle |= LVS_SINGLESEL;
  459. if (_fs.fFlags & FWF_ALIGNLEFT)
  460. dwStyle |= LVS_ALIGNLEFT;
  461. if (_fs.fFlags & FWF_NOSCROLL)
  462. dwStyle |= LVS_NOSCROLL;
  463. return dwStyle;
  464. }
  465. DWORD CDefView::_LVExStyleFromView()
  466. {
  467. DWORD dwLVExStyle = 0;
  468. if (_fs.fFlags & FWF_SNAPTOGRID)
  469. dwLVExStyle |= LVS_EX_SNAPTOGRID;
  470. if (_fs.fFlags & FWF_CHECKSELECT)
  471. dwLVExStyle |= LVS_EX_CHECKBOXES|LVS_EX_SIMPLESELECT;
  472. return dwLVExStyle;
  473. }
  474. HRESULT CDefView::_GetDetailsHelper(int i, DETAILSINFO *pdi)
  475. {
  476. HRESULT hr = E_NOTIMPL;
  477. if (_pshf2)
  478. {
  479. hr = _pshf2->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS *)&pdi->fmt);
  480. }
  481. if (FAILED(hr)) // Don't make NSEs impl all of IShellFolder2
  482. {
  483. if (_psd)
  484. {
  485. // HACK: pdi->fmt is the same layout as SHELLDETAILS
  486. hr = _psd->GetDetailsOf(pdi->pidl, i, (SHELLDETAILS *)&pdi->fmt);
  487. }
  488. else if (HasCB())
  489. {
  490. hr = CallCB(SFVM_GETDETAILSOF, i, (LPARAM)pdi);
  491. }
  492. }
  493. return hr;
  494. }
  495. // Determine if the given defview state struct has valid
  496. // state info. If is doesn't, this function massages the
  497. // values so it does.
  498. UINT CDefView::_GetHeaderCount()
  499. {
  500. UINT cCols = 0;
  501. HWND hwndHead = ListView_GetHeader(_hwndListview);
  502. if (hwndHead)
  503. {
  504. cCols = Header_GetItemCount(hwndHead);
  505. }
  506. return cCols;
  507. }
  508. void CDefView::AddColumns()
  509. {
  510. // so we do this once
  511. if (_bLoadedColumns)
  512. return;
  513. _bLoadedColumns = TRUE;
  514. // I also use this as a flag for whether to free pColHdr
  515. //
  516. // Calculate a reasonable size to initialize the column width to.
  517. _cxChar = GetControlCharWidth(_hwndListview);
  518. // Check whether there is any column enumerator (ShellDetails or callback)
  519. if (_psd || _pshf2 || HasCB())
  520. {
  521. // Some shell extensions return S_OK and NULL pstmCols.
  522. IStream *pstmCols = NULL;
  523. if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_READ, (LPARAM)&pstmCols)) && pstmCols)
  524. {
  525. _vs.LoadColumns(this, pstmCols);
  526. pstmCols->Release();
  527. }
  528. // Verify that this has been initialized. This may not be if there was no state stream.
  529. _vs.InitializeColumns(this);
  530. for (UINT i = 0; i < _vs.GetColumnCount(); ++i)
  531. {
  532. if (_IsColumnInListView(i))
  533. {
  534. UINT iVisible = _RealToVisibleCol(i);
  535. LV_COLUMN col;
  536. col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  537. col.fmt = _vs.GetColumnFormat(i);
  538. // If column width is not specified in the desktop.ini.......
  539. col.cx = _vs.GetColumnWidth(iVisible, _vs.GetColumnCharCount(i) * _cxChar);
  540. col.pszText = _vs.GetColumnName(i);
  541. col.cchTextMax = MAX_COLUMN_NAME_LEN;
  542. col.iSubItem = i;
  543. if (col.fmt & LVCFMT_COL_HAS_IMAGES)
  544. {
  545. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SUBITEMIMAGES, LVS_EX_SUBITEMIMAGES);
  546. col.fmt &= ~LVCFMT_COL_HAS_IMAGES;
  547. }
  548. ListView_InsertColumn(_hwndListview, iVisible, &col);
  549. }
  550. }
  551. // Set the header control to have zero margin around bitmaps, for the sort arrows
  552. Header_SetBitmapMargin(ListView_GetHeader(_hwndListview), 0);
  553. ListView_SetExtendedListViewStyleEx(_hwndListview,
  554. LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP,
  555. LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP);
  556. //We added columns; so, just sync the Column order.
  557. _vs.SyncColumnOrder(this, TRUE);
  558. }
  559. // use real numbers, not visible
  560. int cCols = (int)_vs.GetColumnCount();
  561. if (_vs._iLastColumnClick >= cCols)
  562. {
  563. _vs.InitWithDefaults(this);
  564. if (_vs._iLastColumnClick >= cCols ||
  565. _vs._lParamSort >= cCols)
  566. {
  567. // our defaults won't work on this view....
  568. // hard code these defaults
  569. _vs._lParamSort = 0;
  570. _vs._iDirection = 1;
  571. _vs._iLastColumnClick = -1;
  572. }
  573. }
  574. }
  575. void CDefView::InitSelectionMode()
  576. {
  577. _dwSelectionMode = 0;
  578. if (_fs.fFlags & FWF_SINGLECLICKACTIVATE)
  579. {
  580. _dwSelectionMode = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
  581. }
  582. else if (!_fClassic)
  583. {
  584. SHELLSTATE ss;
  585. SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW, FALSE);
  586. if (!ss.fDoubleClickInWebView)
  587. _dwSelectionMode = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE;
  588. }
  589. }
  590. void CDefView::_UpdateSelectionMode()
  591. {
  592. InitSelectionMode();
  593. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, _dwSelectionMode);
  594. }
  595. DWORD _GetUnderlineStyles()
  596. {
  597. DWORD dwUnderline = ICON_IE;
  598. // Read the icon underline settings.
  599. DWORD cb = sizeof(dwUnderline);
  600. SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
  601. TEXT("IconUnderline"), NULL, &dwUnderline, &cb, FALSE, &dwUnderline, cb);
  602. // If it says to use the IE link settings, read them in.
  603. if (dwUnderline == ICON_IE)
  604. {
  605. dwUnderline = ICON_YES;
  606. TCHAR szUnderline[8];
  607. cb = sizeof(szUnderline);
  608. SHRegGetUSValue(TEXT("Software\\Microsoft\\Internet Explorer\\Main"),
  609. TEXT("Anchor Underline"), NULL, szUnderline, &cb, FALSE, szUnderline, cb);
  610. // Convert the string to an ICON_ value.
  611. if (!lstrcmpi(szUnderline, TEXT("hover")))
  612. dwUnderline = ICON_HOVER;
  613. else if (!lstrcmpi(szUnderline, TEXT("no")))
  614. dwUnderline = ICON_NO;
  615. else
  616. dwUnderline = ICON_YES;
  617. }
  618. // Convert the ICON_ value into an LVS_EX value.
  619. DWORD dwExStyle;
  620. switch (dwUnderline)
  621. {
  622. case ICON_NO:
  623. dwExStyle = 0;
  624. break;
  625. case ICON_HOVER:
  626. dwExStyle = LVS_EX_UNDERLINEHOT;
  627. break;
  628. case ICON_YES:
  629. dwExStyle = LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD;
  630. break;
  631. }
  632. return dwExStyle;
  633. }
  634. void CDefView::_UpdateUnderlines()
  635. {
  636. // Set the new LVS_EX_UNDERLINE flags.
  637. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD, _GetUnderlineStyles());
  638. }
  639. void CDefView::_SetSysImageList()
  640. {
  641. HIMAGELIST himlLarge, himlSmall;
  642. Shell_GetImageLists(&himlLarge, &himlSmall);
  643. ListView_SetImageList(_hwndListview, himlLarge, LVSIL_NORMAL);
  644. ListView_SetImageList(_hwndListview, himlSmall, LVSIL_SMALL);
  645. }
  646. void CDefView::_SetTileview()
  647. {
  648. IImageList* piml;
  649. if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml))))
  650. {
  651. ListView_SetImageList(_hwndListview, IImageListToHIMAGELIST(piml), LVSIL_NORMAL);
  652. piml->Release();
  653. }
  654. }
  655. LRESULT CDefView::_OnCreate(HWND hWnd)
  656. {
  657. _hwndView = hWnd;
  658. _hmenuCur = NULL;
  659. _uState = SVUIA_DEACTIVATE;
  660. _hAccel = LoadAccelerators(HINST_THISDLL, MAKEINTRESOURCE(ACCEL_DEFVIEW));
  661. // Note that we are going to get a WM_SIZE message soon, which will
  662. // place this window correctly
  663. // Map the ViewMode to the proper listview style
  664. DWORD dwStyle = _LVStyleFromView() | LVS_EDITLABELS;
  665. DWORD dwExStyle = 0;
  666. // If the parent window is mirrored then the treeview window will inheret the mirroring flag
  667. // And we need the reading order to be Left to right, which is the right to left in the mirrored mode.
  668. if (IS_WINDOW_RTL_MIRRORED(hWnd))
  669. {
  670. // This means left to right reading order because this window will be mirrored.
  671. dwExStyle |= WS_EX_RTLREADING;
  672. }
  673. // don't set this as in webview this is normally off, having this
  674. // set causes a 3d edge to flash on in a refresh
  675. if (!_ShouldShowWebView() && !_IsDesktop() && !(_fs.fFlags & FWF_NOCLIENTEDGE))
  676. {
  677. dwExStyle |= WS_EX_CLIENTEDGE;
  678. }
  679. if (_IsOwnerData())
  680. dwStyle |= LVS_OWNERDATA;
  681. _hwndListview = CreateWindowEx(dwExStyle, WC_LISTVIEW, TEXT("FolderView"), // MSAA name
  682. dwStyle | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_SHAREIMAGELISTS,
  683. 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, HINST_THISDLL, NULL);
  684. if (_hwndListview)
  685. {
  686. // Set up non-viewmode-dependant listview information here.
  687. // Other flags are set up in _SwitchToViewFVM
  688. DWORD dwLVExStyle = _LVExStyleFromView() | LVS_EX_INFOTIP | LVS_EX_LABELTIP;
  689. if (_IsDesktop())
  690. {
  691. if (GetNumberOfMonitors() > 1)
  692. dwLVExStyle |= LVS_EX_MULTIWORKAREAS;
  693. }
  694. else
  695. {
  696. dwLVExStyle |= LVS_EX_DOUBLEBUFFER; // Enable double buffering for all but desktop for affects
  697. }
  698. // turn on infotips -- window was just created, so all LVS_EX bits are off
  699. ListView_SetExtendedListViewStyle(_hwndListview, dwLVExStyle);
  700. // Get the proper RTL bits to pass on to our child windows
  701. _fmt = 0;
  702. // Be sure that the OS is supporting the flags DATE_LTRREADING and DATE_RTLREADING
  703. if (g_bBiDiPlatform)
  704. {
  705. // Get the date format reading order
  706. LCID locale = GetUserDefaultLCID();
  707. if ((PRIMARYLANGID(LANGIDFROMLCID(locale)) == LANG_ARABIC))
  708. {
  709. // Get the real list view windows ExStyle.
  710. // [msadek]; we shouldn't check for either WS_EX_RTLREADING OR RTL_MIRRORED_WINDOW
  711. // on localized builds we have both of them to display dirve letters,..etc correctly
  712. // on enabled builds we have none of them. let's check on RTL_MIRRORED_WINDOW only
  713. if (GetWindowLong(_hwndListview, GWL_EXSTYLE) & RTL_MIRRORED_WINDOW)
  714. _fmt = LVCFMT_RIGHT_TO_LEFT;
  715. else
  716. _fmt = LVCFMT_LEFT_TO_RIGHT;
  717. }
  718. }
  719. // Get hwndInfotip (the control for all listview infotips).
  720. HWND hwndInfotip = ListView_GetToolTips(_hwndListview);
  721. if (hwndInfotip)
  722. {
  723. // make the tooltip window to be topmost window (set the TTS_TOPMOST style bit for the tooltip)
  724. SetWindowPos(hwndInfotip, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  725. // Initialize hwndInfotip.
  726. _InitInfotipControl(hwndInfotip);
  727. }
  728. _UpdateUnderlines();
  729. // IShellDetails for old callers, new guys use IShellFolder2
  730. ASSERT(_psd == NULL);
  731. _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IShellDetails, &_psd));
  732. // App compat - some apps need columns loaded first thing
  733. if (SHGetAppCompatFlags(ACF_LOADCOLUMNHANDLER) & ACF_LOADCOLUMNHANDLER)
  734. {
  735. AddColumns();
  736. }
  737. _SetFolderColors();
  738. }
  739. // Create _hwndInfotip (the control for all non-listview infotips).
  740. _hwndInfotip = _CreateInfotipControl(hWnd);
  741. if (_hwndInfotip)
  742. {
  743. // Initialize _hwndInfotip.
  744. _InitInfotipControl(_hwndInfotip);
  745. }
  746. return _hwndListview ? 0 : -1; // 0 is success, -1 is failure from WM_CREATE
  747. }
  748. HWND CDefView::_CreateInfotipControl(HWND hwndParent)
  749. {
  750. // hwndInfotip is currently expected to be destroyed by destruction of
  751. // the parent hwnd (hwndParent). Thus, hwndParent should not be NULL.
  752. ASSERT(hwndParent != NULL); // Sanity check.
  753. // Create hwndInfotip.
  754. return ::CreateWindowEx(
  755. IS_WINDOW_RTL_MIRRORED(hwndParent) || IS_BIDI_LOCALIZED_SYSTEM()
  756. ? WS_EX_LAYOUTRTL
  757. : 0,
  758. TOOLTIPS_CLASS,
  759. NULL,
  760. 0,
  761. CW_USEDEFAULT,
  762. CW_USEDEFAULT,
  763. CW_USEDEFAULT,
  764. CW_USEDEFAULT,
  765. hwndParent,
  766. NULL,
  767. g_hinst,
  768. NULL);
  769. }
  770. void CDefView::_InitInfotipControl(HWND hwndInfotip)
  771. {
  772. ASSERT(hwndInfotip);
  773. // Set the length of time the pointer must remain stationary within a tool's
  774. // bounding rectangle before the ToolTip window appears to 2 times the default.
  775. INT iTime = ::SendMessage(hwndInfotip, TTM_GETDELAYTIME, TTDT_INITIAL, 0);
  776. ::SendMessage(hwndInfotip, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM)(INT)MAKELONG(iTime * 2, 0));
  777. // Set the length of time a ToolTip window remains visible if the pointer
  778. // is stationary within a tool's bounding rectangle to a very large value.
  779. ::SendMessage(hwndInfotip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)(INT)MAKELONG(MAXSHORT, 0));
  780. }
  781. // "Auto" AutoArrange means re-position if we are in a positioned view
  782. // and the listview is not in auto-arrange mode. we do this to re-layout
  783. // the icons in cases where that makes sense
  784. HRESULT CDefView::_AutoAutoArrange(DWORD dwReserved)
  785. {
  786. if (!_fUserPositionedItems && _IsPositionedView() &&
  787. !(GetWindowStyle(_hwndListview) & LVS_AUTOARRANGE))
  788. {
  789. ListView_Arrange(_hwndListview, LVA_DEFAULT);
  790. }
  791. return S_OK;
  792. }
  793. LRESULT CDefView::WndSize(HWND hWnd)
  794. {
  795. RECT rc;
  796. // We need to dismiss "name edit" mode, if we are in.
  797. _DismissEdit();
  798. // Get the client size.
  799. GetClientRect(hWnd, &rc);
  800. // Set the Static to be the Client size.
  801. if (_hwndStatic)
  802. {
  803. MoveWindow(_hwndStatic, rc.left, rc.top,
  804. rc.right-rc.left, rc.bottom-rc.top, TRUE);
  805. HWND hAnimate = ::GetWindow (_hwndStatic, GW_CHILD);
  806. if (hAnimate)
  807. {
  808. MoveWindow(hAnimate, rc.left, rc.top,
  809. rc.right-rc.left, rc.bottom-rc.top, TRUE);
  810. }
  811. RedrawWindow(_hwndStatic, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
  812. }
  813. // Set all windows to their new rectangles.
  814. _cFrame.SetRect(&rc);
  815. // Don't resize _hwndListview if a DefViewOC is using it.
  816. //
  817. // If we're waiting for a Web View (!_fCanActivateNow), then it
  818. // doesn't make sense to resize the _hwndListview -- just extra
  819. // work, right? But in the non-WebView case, it's this first
  820. // resize which sets the listview size, and then there are no
  821. // more. Unfortunately, the first resize comes in when the
  822. // _hwndListview is created, which is *before* _fCanActivateNow
  823. // can possibly be set.
  824. if (!_fGetWindowLV && !_pDUIView)
  825. {
  826. SetWindowPos(_hwndListview, NULL, rc.left, rc.top,
  827. rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
  828. OnResizeListView();
  829. }
  830. if (_pDUIView)
  831. {
  832. _pDUIView->SetSize (&rc);
  833. _AutoAutoArrange(0);
  834. }
  835. CallCB(SFVM_SIZE, 0, 0);
  836. return 1;
  837. }
  838. UINT _GetMenuIDFromViewMode(UINT uViewMode)
  839. {
  840. ASSERTMSG(FVM_FIRST <= uViewMode && uViewMode <= FVM_LAST, "_GetMenuIDFromViewMode received unknown uViewMode");
  841. return SFVIDM_VIEW_FIRSTVIEW + uViewMode - FVM_FIRST;
  842. }
  843. void CDefView::CheckToolbar()
  844. {
  845. if (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW)
  846. {
  847. int idCmdCurView = _GetMenuIDFromViewMode(_fs.ViewMode);
  848. // preserve win95 behavior for dumb corel apps
  849. for (int idCmd = SFVIDM_VIEW_ICON; idCmd <= SFVIDM_VIEW_DETAILS; idCmd++)
  850. {
  851. _psb->SendControlMsg(
  852. FCW_TOOLBAR, TB_CHECKBUTTON, idCmd, (LPARAM)(idCmd == idCmdCurView), NULL);
  853. }
  854. }
  855. }
  856. void CDefView::OnListViewDelete(int iItem, LPITEMIDLIST pidlToFree, BOOL fCallCB)
  857. {
  858. LPCITEMIDLIST pidlReal = _GetPIDLParam((LPARAM)pidlToFree, iItem);
  859. if (fCallCB)
  860. {
  861. CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlReal);
  862. }
  863. ILFree(pidlToFree); // NULL in owner data case
  864. }
  865. // NOTE: many keys are handled as accelerators
  866. void CDefView::HandleKeyDown(LV_KEYDOWN *pnmhdr)
  867. {
  868. // REVIEW: these are things not handled by accelerators, see if we can
  869. // make them all based on accelerators
  870. switch (pnmhdr->wVKey)
  871. {
  872. case VK_ESCAPE:
  873. if (_bHaveCutStuff)
  874. OleSetClipboard(NULL);
  875. break;
  876. }
  877. }
  878. // This function checks to see if we are in virtual mode or not. If we are in
  879. // virtual mode, we always need to ask our folder we are viewing for the item and
  880. // not the listview.
  881. LPCITEMIDLIST CDefView::_GetPIDL(int i)
  882. {
  883. if (_IsOwnerData())
  884. {
  885. LPCITEMIDLIST pidl = NULL;
  886. CallCB(SFVM_GETITEMIDLIST, i, (LPARAM)&pidl);
  887. return pidl;
  888. }
  889. return (LPCITEMIDLIST)LVUtil_GetLParam(_hwndListview, i);
  890. }
  891. LPCITEMIDLIST CDefView::_GetPIDLParam(LPARAM lParam, int i)
  892. {
  893. return lParam ? (LPCITEMIDLIST)lParam : _GetPIDL(i);
  894. }
  895. // returns an array of LPCITEMIDLIST for objects in the view (selected or all)
  896. // the "focused" item is always in array entry 0. this array contains poitners to pidls
  897. // owned stored in the listview, so YOU SHOULD NOT FREE THEM OR MESS WITH THEM IN ANYWAY.
  898. // this also implies the lifetime of this array must be shorter than the listview
  899. // data it points to. that is if the view changes under you you are hosed.
  900. //
  901. // Notes: this function returns LP*C*ITEMIDLIST. The caller is not
  902. // supposed alter or delete them. Their lifetime are very short (until the
  903. // list view is modified).
  904. typedef struct
  905. {
  906. LPCITEMIDLIST pidl;
  907. POINT pt;
  908. int iItem;
  909. } POS_SORT_INFO;
  910. // standard compare returns
  911. // -1 1 < 2
  912. // 0 1 = 2
  913. // 1 1 > 2
  914. //
  915. // NOTE: in the RTL_MIRRORED_WINDOW case the coords are reversed for us
  916. int _CmpTopToBottomLeftToRight(POS_SORT_INFO *psi1, POS_SORT_INFO *psi2, LPARAM lParam)
  917. {
  918. int iCmp = psi1->pt.y - psi2->pt.y;
  919. if (0 == iCmp)
  920. {
  921. iCmp = psi1->pt.x - psi2->pt.x;
  922. }
  923. return iCmp;
  924. }
  925. int _CmpLeftToRightTopToBottom(POS_SORT_INFO *psi1, POS_SORT_INFO *psi2, LPARAM lParam)
  926. {
  927. int iCmp = psi1->pt.x - psi2->pt.x;
  928. if (0 == iCmp)
  929. {
  930. iCmp = psi1->pt.y - psi2->pt.y;
  931. }
  932. return iCmp;
  933. }
  934. CDPA<POS_SORT_INFO>::_PFNDPACOMPARE _GetSortFunction(HWND hwndListview)
  935. {
  936. if (GetWindowStyle(hwndListview) & LVS_ALIGNLEFT)
  937. {
  938. return _CmpLeftToRightTopToBottom; // desktop LV_VIEW_ICON case
  939. }
  940. else
  941. {
  942. UINT uViewMode = ListView_GetView(hwndListview);
  943. switch (uViewMode)
  944. {
  945. case LV_VIEW_DETAILS:
  946. case LV_VIEW_LIST:
  947. return _CmpLeftToRightTopToBottom;
  948. case LV_VIEW_TILE:
  949. case LV_VIEW_ICON:
  950. default:
  951. return _CmpTopToBottomLeftToRight;
  952. }
  953. }
  954. }
  955. UINT CDefView::_GetItemArray(LPCITEMIDLIST apidl[], UINT capidl, UINT uWhat)
  956. {
  957. UINT cItems = 0;
  958. if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_SELECTION)
  959. {
  960. cItems = ListView_GetSelectedCount(_hwndListview);
  961. }
  962. else if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_CHECKED)
  963. {
  964. int iItem = ListView_GetItemCount(_hwndListview) - 1;
  965. for (; iItem >= 0; iItem--)
  966. {
  967. if (ListView_GetCheckState(_hwndListview, iItem))
  968. cItems++;
  969. }
  970. }
  971. else
  972. {
  973. cItems = ListView_GetItemCount(_hwndListview);
  974. }
  975. if (apidl)
  976. {
  977. UINT uType = (SVGIO_SELECTION == (uWhat & SVGIO_TYPE_MASK)) ? LVNI_SELECTED : LVNI_ALL;
  978. BOOL bArrayFilled = FALSE; // gets set on success of the sort code path
  979. // optimize the 1 case, the sort below is not needed
  980. if (!(SVGIO_FLAG_VIEWORDER & uWhat) && (capidl > 1))
  981. {
  982. CDPA<POS_SORT_INFO> dpaItemInfo;
  983. // pick a grow size of capidl so that we get a single alloc
  984. // when we add the first item to the array
  985. if (dpaItemInfo.Create(capidl))
  986. {
  987. POS_SORT_INFO *ppsi = new POS_SORT_INFO[capidl];
  988. if (ppsi)
  989. {
  990. UINT iDPAIndex = 0;
  991. for (int iListView = ListView_GetNextItem(_hwndListview, -1, uType);
  992. (iListView >= 0) && (iDPAIndex < capidl);
  993. iListView = ListView_GetNextItem(_hwndListview, iListView, uType))
  994. {
  995. // if we want checked then it must be checked, otherwise just return (or skip)
  996. if ((SVGIO_CHECKED != (uWhat & SVGIO_TYPE_MASK)) || ListView_GetCheckState(_hwndListview, iListView))
  997. {
  998. ppsi[iDPAIndex].pidl = _GetPIDL(iListView);
  999. ppsi[iDPAIndex].iItem = iListView;
  1000. ListView_GetItemPosition(_hwndListview, iListView, &ppsi[iDPAIndex].pt);
  1001. // this may fail, but we catch that case below
  1002. dpaItemInfo.SetPtr(iDPAIndex, &ppsi[iDPAIndex]);
  1003. iDPAIndex++;
  1004. }
  1005. }
  1006. // make sure the DPA got all of the items, if not
  1007. // we fall through to the unsorted case
  1008. if (dpaItemInfo.GetPtrCount() == capidl)
  1009. {
  1010. dpaItemInfo.Sort(_GetSortFunction(_hwndListview), 0);
  1011. int iFirstItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
  1012. // compute the start index in the dpa based on iFirstItem. this is to
  1013. // rotate the array so that iFirstItem is first in the list
  1014. for (iDPAIndex = 0; iDPAIndex < (UINT)dpaItemInfo.GetPtrCount(); iDPAIndex++)
  1015. {
  1016. if (dpaItemInfo.FastGetPtr(iDPAIndex)->iItem == iFirstItem)
  1017. {
  1018. break; // iDPAIndex setup for loop below
  1019. }
  1020. }
  1021. for (int i = 0; i < dpaItemInfo.GetPtrCount(); i++, iDPAIndex++)
  1022. {
  1023. if (iDPAIndex >= (UINT)dpaItemInfo.GetPtrCount())
  1024. iDPAIndex = 0; // wrap back to zero
  1025. apidl[i] = dpaItemInfo.FastGetPtr(iDPAIndex)->pidl;
  1026. }
  1027. bArrayFilled = TRUE; // we have the results we want
  1028. delete [] ppsi;
  1029. }
  1030. }
  1031. dpaItemInfo.Destroy();
  1032. }
  1033. }
  1034. if (!bArrayFilled)
  1035. {
  1036. UINT i = 0;
  1037. for (int iListView = ListView_GetNextItem(_hwndListview, -1, uType);
  1038. (iListView >= 0) && (i < capidl);
  1039. iListView = ListView_GetNextItem(_hwndListview, iListView, uType))
  1040. {
  1041. // if we want checked then it must be checked, otherwise just return (or skip)
  1042. if ((SVGIO_CHECKED != (uWhat & SVGIO_TYPE_MASK)) || ListView_GetCheckState(_hwndListview, iListView))
  1043. {
  1044. apidl[i++] = _GetPIDL(iListView);
  1045. }
  1046. }
  1047. }
  1048. }
  1049. return cItems;
  1050. }
  1051. //
  1052. // get the array of IDList from the selection and calls
  1053. // IShellFolder::GetUIObjectOf member to get the specified UI object
  1054. // interface.
  1055. //
  1056. HRESULT CDefView::_GetUIObjectFromItem(REFIID riid, void **ppv, UINT uWhat, BOOL fSetPoints)
  1057. {
  1058. LPCITEMIDLIST *apidl;
  1059. UINT cItems;
  1060. HRESULT hr;
  1061. if (SVGIO_SELECTION == (uWhat & SVGIO_TYPE_MASK))
  1062. {
  1063. hr = GetSelectedObjects(&apidl, &cItems);
  1064. }
  1065. else
  1066. {
  1067. hr = _GetItemObjects(&apidl, uWhat, &cItems);
  1068. }
  1069. if (SUCCEEDED(hr))
  1070. {
  1071. if (cItems)
  1072. {
  1073. hr = _pshf->GetUIObjectOf(_hwndMain, cItems, apidl, riid, 0, ppv);
  1074. if (SUCCEEDED(hr) && (IID_IDataObject == riid) && fSetPoints)
  1075. {
  1076. _SetPoints(cItems, apidl, (IDataObject *)*ppv);
  1077. }
  1078. LocalFree((HLOCAL)apidl);
  1079. }
  1080. else
  1081. hr = E_INVALIDARG;
  1082. }
  1083. return hr;
  1084. }
  1085. // If the browser has a Tree then we want to use explore.
  1086. UINT CDefView::_GetExplorerFlag()
  1087. {
  1088. return IsExplorerBrowser(_psb) ? CMF_EXPLORE : 0;
  1089. }
  1090. // creates a selection object out of the current selection.
  1091. IShellItemArray* CDefView::_CreateSelectionShellItemArray(void)
  1092. {
  1093. IShellItemArray *pSelectionObj = NULL;
  1094. LPCITEMIDLIST *apidl;
  1095. UINT cItems;
  1096. if (SUCCEEDED(_GetItemObjects(&apidl, SVGIO_SELECTION | SVGIO_FLAG_VIEWORDER, &cItems)) && cItems)
  1097. {
  1098. SHCreateShellItemArray(NULL, _pshf, cItems, apidl, &pSelectionObj);
  1099. LocalFree(apidl);
  1100. }
  1101. return pSelectionObj;
  1102. }
  1103. DWORD CDefView::_AttributesFromSel(DWORD dwAttributesNeeded)
  1104. {
  1105. // If this gets hit then chances are it's a performance problem...
  1106. //
  1107. if (_fSelectionChangePending)
  1108. {
  1109. TraceMsg(TF_WARNING, "Potential perf badness: may be asking for attributes during OnLVNUpdateItem!");
  1110. if (_pSelectionShellItemArray)
  1111. ATOMICRELEASE(_pSelectionShellItemArray);
  1112. _pSelectionShellItemArray = _CreateSelectionShellItemArray();
  1113. }
  1114. DWORD dwAttributes = 0;
  1115. if (_pSelectionShellItemArray)
  1116. {
  1117. _pSelectionShellItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, dwAttributesNeeded, &dwAttributes);
  1118. }
  1119. return dwAttributes;
  1120. }
  1121. // IContextMenuSite:
  1122. // Defview's context menu implementation isn't very clean. As a temporary step towards
  1123. // cleaning it up (CONTEXT and BACK_CONTEXT are intermingled), use the new DOCONTEXTMENUPOPUP range
  1124. //
  1125. HRESULT CDefView::DoContextMenuPopup(IUnknown* punkCM, UINT fFlags, POINT pt)
  1126. {
  1127. return _DoContextMenuPopup(punkCM, fFlags, pt, FALSE);
  1128. }
  1129. HRESULT CDefView::_DoContextMenuPopup(IUnknown* punk, UINT fFlags, POINT pt, BOOL fListviewItem)
  1130. {
  1131. IContextMenu* pcm;
  1132. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IContextMenu, &pcm));
  1133. if (SUCCEEDED(hr))
  1134. {
  1135. hr = E_OUTOFMEMORY;
  1136. HMENU hmContext = CreatePopupMenu();
  1137. if (hmContext)
  1138. {
  1139. fFlags |= _GetExplorerFlag();
  1140. if (0 > GetKeyState(VK_SHIFT))
  1141. fFlags |= CMF_EXTENDEDVERBS;
  1142. IContextMenu3* pcm3;
  1143. if (SUCCEEDED(pcm->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3))))
  1144. {
  1145. fFlags |= CMF_ICM3;
  1146. pcm3->Release();
  1147. }
  1148. // Give the context menu a site if it doesn't have one already
  1149. IUnknown* punkSite;
  1150. if (SUCCEEDED(IUnknown_GetSite(pcm, IID_PPV_ARG(IUnknown, &punkSite))))
  1151. {
  1152. punkSite->Release();
  1153. }
  1154. else
  1155. {
  1156. IUnknown_SetSite(pcm, SAFECAST(this, IShellView2*));
  1157. }
  1158. hr = pcm->QueryContextMenu(hmContext, 0, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST, fFlags);
  1159. if (SUCCEEDED(hr))
  1160. {
  1161. // Must preinitialize to NULL; Adaptec Easy CD Creator 3.5 does not
  1162. // null out the pointer on failure.
  1163. ICommDlgBrowser2 *pcdb2 = NULL;
  1164. _psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2));
  1165. // If this is the common dialog browser, we need to make the
  1166. // default command "Select" so that double-clicking (which is
  1167. // open in common dialog) makes sense.
  1168. if (_IsCommonDialog())
  1169. {
  1170. // make sure this is an item
  1171. if (fListviewItem)
  1172. {
  1173. HMENU hmSelect = SHLoadPopupMenu(HINST_THISDLL, POPUP_COMMDLG_POPUPMERGE);
  1174. // If we have a pointer to the ICommDlgBrowser2 interface
  1175. // query if this interface wants to change the text of the
  1176. // default verb. This interface is needed in the common print
  1177. // dialog to change the default text from 'Select' to 'Print'.
  1178. if (pcdb2)
  1179. {
  1180. WCHAR szTextW[MAX_PATH] = {0};
  1181. if (pcdb2->GetDefaultMenuText(this, szTextW, ARRAYSIZE(szTextW)) == S_OK)
  1182. {
  1183. MENUITEMINFO mi = {0};
  1184. mi.cbSize = sizeof(mi);
  1185. mi.fMask = MIIM_TYPE;
  1186. mi.fType = MFT_STRING;
  1187. mi.dwTypeData = szTextW;
  1188. SetMenuItemInfo(hmSelect, 0, MF_BYPOSITION, &mi);
  1189. }
  1190. }
  1191. // NOTE: Since commdlg always eats the default command,
  1192. // we don't care what id we assign hmSelect, as long as it
  1193. // doesn't conflict with any other context menu id.
  1194. // SFVIDM_CONTEXT_FIRST-1 won't conflict with anyone.
  1195. Shell_MergeMenus(hmContext, hmSelect, 0,
  1196. (UINT)(SFVIDM_BACK_CONTEXT_FIRST-1), (UINT)-1,
  1197. MM_ADDSEPARATOR);
  1198. SetMenuDefaultItem(hmContext, 0, MF_BYPOSITION);
  1199. DestroyMenu(hmSelect);
  1200. }
  1201. }
  1202. _SHPrettyMenu(hmContext);
  1203. // If this is the common dialog browser 2, we need inform it
  1204. // the context menu is has started. This notifiction is use in
  1205. // the common print dialog on NT which hosts the printers folder.
  1206. // Common dialog want to relselect the printer object if the user
  1207. // selected the context menu from the background.
  1208. if (pcdb2)
  1209. {
  1210. pcdb2->Notify(this, CDB2N_CONTEXTMENU_START);
  1211. }
  1212. // To reduce some menu message forwarding, throw away _pcmFile if we have one
  1213. // (Since we can't have a TrackPopupMenu and a File menu open at the same time)
  1214. IUnknown_SetSite(_pcmFile, NULL);
  1215. ATOMICRELEASE(_pcmFile);
  1216. // stash pcm in _pcmContextMenuPopup so we can forward menu messages
  1217. ASSERT(NULL==_pcmContextMenuPopup);
  1218. _pcmContextMenuPopup = pcm;
  1219. _pcmContextMenuPopup->AddRef();
  1220. int idDefault = GetMenuDefaultItem(hmContext, MF_BYCOMMAND, 0);
  1221. int idCmd = TrackPopupMenu(hmContext,
  1222. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  1223. pt.x, pt.y, 0, _hwndView, NULL);
  1224. ATOMICRELEASE(_pcmContextMenuPopup);
  1225. if ((idCmd == idDefault) &&
  1226. _OnDefaultCommand() == S_OK)
  1227. {
  1228. // commdlg browser ate the default command
  1229. }
  1230. else if (idCmd == 0)
  1231. {
  1232. // No item selected
  1233. }
  1234. else if (idCmd >= SFVIDM_BACK_CONTEXT_FIRST && idCmd <= SFVIDM_BACK_CONTEXT_LAST)
  1235. {
  1236. idCmd -= SFVIDM_BACK_CONTEXT_FIRST;
  1237. // We need to special case the rename command (just in case a legacy contextmenu impl relied on this behavior)
  1238. TCHAR szCommandString[64];
  1239. ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString));
  1240. if (lstrcmpi(szCommandString, c_szRename) == 0)
  1241. {
  1242. DoRename();
  1243. }
  1244. else
  1245. {
  1246. CMINVOKECOMMANDINFOEX ici = { 0 };
  1247. ici.cbSize = sizeof(ici);
  1248. ici.hwnd = _hwndMain;
  1249. ici.lpVerb = IntToPtr_(LPCSTR, idCmd);
  1250. ici.nShow = SW_NORMAL;
  1251. ici.ptInvoke = pt;
  1252. ici.fMask |= CMIC_MASK_PTINVOKE | CMIC_MASK_FLAG_LOG_USAGE;
  1253. // record if shift or control was being held down
  1254. SetICIKeyModifiers(&ici.fMask);
  1255. _InvokeContextMenu(pcm, &ici);
  1256. }
  1257. }
  1258. else
  1259. {
  1260. RIPMSG(FALSE, "CDefView::DoContextMenuPopup - Some IContextMenu inserted an ID out of our range. Ignoring.");
  1261. }
  1262. // If this is the common dialog browser 2, we need inform it
  1263. // the context menu is done. This notifiction is use in
  1264. // the common print dialog on NT which hosts the printers folder.
  1265. // Common dialog want to relselect the printer object if the user
  1266. // selected the context menu from the background.
  1267. if (pcdb2)
  1268. {
  1269. pcdb2->Notify(this, CDB2N_CONTEXTMENU_DONE);
  1270. pcdb2->Release();
  1271. }
  1272. }
  1273. DestroyMenu(hmContext);
  1274. }
  1275. // Always remove the site even if we didn't set it -- once used, the IContextMenu is dead.
  1276. IUnknown_SetSite(pcm, NULL);
  1277. pcm->Release();
  1278. }
  1279. return hr;
  1280. }
  1281. void CDefView::ContextMenu(DWORD dwPos)
  1282. {
  1283. int iItem;
  1284. UINT fFlags = 0;
  1285. POINT pt;
  1286. if (SHRestricted(REST_NOVIEWCONTEXTMENU))
  1287. {
  1288. return;
  1289. }
  1290. // if shell32's global copy of the stopwatch mode is not init'd yet, init it now.
  1291. if (g_dwStopWatchMode == 0xffffffff)
  1292. g_dwStopWatchMode = StopWatchMode();
  1293. if (g_dwStopWatchMode)
  1294. StopWatch_Start(SWID_MENU, TEXT("Defview ContextMenu Start"), SPMODE_SHELL | SPMODE_DEBUGOUT);
  1295. if (IsWindowVisible(_hwndListview) && (IsChildOrSelf(_hwndListview, GetFocus()) == S_OK))
  1296. {
  1297. // Find the selected item
  1298. iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
  1299. }
  1300. else
  1301. {
  1302. iItem = -1;
  1303. }
  1304. if (dwPos == (DWORD) -1)
  1305. {
  1306. if (iItem != -1)
  1307. {
  1308. RECT rc;
  1309. int iItemFocus = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED|LVNI_SELECTED);
  1310. if (iItemFocus == -1)
  1311. iItemFocus = iItem;
  1312. //
  1313. // Note that LV_GetItemRect returns it in client coordinate!
  1314. //
  1315. ListView_GetItemRect(_hwndListview, iItemFocus, &rc, LVIR_ICON);
  1316. pt.x = (rc.left + rc.right) / 2;
  1317. pt.y = (rc.top + rc.bottom) / 2;
  1318. }
  1319. else
  1320. {
  1321. pt.x = pt.y = 0;
  1322. }
  1323. MapWindowPoints(_hwndListview, HWND_DESKTOP, &pt, 1);
  1324. }
  1325. else
  1326. {
  1327. pt.x = GET_X_LPARAM(dwPos);
  1328. pt.y = GET_Y_LPARAM(dwPos);
  1329. }
  1330. IContextMenu* pcm;
  1331. LPARAM uemEvent;
  1332. if (iItem == -1)
  1333. {
  1334. DECLAREWAITCURSOR;
  1335. SetWaitCursor();
  1336. // use the background context menu wrapper
  1337. GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm));
  1338. ResetWaitCursor();
  1339. // set the max range for these, so that they are unaffected...
  1340. uemEvent = _IsDesktop() ? UIBL_CTXTDESKBKGND : UIBL_CTXTDEFBKGND;
  1341. }
  1342. else
  1343. {
  1344. fFlags |= CMF_CANRENAME;
  1345. // One or more items are selected, let the folder add menuitems.
  1346. _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcm));
  1347. uemEvent = _IsDesktop() ? UIBL_CTXTDESKITEM : UIBL_CTXTDEFITEM;
  1348. }
  1349. UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UICONTEXT, uemEvent);
  1350. if (g_dwStopWatchMode)
  1351. StopWatch_Stop(SWID_MENU, TEXT("Defview ContextMenu Stop (!SafeToDefaultVerb)"), SPMODE_SHELL | SPMODE_DEBUGOUT);
  1352. if (IsSafeToDefaultVerb() && pcm)
  1353. {
  1354. _DoContextMenuPopup(pcm, fFlags, pt, iItem != -1);
  1355. }
  1356. ATOMICRELEASE(pcm);
  1357. }
  1358. BOOL CDefView::_GetItemSpacing(ITEMSPACING *pis)
  1359. {
  1360. DWORD dwSize = ListView_GetItemSpacing(_hwndListview, TRUE);
  1361. pis->cxSmall = GET_X_LPARAM(dwSize);
  1362. pis->cySmall = GET_Y_LPARAM(dwSize);
  1363. dwSize = ListView_GetItemSpacing(_hwndListview, FALSE);
  1364. pis->cxLarge = GET_X_LPARAM(dwSize);
  1365. pis->cyLarge = GET_Y_LPARAM(dwSize);
  1366. return _fs.ViewMode != FVM_ICON;
  1367. }
  1368. BOOL _DidDropOnRecycleBin(IDataObject *pdtobj)
  1369. {
  1370. CLSID clsid;
  1371. return SUCCEEDED(DataObj_GetDropTarget(pdtobj, &clsid)) &&
  1372. IsEqualCLSID(clsid, CLSID_RecycleBin);
  1373. }
  1374. void CDefView::_SetPoints(UINT cidl, LPCITEMIDLIST *apidl, IDataObject *pdtobj)
  1375. {
  1376. POINT pt;
  1377. GetDragPoint(&pt);
  1378. ::SetPositionItemsPoints(SAFECAST(this, IFolderView*), apidl, cidl, pdtobj, &pt);
  1379. }
  1380. LRESULT CDefView::_OnBeginDrag(NM_LISTVIEW * pnm)
  1381. {
  1382. POINT ptOffset = pnm->ptAction; // hwndLV client coords
  1383. // This DefView is used as a drag source so we need to see if it's
  1384. // is hosted by something that can disguise the action.
  1385. if (S_OK != _ZoneCheck(PUAF_NOUI, URLACTION_SHELL_WEBVIEW_VERB))
  1386. {
  1387. // This DefView is hosted in HTML, so we need to turn off the
  1388. // ability of this defview from being a drag source.
  1389. return 0;
  1390. }
  1391. _OnDelayedSelectionChange();
  1392. if (FAILED(_FireEvent(DISPID_BEGINDRAG))) // script canceles dragging
  1393. return 0;
  1394. DWORD dwEffect = _AttributesFromSel(SFGAO_CANDELETE | DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY);
  1395. // Turn on DROPEFFECT_MOVE for any deleteable item
  1396. // (this is so the item can be dragged to the recycle bin)
  1397. if (SFGAO_CANDELETE & dwEffect)
  1398. {
  1399. dwEffect |= DROPEFFECT_MOVE;
  1400. }
  1401. // Mask out all attribute bits that aren't also DROPEFFECT bits:
  1402. dwEffect &= (DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY);
  1403. // Somebody began dragging in our window, so store that fact
  1404. _bDragSource = TRUE;
  1405. // save away the anchor point
  1406. _ptDragAnchor = pnm->ptAction;
  1407. LVUtil_ClientToLV(_hwndListview, &_ptDragAnchor);
  1408. ClientToScreen(_hwndListview, &ptOffset); // now in screen
  1409. // can't use _pdoSelection here since we need fSetPoints
  1410. IDataObject *pdtobj;
  1411. if (SUCCEEDED(_GetUIObjectFromItem(IID_PPV_ARG(IDataObject, &pdtobj), SVGIO_SELECTION, TRUE)))
  1412. {
  1413. // Give the source a chance to alter the drop effect.
  1414. CallCB(SFVM_ALTERDROPEFFECT, (WPARAM)&dwEffect, (LPARAM)pdtobj);
  1415. if (DAD_SetDragImageFromWindow(_hwndListview, &ptOffset, pdtobj))
  1416. {
  1417. if (DRAGDROP_S_DROP == SHDoDragDrop(_hwndMain, pdtobj, NULL, dwEffect, &dwEffect))
  1418. {
  1419. if (S_OK != CallCB(SFVM_DIDDRAGDROP, (WPARAM)dwEffect, (LPARAM)pdtobj))
  1420. {
  1421. // the return of DROPEFFECT_MOVE tells us we need to delete the data
  1422. // see if we need to do that now...
  1423. // NOTE: we can't trust the dwEffect return result from DoDragDrop() because
  1424. // some apps (adobe photoshop) return this when you drag a file on them that
  1425. // they intend to open. so we invented the "PreformedEffect" as a way to
  1426. // know what the real value is, that is why we test both of these.
  1427. if ((DROPEFFECT_MOVE == dwEffect) &&
  1428. (DROPEFFECT_MOVE == DataObj_GetDWORD(pdtobj, g_cfPerformedDropEffect, DROPEFFECT_NONE)))
  1429. {
  1430. // enable UI for the recycle bin case (the data will be lost
  1431. // as the recycle bin really can't recycle stuff that is not files)
  1432. UINT uFlags = _DidDropOnRecycleBin(pdtobj) ? 0 : CMIC_MASK_FLAG_NO_UI;
  1433. SHInvokeCommandOnDataObject(_hwndMain, NULL, pdtobj, uFlags,c_szDeleteA);
  1434. }
  1435. }
  1436. }
  1437. //
  1438. // We need to clear the dragged image only if we still have the drag context.
  1439. //
  1440. DAD_SetDragImage((HIMAGELIST)-1, NULL);
  1441. }
  1442. pdtobj->Release();
  1443. }
  1444. _bDragSource = FALSE; // All done dragging
  1445. return 0;
  1446. }
  1447. void CDefView::_FocusOnSomething(void)
  1448. {
  1449. int iFocus = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
  1450. if (iFocus == -1)
  1451. {
  1452. if (ListView_GetItemCount(_hwndListview) > 0)
  1453. {
  1454. // set the focus on the first item.
  1455. ListView_SetItemState(_hwndListview, 0, LVIS_FOCUSED, LVIS_FOCUSED);
  1456. }
  1457. }
  1458. }
  1459. HRESULT CDefView::_InvokeContextMenu(IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici)
  1460. {
  1461. TCHAR szWorkingDir[MAX_PATH];
  1462. CHAR szWorkingDirAnsi[MAX_PATH];
  1463. if (SUCCEEDED(CallCB(SFVM_GETWORKINGDIR, ARRAYSIZE(szWorkingDir), (LPARAM)szWorkingDir)))
  1464. {
  1465. // Fill in both the ansi working dir and the unicode one
  1466. // since we don't know who's gonna be processing this thing.
  1467. SHUnicodeToAnsi(szWorkingDir, szWorkingDirAnsi, ARRAYSIZE(szWorkingDirAnsi));
  1468. pici->lpDirectory = szWorkingDirAnsi;
  1469. pici->lpDirectoryW = szWorkingDir;
  1470. pici->fMask |= CMIC_MASK_UNICODE;
  1471. }
  1472. // In case the ptInvoke field was not already set for us, guess where
  1473. // that could be. (dli) maybe should let the caller set all points
  1474. if (!(pici->fMask & CMIC_MASK_PTINVOKE))
  1475. {
  1476. if (GetCursorPos(&pici->ptInvoke))
  1477. pici->fMask |= CMIC_MASK_PTINVOKE;
  1478. }
  1479. pici->fMask |= CMIC_MASK_ASYNCOK;
  1480. _OnDelayedSelectionChange();
  1481. HRESULT hr = _FireEvent(DISPID_VERBINVOKED);
  1482. if (SUCCEEDED(hr))
  1483. hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)pici);
  1484. return hr;
  1485. }
  1486. DWORD CDefView::_GetNeededSecurityAction(void)
  1487. {
  1488. DWORD dwUrlAction = 0;
  1489. // this blanks out everything that has SFGAO_FOLDER -- what about zip/cab?
  1490. // this check is used in context menu ops to see if the default verb is
  1491. // trusted -- for the zip/cab case, we DO trust the default verb since we
  1492. // expose them as folders throughout the rest of the shell.
  1493. if (!(SFGAO_FOLDER & _AttributesFromSel(SFGAO_FOLDER)))
  1494. {
  1495. // If we are hosted by Trident, Zone Check Action.
  1496. // this is how we detect if we are hosted in an IFRAME or OBJECT tag
  1497. IUnknown *punk;
  1498. if (SUCCEEDED(_psb->QueryInterface(IID_IIsWebBrowserSB, (void **)&punk)))
  1499. {
  1500. dwUrlAction = URLACTION_SHELL_VERB;
  1501. punk->Release();
  1502. }
  1503. else if (_fGetWindowLV)
  1504. {
  1505. // If we are using WebView, Zone Check Action.
  1506. dwUrlAction = URLACTION_SHELL_WEBVIEW_VERB;
  1507. }
  1508. }
  1509. return dwUrlAction;
  1510. }
  1511. HRESULT CDefView::_ZoneCheck(DWORD dwFlags, DWORD dwAllowAction)
  1512. {
  1513. HRESULT hr = S_OK;
  1514. DWORD dwUrlAction = _GetNeededSecurityAction();
  1515. if (dwUrlAction && (dwUrlAction != dwAllowAction))
  1516. {
  1517. // First check if our parent wants to generate our context (Zone/URL).
  1518. IInternetHostSecurityManager *pihsm;
  1519. hr = IUnknown_QueryService(_psb, IID_IInternetHostSecurityManager, IID_PPV_ARG(IInternetHostSecurityManager, &pihsm));
  1520. if (FAILED(hr) && _cFrame._pDocView)
  1521. {
  1522. // Yes, so if we are in WebView mode, check the instance of Trident that is
  1523. // displaying the WebView content, because that content could discuise the DefView
  1524. // and make the user unknowingly do something bad.
  1525. hr = IUnknown_QueryService(_cFrame._pDocView, IID_IInternetHostSecurityManager, IID_PPV_ARG(IInternetHostSecurityManager, &pihsm));
  1526. }
  1527. if (SUCCEEDED(hr))
  1528. {
  1529. // This is the prefered way to do the zone check.
  1530. hr = ZoneCheckHost(pihsm, dwUrlAction, dwFlags | PUAF_FORCEUI_FOREGROUND);
  1531. pihsm->Release();
  1532. }
  1533. else
  1534. {
  1535. // No, we were not able to get the interface. So fall back to zone checking the
  1536. // URL that comes from the pidl we are at.
  1537. TCHAR szPathSource[MAX_PATH];
  1538. if (_GetPath(szPathSource))
  1539. {
  1540. // Try to get a IInternetSecurityMgrSite so our UI will be modal.
  1541. IInternetSecurityMgrSite *pisms;
  1542. if (SUCCEEDED(IUnknown_QueryService(_psb, SID_STopLevelBrowser, IID_PPV_ARG(IInternetSecurityMgrSite, &pisms))))
  1543. {
  1544. // TODO: Have this object support IInternetSecurityMgrSite in case our parent doesn't provide one.
  1545. // Make that code support ::GetWindow() and ::EnableModless() or we won't get the modal behavior
  1546. // needed for VB and AOL.
  1547. hr = ZoneCheckUrl(szPathSource, dwUrlAction, dwFlags | PUAF_ISFILE | PUAF_FORCEUI_FOREGROUND, pisms);
  1548. pisms->Release();
  1549. }
  1550. }
  1551. }
  1552. }
  1553. return hr;
  1554. }
  1555. BOOL CDefView::IsSafeToDefaultVerb(void)
  1556. {
  1557. return S_OK == _ZoneCheck(PUAF_WARN_IF_DENIED, 0);
  1558. }
  1559. HRESULT CDefView::_InvokeContextMenuVerb(IContextMenu* pcm, LPCSTR pszVerb, UINT uKeyFlags, DWORD dwCMMask)
  1560. {
  1561. DECLAREWAITCURSOR;
  1562. SetWaitCursor();
  1563. CMINVOKECOMMANDINFOEX ici = {0};
  1564. ici.cbSize = sizeof(ici);
  1565. ici.hwnd = _hwndMain;
  1566. ici.nShow = SW_NORMAL;
  1567. ici.fMask = dwCMMask;
  1568. // Get the point where the double click is invoked.
  1569. GetMsgPos(&ici.ptInvoke);
  1570. ici.fMask |= CMIC_MASK_PTINVOKE;
  1571. // record if shift or control was being held down
  1572. SetICIKeyModifiers(&ici.fMask);
  1573. IUnknown_SetSite(pcm, SAFECAST(this, IOleCommandTarget *));
  1574. // Security note: we assume all non default verbs safe
  1575. HRESULT hr;
  1576. if (pszVerb ||
  1577. (IsSafeToDefaultVerb() && SUCCEEDED(_FireEvent(DISPID_DEFAULTVERBINVOKED))))
  1578. {
  1579. WCHAR szVerbW[128];
  1580. if (pszVerb)
  1581. {
  1582. ici.lpVerb = pszVerb;
  1583. SHAnsiToUnicode(pszVerb, szVerbW, ARRAYSIZE(szVerbW));
  1584. ici.lpVerbW = szVerbW;
  1585. ici.fMask |= CMIC_MASK_UNICODE;
  1586. }
  1587. HMENU hmenu = CreatePopupMenu();
  1588. if (hmenu)
  1589. {
  1590. UINT fFlags = _GetExplorerFlag();
  1591. if (NULL == pszVerb)
  1592. fFlags |= CMF_DEFAULTONLY; // optmization
  1593. // SHIFT + dbl click does a Explore by default
  1594. if (uKeyFlags & LVKF_SHIFT)
  1595. fFlags |= CMF_EXPLORE;
  1596. pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, fFlags);
  1597. if (pszVerb)
  1598. hr = S_OK;
  1599. else
  1600. {
  1601. UINT idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, GMDI_GOINTOPOPUPS);
  1602. if (idCmd == -1)
  1603. {
  1604. hr = E_FAIL;
  1605. }
  1606. else
  1607. {
  1608. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST);
  1609. hr = S_OK;
  1610. }
  1611. }
  1612. if (SUCCEEDED(hr))
  1613. {
  1614. // need to reset it so that user won't blow off the app starting cursor
  1615. // also so that if we won't leave the wait cursor up when we're not waiting
  1616. // (like in a prop sheet or something that has a message loop
  1617. ResetWaitCursor();
  1618. hcursor_wait_cursor_save = NULL;
  1619. hr = _InvokeContextMenu(pcm, &ici);
  1620. }
  1621. DestroyMenu(hmenu);
  1622. }
  1623. else
  1624. {
  1625. hr = E_OUTOFMEMORY;
  1626. }
  1627. }
  1628. else
  1629. {
  1630. hr = E_ACCESSDENIED;
  1631. }
  1632. IUnknown_SetSite(pcm, NULL);
  1633. if (hcursor_wait_cursor_save)
  1634. ResetWaitCursor();
  1635. return hr;
  1636. }
  1637. HRESULT CDefView::_InvokeContextMenuVerbOnSelection(LPCSTR pszVerb, UINT uKeyFlags, DWORD dwCMMask)
  1638. {
  1639. if (NULL == pszVerb)
  1640. {
  1641. if (_IsDesktop())
  1642. UEMFireEvent(&UEMIID_SHELL, UEME_UISCUT, UEMF_XEVENT, -1, (LPARAM)-1);
  1643. if (S_OK == _OnDefaultCommand())
  1644. {
  1645. return S_FALSE; /* commdlg browser ate the message */
  1646. }
  1647. if (uKeyFlags & LVKF_ALT)
  1648. pszVerb = "properties";
  1649. }
  1650. // Dealing with context menus can be slow
  1651. DECLAREWAITCURSOR;
  1652. SetWaitCursor();
  1653. IContextMenu *pcmSel;
  1654. HRESULT hr = _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcmSel));
  1655. if (SUCCEEDED(hr))
  1656. _LogDesktopLinksAndRegitems();
  1657. ResetWaitCursor(); // undo the cursor since the below _Invoke needs to control cursor shape
  1658. if (SUCCEEDED(hr))
  1659. {
  1660. hr = _InvokeContextMenuVerb(pcmSel, pszVerb, uKeyFlags, dwCMMask);
  1661. pcmSel->Release();
  1662. }
  1663. return hr;
  1664. }
  1665. //
  1666. // We want to keep track of which desktop regitems and links the user is using.
  1667. // This lets the desktop cleaner app know which ones can safely be
  1668. // cleaned up.
  1669. //
  1670. // Be careful - there are many race conditions... You have to do the
  1671. // GetSelectedObjects before any InvokeCommands are done, because the
  1672. // InvokeCommand calls might change the selection state. But you also
  1673. // have to use the result of GetSelectedObjects immediately, because
  1674. // it returns pidls that are owned by the defview, and if a filesys
  1675. // notify comes in, you might end up with pidls that have been freed.
  1676. //
  1677. // So we just do all the work up front, before actually invoking anything.
  1678. // This does mean that if the invoke fails, we still log the usage,
  1679. // but that seems like a small price to pay.
  1680. //
  1681. void CDefView::_LogDesktopLinksAndRegitems()
  1682. {
  1683. if (_IsDesktop())
  1684. {
  1685. LPCITEMIDLIST *apidl;
  1686. UINT cItems;
  1687. if (SUCCEEDED(GetSelectedObjects(&apidl, &cItems)) && apidl)
  1688. {
  1689. for (UINT i = 0; i < cItems; i++)
  1690. {
  1691. TCHAR szDisplayName[GUIDSTR_MAX+2]; // +2 for leading "::"
  1692. if (SUCCEEDED(DisplayNameOf(_pshf, apidl[i], SHGDN_INFOLDER | SHGDN_FORPARSING,
  1693. szDisplayName, ARRAYSIZE(szDisplayName))))
  1694. {
  1695. if (_Attributes(apidl[i], SFGAO_LINK))
  1696. {
  1697. // its a link
  1698. UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szDisplayName);
  1699. }
  1700. else if (IsRegItemName(szDisplayName, NULL))
  1701. {
  1702. // it's a regitem
  1703. UEMFireEvent(&UEMIID_SHELL, UEME_RUNPATH, UEMF_XEVENT, -1, (LPARAM)szDisplayName);
  1704. }
  1705. }
  1706. }
  1707. LocalFree((HLOCAL)apidl);
  1708. }
  1709. }
  1710. }
  1711. void CDefView::_UpdateColData(CBackgroundColInfo *pbgci)
  1712. {
  1713. UINT iItem = ListView_MapIDToIndex(_hwndListview, pbgci->GetId());
  1714. if (iItem != -1)
  1715. {
  1716. UINT uiCol = pbgci->GetColumn();
  1717. if (_IsColumnInListView(uiCol))
  1718. {
  1719. UINT iVisCol = _RealToVisibleCol(uiCol);
  1720. ListView_SetItemText(_hwndListview, iItem, iVisCol, (LPTSTR)pbgci->GetText());
  1721. }
  1722. }
  1723. delete pbgci;
  1724. }
  1725. void CDefView::_UpdateIcon(LPITEMIDLIST pidl, UINT iIcon)
  1726. {
  1727. int i = _FindItem(pidl, NULL, FALSE, FALSE);
  1728. if (i >= 0)
  1729. {
  1730. LV_ITEM item = {0};
  1731. item.mask = LVIF_IMAGE;
  1732. item.iItem = i;
  1733. item.iImage = iIcon;
  1734. ListView_SetItem(_hwndListview, &item);
  1735. }
  1736. ILFree(pidl);
  1737. }
  1738. void CDefView::_UpdateOverlay(int iList, int iOverlay)
  1739. {
  1740. ASSERT (iList >= 0);
  1741. if (_IsOwnerData())
  1742. {
  1743. // In the ownerdata case, tell the owner that the overlay changed
  1744. CallCB(SFVM_SETICONOVERLAY, iList, iOverlay);
  1745. ListView_RedrawItems(_hwndListview, iList, iList);
  1746. }
  1747. else
  1748. {
  1749. ListView_SetItemState(_hwndListview, iList, INDEXTOOVERLAYMASK(iOverlay), LVIS_OVERLAYMASK);
  1750. }
  1751. }
  1752. HRESULT CDefView::_GetIconAsync(LPCITEMIDLIST pidl, int *piIcon, BOOL fCanWait)
  1753. {
  1754. HRESULT hr;
  1755. // if we are not an owner-data view then try to extract asynchronously
  1756. UINT flags = (_IsOwnerData() ? 0 : GIL_ASYNC);
  1757. if (GIL_ASYNC & flags)
  1758. {
  1759. hr = SHMapIDListToImageListIndexAsync(_pScheduler, _pshf, pidl, flags, _AsyncIconTaskCallback, this, NULL, piIcon, NULL);
  1760. if (SUCCEEDED(hr))
  1761. {
  1762. return S_OK; // indicate that we got the real icon
  1763. }
  1764. else if (hr == E_PENDING)
  1765. {
  1766. hr = S_FALSE; // the icon index we have is a placeholder
  1767. }
  1768. }
  1769. else
  1770. {
  1771. hr = SHGetIconFromPIDL(_pshf, _psi, pidl, flags, piIcon);
  1772. }
  1773. return hr;
  1774. }
  1775. void CDefView::_AsyncIconTaskCallback(LPCITEMIDLIST pidl, void *pvData, void *pvHint, INT iIconIndex, INT iOpenIconIndex)
  1776. {
  1777. CDefView *pdv = (CDefView *)pvData;
  1778. ASSERT(pdv);
  1779. if (pdv)
  1780. {
  1781. LPITEMIDLIST pidlClone = ILClone(pidl);
  1782. if (pidlClone && !PostMessage(pdv->_hwndView, WM_DSV_UPDATEICON, (WPARAM)pidlClone, (LPARAM)iIconIndex))
  1783. ILFree(pidlClone);
  1784. }
  1785. }
  1786. HRESULT CDefView::_GetOverlayIndexAsync(LPCITEMIDLIST pidl, int iList)
  1787. {
  1788. IRunnableTask * pTask;
  1789. HRESULT hr = CIconOverlayTask_CreateInstance(this, pidl, iList, &pTask);
  1790. if (SUCCEEDED(hr))
  1791. {
  1792. _AddTask(pTask, TOID_DVIconOverlay, 0, TASK_PRIORITY_GET_ICON, ADDTASK_ATEND);
  1793. pTask->Release();
  1794. }
  1795. return hr;
  1796. }
  1797. //
  1798. // Returns: if the cursor is over a listview item, its index; otherwise, -1.
  1799. //
  1800. int CDefView::_HitTest(const POINT *ppt, BOOL fIgnoreEdge)
  1801. {
  1802. LV_HITTESTINFO info;
  1803. if (!_IsListviewVisible())
  1804. return -1;
  1805. info.pt = *ppt;
  1806. int iRet = ListView_HitTest(_hwndListview, &info);
  1807. if (-1 != iRet && fIgnoreEdge)
  1808. {
  1809. // If we're in one of these large image area modes, and the caller says
  1810. // it's okay to ignore "edge" hits, then pretend the user is over nothing.
  1811. // Tile mode only ignores the left edge of the icon, since the right edge
  1812. // is all text (and usually shorter than the tile width anyway).
  1813. if (_IsTileMode() && (info.flags & LVHT_ONLEFTSIDEOFICON))
  1814. iRet = -1;
  1815. else if (_IsImageMode() && (info.flags & (LVHT_ONLEFTSIDEOFICON|LVHT_ONRIGHTSIDEOFICON)))
  1816. iRet = -1;
  1817. }
  1818. return iRet;
  1819. }
  1820. void CDefView::_OnGetInfoTip(NMLVGETINFOTIP *plvn)
  1821. {
  1822. if (!SHShowInfotips())
  1823. return;
  1824. LPCITEMIDLIST pidl = _GetPIDL(plvn->iItem);
  1825. if (pidl)
  1826. {
  1827. ATOMICRELEASE(_pBackgroundInfoTip); // Release the previous value, if any
  1828. HRESULT hr = E_FAIL;
  1829. _pBackgroundInfoTip = new CBackgroundInfoTip(&hr, plvn);
  1830. if (_pBackgroundInfoTip && SUCCEEDED(hr))
  1831. {
  1832. LPITEMIDLIST pidlFolder = _GetViewPidl();
  1833. if (pidlFolder)
  1834. {
  1835. CStatusBarAndInfoTipTask *pTask;
  1836. hr = CStatusBarAndInfoTipTask_CreateInstance(pidlFolder, pidl, 0, 0, _pBackgroundInfoTip, _hwndView, _pScheduler, &pTask);
  1837. if (SUCCEEDED(hr))
  1838. {
  1839. if (_pScheduler)
  1840. {
  1841. // make sure there are no other background infotip tasks going on...
  1842. _pScheduler->RemoveTasks(TOID_DVBackgroundInfoTip, ITSAT_DEFAULT_LPARAM, FALSE);
  1843. }
  1844. _AddTask(pTask, TOID_DVBackgroundInfoTip, 0, TASK_PRIORITY_INFOTIP, ADDTASK_ATEND);
  1845. pTask->Release();
  1846. }
  1847. ILFree(pidlFolder);
  1848. }
  1849. }
  1850. }
  1851. // Do not show a tip while the processing is happening in the background
  1852. plvn->pszText[0] = 0;
  1853. }
  1854. HRESULT CDefView::_OnViewWindowActive()
  1855. {
  1856. IShellView *psv = _psvOuter ? _psvOuter : SAFECAST(this, IShellView*);
  1857. return _psb->OnViewWindowActive(psv);
  1858. }
  1859. // CLR_NONE is a special value that never matches a valid RGB
  1860. COLORREF g_crAltColor = CLR_NONE; // uninitialized magic value
  1861. COLORREF g_crAltEncryptedColor = CLR_NONE; // uninitialized magic value
  1862. DWORD GetRegColor(COLORREF clrDefault, LPCTSTR pszName, COLORREF *pValue)
  1863. {
  1864. // Fetch the alternate color (for compression) if supplied.
  1865. if (*pValue == CLR_NONE) // initialized yet?
  1866. {
  1867. DWORD cbData = sizeof(*pValue);
  1868. if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, pszName, NULL, pValue, &cbData)))
  1869. {
  1870. *pValue = clrDefault; // default value
  1871. }
  1872. }
  1873. return *pValue;
  1874. }
  1875. LRESULT CDefView::_GetDisplayInfo(LV_DISPINFO *plvdi)
  1876. {
  1877. LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem);
  1878. if (pidl && (plvdi->item.mask & (DEFVIEW_LISTCALLBACK_FLAGS)))
  1879. {
  1880. ASSERT(IsValidPIDL(pidl));
  1881. ASSERT(plvdi->item.iSubItem != 0 ? ViewRequiresColumns(_fs.ViewMode) : TRUE);
  1882. LV_ITEM item = {0};
  1883. item.mask = plvdi->item.mask & (DEFVIEW_LISTCALLBACK_FLAGS);
  1884. item.iItem = plvdi->item.iItem;
  1885. item.iImage = plvdi->item.iImage = -1; // for iSubItem != 0 case
  1886. if ((plvdi->item.iSubItem == 0) && (item.mask & LVIF_IMAGE))
  1887. {
  1888. // If the folder supports IShellIconOverlay then only need to ask for ghosted, else
  1889. // we need to do the old stuff...
  1890. DWORD uFlags = _Attributes(pidl, _psio ? SFGAO_GHOSTED : SFGAO_LINK | SFGAO_SHARE | SFGAO_GHOSTED);
  1891. // set the mask
  1892. item.mask |= LVIF_STATE;
  1893. plvdi->item.mask |= LVIF_STATE;
  1894. item.stateMask = LVIS_OVERLAYMASK;
  1895. // Pick the right overlay icon. The order is significant.
  1896. item.state = 0;
  1897. if (_psio)
  1898. {
  1899. int iOverlayIndex = SFV_ICONOVERLAY_UNSET;
  1900. if (_IsOwnerData())
  1901. {
  1902. // Note: we are passing SFV_ICONOVERLAY_DEFAULT here because
  1903. // some owners do not respond to SFVM_GETICONOVERLAY might return
  1904. // iOverlayIndex unchanged and it will get
  1905. iOverlayIndex = SFV_ICONOVERLAY_DEFAULT;
  1906. CallCB(SFVM_GETICONOVERLAY, plvdi->item.iItem, (LPARAM)&iOverlayIndex);
  1907. if (iOverlayIndex > 0)
  1908. {
  1909. item.stateMask |= LVIS_OVERLAYMASK;
  1910. item.state |= INDEXTOOVERLAYMASK(iOverlayIndex);
  1911. }
  1912. }
  1913. if (iOverlayIndex == SFV_ICONOVERLAY_UNSET)
  1914. {
  1915. iOverlayIndex = OI_ASYNC;
  1916. HRESULT hr = _psio->GetOverlayIndex(pidl, &iOverlayIndex);
  1917. if (E_PENDING == hr)
  1918. _GetOverlayIndexAsync(pidl, item.iItem);
  1919. else if (S_OK == hr)
  1920. {
  1921. ASSERT(iOverlayIndex >= 0);
  1922. ASSERT(iOverlayIndex < MAX_OVERLAY_IMAGES);
  1923. // In the owner data case, tell the owner we got an Overlay index
  1924. if (_IsOwnerData())
  1925. CallCB(SFVM_SETICONOVERLAY, item.iItem, iOverlayIndex);
  1926. item.state = INDEXTOOVERLAYMASK(iOverlayIndex);
  1927. }
  1928. }
  1929. }
  1930. else
  1931. {
  1932. if (uFlags & SFGAO_LINK)
  1933. {
  1934. item.state = INDEXTOOVERLAYMASK(II_LINK - II_OVERLAYFIRST + 1);
  1935. }
  1936. else if (uFlags & SFGAO_SHARE)
  1937. {
  1938. item.state = INDEXTOOVERLAYMASK(II_SHARE - II_OVERLAYFIRST + 1);
  1939. }
  1940. }
  1941. if (uFlags & SFGAO_GHOSTED)
  1942. {
  1943. item.stateMask |= LVIS_CUT;
  1944. item.state |= LVIS_CUT;
  1945. }
  1946. else
  1947. {
  1948. item.stateMask |= LVIS_CUT;
  1949. item.state &= ~LVIS_CUT;
  1950. }
  1951. plvdi->item.stateMask = item.stateMask;
  1952. plvdi->item.state = item.state;
  1953. // Get the image
  1954. if (_IsOwnerData() && !_IsImageMode())
  1955. {
  1956. CallCB(SFVM_GETITEMICONINDEX, plvdi->item.iItem, (LPARAM)&item.iImage);
  1957. }
  1958. if (item.iImage == -1)
  1959. {
  1960. if (_IsImageMode())
  1961. {
  1962. // Check if the item is visible. If it is not, then the image was
  1963. // probably asked for by the thumbnail read ahead task, in which case, we set a
  1964. // different priority.
  1965. if (ListView_IsItemVisible(_hwndListview, item.iItem))
  1966. {
  1967. if (S_OK != ExtractItem((UINT*)&item.iImage, item.iItem, pidl, TRUE, FALSE, PRIORITY_P5))
  1968. {
  1969. _CacheDefaultThumbnail(pidl, &item.iImage);
  1970. }
  1971. }
  1972. else
  1973. {
  1974. // Likely from read ahead task.
  1975. ExtractItem((UINT*)&item.iImage, item.iItem, pidl, TRUE, FALSE, PRIORITY_READAHEAD_EXTRACT);
  1976. }
  1977. }
  1978. else
  1979. _GetIconAsync(pidl, &item.iImage, TRUE);
  1980. }
  1981. plvdi->item.iImage = item.iImage;
  1982. }
  1983. if (item.mask & LVIF_TEXT)
  1984. {
  1985. if (plvdi->item.cchTextMax)
  1986. *plvdi->item.pszText = 0;
  1987. // Note that we do something different for index 0 = NAME
  1988. if (plvdi->item.iSubItem == 0)
  1989. {
  1990. DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER, plvdi->item.pszText, plvdi->item.cchTextMax);
  1991. }
  1992. else
  1993. {
  1994. // on the first slow column complete all of the other columns (assumed to be slow)
  1995. // now so we get good caching from the col handlers
  1996. UINT iReal = _VisibleToRealCol(plvdi->item.iSubItem);
  1997. if (_vs.GetColumnState(iReal) & SHCOLSTATE_SLOW)
  1998. {
  1999. UINT cCols = _vs.GetColumnCount();
  2000. for (UINT iVisCol = plvdi->item.iSubItem; iReal < cCols; iReal++)
  2001. {
  2002. if (_IsColumnInListView(iReal))
  2003. {
  2004. ASSERT(_vs.GetColumnState(iReal) & SHCOLSTATE_SLOW);
  2005. UINT uId = ListView_MapIndexToID(_hwndListview, plvdi->item.iItem);
  2006. // in the async case set the text to nothing (NULL). this will
  2007. // prevent another call to ListView_GetItemText() from invoking us
  2008. ListView_SetItemText(_hwndListview, plvdi->item.iItem, iVisCol++, NULL);
  2009. IRunnableTask *pTask;
  2010. if (SUCCEEDED(CExtendedColumnTask_CreateInstance(this, pidl, uId, _fmt, iReal, &pTask)))
  2011. {
  2012. _AddTask(pTask, TOID_DVBackgroundEnum, 0, TASK_PRIORITY_FILE_PROPS, ADDTASK_ATEND);
  2013. pTask->Release();
  2014. }
  2015. }
  2016. }
  2017. return 0; // bail!
  2018. }
  2019. DETAILSINFO di;
  2020. di.pidl = pidl;
  2021. di.fmt = _fmt;
  2022. di.iImage = -1; // Assume for now no image...
  2023. if (SUCCEEDED(_GetDetailsHelper(iReal, &di)))
  2024. {
  2025. StrRetToBuf(&di.str, pidl, plvdi->item.pszText, plvdi->item.cchTextMax);
  2026. if ((di.iImage != -1) && (plvdi->item.mask & LVIF_IMAGE))
  2027. {
  2028. plvdi->item.iImage = di.iImage;
  2029. }
  2030. }
  2031. }
  2032. }
  2033. if ((item.mask & LVIF_GROUPID) && _fGroupView)
  2034. {
  2035. plvdi->item.mask |= LVIF_GROUPID;
  2036. plvdi->item.iGroupId = _GetGroupForItem(plvdi->item.iItem, pidl);
  2037. }
  2038. if (item.mask & LVIF_COLUMNS)
  2039. {
  2040. if (_fScrolling)
  2041. {
  2042. // Ignore any column requests if we're currently scrolling. However, don't
  2043. // return zero for the number of columns, return I_COLUMNSCALLBACK instead, because
  2044. // we do still want listview to call us back to ask for them if it is every displaying
  2045. // this guy while we're not scrolling.
  2046. plvdi->item.cColumns = I_COLUMNSCALLBACK;
  2047. plvdi->item.puColumns = NULL;
  2048. _fRequestedTileDuringScroll = TRUE;
  2049. }
  2050. else
  2051. {
  2052. if (_IsOwnerData())
  2053. {
  2054. AddColumns();
  2055. if (plvdi->item.cColumns > 1)
  2056. {
  2057. // hack special case for the find folder
  2058. if (_MapSCIDToColumn(&SCID_DIRECTORY, &plvdi->item.puColumns[0]))
  2059. plvdi->item.cColumns = 1;
  2060. }
  2061. }
  2062. else
  2063. {
  2064. BOOL fGotColumns = FALSE;
  2065. // Start a task to extract the important columns for this item.
  2066. LPCITEMIDLIST pidl = _GetPIDL(plvdi->item.iItem);
  2067. if (pidl)
  2068. {
  2069. plvdi->item.cColumns = TILEVIEWLINES;
  2070. if (SUCCEEDED(_PeekColumnsCache(NULL, 0, pidl, plvdi->item.puColumns, &plvdi->item.cColumns)))
  2071. {
  2072. // Make sure columns are loaded
  2073. AddColumns();
  2074. _FixupColumnsForTileview(plvdi->item.puColumns, plvdi->item.cColumns);
  2075. fGotColumns = TRUE;
  2076. }
  2077. else
  2078. {
  2079. IRunnableTask *pTask;
  2080. UINT uId = ListView_MapIndexToID(_hwndListview, plvdi->item.iItem);
  2081. if (SUCCEEDED(CFileTypePropertiesTask_CreateInstance(this, pidl, TILEVIEWLINES, uId, &pTask))) //pidl gets cloned
  2082. {
  2083. _AddTask(pTask, TOID_DVFileTypeProperties, 0, TASK_PRIORITY_FILE_PROPS, ADDTASK_ATEND);
  2084. pTask->Release();
  2085. }
  2086. }
  2087. }
  2088. if (!fGotColumns)
  2089. {
  2090. plvdi->item.cColumns = 0;
  2091. plvdi->item.puColumns = NULL;
  2092. }
  2093. }
  2094. }
  2095. }
  2096. if (plvdi->item.iSubItem == 0)
  2097. plvdi->item.mask |= LVIF_DI_SETITEM; // always store the name
  2098. }
  2099. return 0;
  2100. }
  2101. int CALLBACK GroupCompare(int iGroup1, int iGroup2, void *pvData)
  2102. {
  2103. ICategorizer* pcat = (ICategorizer*)pvData;
  2104. HRESULT hr = pcat->CompareCategory(CATSORT_DEFAULT, (DWORD)iGroup1, (DWORD)iGroup2);
  2105. return ShortFromResult(hr);
  2106. }
  2107. void CDefView::_OnCategoryTaskAdd()
  2108. {
  2109. _fInBackgroundGrouping = TRUE;
  2110. }
  2111. void CDefView::_OnCategoryTaskDone()
  2112. {
  2113. _fInBackgroundGrouping = FALSE;
  2114. LONG cTasksCompleteLocal;
  2115. ENTERCRITICAL;
  2116. {
  2117. _fGroupingMsgInFlight = FALSE;
  2118. cTasksCompleteLocal = _cTasksCompleted;
  2119. _cTasksCompleted = 0;
  2120. // swap the DPAs
  2121. HDPA hdpaTemp = _hdpaGroupingListBackup;
  2122. _hdpaGroupingListBackup = _hdpaGroupingListActive;
  2123. _hdpaGroupingListActive = hdpaTemp;
  2124. }
  2125. LEAVECRITICAL;
  2126. LONG cEntries = DPA_GetPtrCount(_hdpaGroupingListBackup);
  2127. // now process everything in _hdpaGroupingListEmpty
  2128. for (int i = 0; i < cEntries; i++)
  2129. {
  2130. CBackgroundGroupInfo* pbggi = (CBackgroundGroupInfo*)DPA_GetPtr(_hdpaGroupingListBackup, i);
  2131. if (pbggi)
  2132. {
  2133. if (pbggi->VerifyGroupExists(_hwndListview, _pcat))
  2134. {
  2135. int iItem = ListView_MapIDToIndex(_hwndListview, pbggi->GetId());
  2136. if (iItem != -1)
  2137. {
  2138. LVITEM lvi = {0};
  2139. lvi.mask = LVIF_GROUPID;
  2140. lvi.iGroupId = pbggi->GetGroupId();
  2141. lvi.iItem = iItem;
  2142. ListView_SetItem(_hwndListview, &lvi);
  2143. }
  2144. }
  2145. delete pbggi;
  2146. }
  2147. }
  2148. DPA_DeleteAllPtrs(_hdpaGroupingListBackup);
  2149. if (_pidlSelectAndPosition)
  2150. {
  2151. POINT pt = {0}; // Don't care: Groups don't have a position
  2152. SelectAndPositionItem(_pidlSelectAndPosition, _uSelectAndPositionFlags, &pt);
  2153. Pidl_Set(&_pidlSelectAndPosition, NULL);
  2154. _uSelectAndPositionFlags = 0;
  2155. }
  2156. for (LONG i = 0; i < cTasksCompleteLocal; i++)
  2157. {
  2158. _ShowSearchUI(FALSE);
  2159. _GlobeAnimation(FALSE);
  2160. }
  2161. }
  2162. DWORD CDefView::_GetGroupForItem(int iItem, LPCITEMIDLIST pidl)
  2163. {
  2164. DWORD dwGroup = I_GROUPIDNONE;
  2165. if (_fGroupView)
  2166. {
  2167. if (_fSlowGroup)
  2168. {
  2169. UINT uId = ListView_MapIndexToID(_hwndListview, iItem);
  2170. IRunnableTask* pTask;
  2171. if (SUCCEEDED(CCategoryTask_Create(this, pidl, uId, &pTask)))
  2172. {
  2173. // Need to get the globe/search stuff kicked off while within the CreateViewWindow2 call,
  2174. // so do it here instead of a posted message in the above constructor
  2175. _OnCategoryTaskAdd();
  2176. _AddTask(pTask, TOID_DVBackgroundGroup, 0, TASK_PRIORITY_GROUP, ADDTASK_ATEND);
  2177. pTask->Release();
  2178. }
  2179. }
  2180. else
  2181. {
  2182. _pcat->GetCategory(1, (LPCITEMIDLIST*)&pidl, &dwGroup);
  2183. if (!ListView_HasGroup(_hwndListview, dwGroup))
  2184. {
  2185. CATEGORY_INFO ci;
  2186. _pcat->GetCategoryInfo(dwGroup, &ci);
  2187. LVINSERTGROUPSORTED igrp;
  2188. igrp.pfnGroupCompare = GroupCompare;
  2189. igrp.pvData = (void *)_pcat;
  2190. igrp.lvGroup.cbSize = sizeof(LVGROUP);
  2191. igrp.lvGroup.mask = LVGF_HEADER | LVGF_GROUPID;
  2192. igrp.lvGroup.pszHeader= ci.wszName;
  2193. igrp.lvGroup.iGroupId = (int)dwGroup;
  2194. ListView_InsertGroupSorted(_hwndListview, &igrp);
  2195. }
  2196. }
  2197. }
  2198. return dwGroup;
  2199. }
  2200. BOOL CDefView::_EnsureSCIDCache()
  2201. {
  2202. BOOL bRet = FALSE;
  2203. if (_hdsaSCIDCache)
  2204. {
  2205. bRet = TRUE;
  2206. }
  2207. else if (_pshf2)
  2208. {
  2209. _hdsaSCIDCache = DSA_Create(sizeof(SHCOLUMNID), 30);
  2210. if (_hdsaSCIDCache)
  2211. {
  2212. SHCOLUMNID scid;
  2213. for (UINT iCol = 0; SUCCEEDED(_pshf2->MapColumnToSCID(iCol, &scid)); iCol++)
  2214. {
  2215. // ignore failure, just means we can't find the thing
  2216. DSA_AppendItem(_hdsaSCIDCache, &scid);
  2217. }
  2218. bRet = TRUE;
  2219. }
  2220. }
  2221. return bRet;
  2222. }
  2223. BOOL CDefView::_MapSCIDToColumn(const SHCOLUMNID *pscid, UINT *pnColumn)
  2224. {
  2225. BOOL bRet = FALSE;
  2226. *pnColumn = 0;
  2227. if (_EnsureSCIDCache())
  2228. {
  2229. UINT cCol = DSA_GetItemCount(_hdsaSCIDCache);
  2230. for (UINT iCol = 0; iCol < cCol; iCol++)
  2231. {
  2232. SHCOLUMNID scid;
  2233. DSA_GetItem(_hdsaSCIDCache, iCol, &scid);
  2234. if (IsEqualSCID(*pscid, scid))
  2235. {
  2236. *pnColumn = iCol;
  2237. bRet = TRUE;
  2238. break;
  2239. }
  2240. }
  2241. }
  2242. return bRet;
  2243. }
  2244. HRESULT CDefView::_GetPropertyUI(IPropertyUI **pppui)
  2245. {
  2246. if (!_ppui)
  2247. SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
  2248. return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL;
  2249. }
  2250. HRESULT CDefView::_PeekColumnsCache(PTSTR pszPath, UINT cchPath, LPCITEMIDLIST pidl, UINT rguColumns[], UINT *pcColumns)
  2251. {
  2252. TCHAR szPath[MAX_PATH];
  2253. if (pszPath == NULL)
  2254. {
  2255. pszPath = szPath;
  2256. cchPath = ARRAYSIZE(szPath);
  2257. }
  2258. // NOTE - need to replace this with GetDetailsEx(SCID_CANONICALTYPE) to support
  2259. // caching properly. then we dont need to sniff attributes or the name in order to get
  2260. // a nice caching index.
  2261. HRESULT hr = DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, pszPath, cchPath);
  2262. if (SUCCEEDED(hr))
  2263. {
  2264. LPCWSTR pszExt = _Attributes(pidl, SFGAO_FOLDER) ? NULL : PathFindExtension(pszPath);
  2265. hr = E_FAIL;
  2266. // Check file table cache:
  2267. ENTERCRITICAL;
  2268. SHCOLUMNID *pscidCached;
  2269. UINT cSCIDCached = pszExt ? LookupFileSCIDs(pszExt, &pscidCached) : 0; //Handle no extension case by not looking up in cache
  2270. LEAVECRITICAL;
  2271. if (cSCIDCached) // Found the SCIDs cache in the file table
  2272. {
  2273. UINT nFilled = 0;
  2274. // Found it... we don't need to check the registry
  2275. for (UINT nSCID = 0; nSCID < cSCIDCached && nFilled < *pcColumns; nSCID++)
  2276. {
  2277. if (_MapSCIDToColumn(&pscidCached[nSCID], &rguColumns[nFilled]))
  2278. nFilled++;
  2279. }
  2280. *pcColumns = nFilled;
  2281. LocalFree(pscidCached);
  2282. hr = S_OK;
  2283. }
  2284. }
  2285. return hr;
  2286. }
  2287. // Get the important columns for this guy, based on file extension
  2288. // pidl: The pidl of the item in question
  2289. // puColumns[]: The array which will get filled with important column indicies
  2290. // pcColumns IN: specifies how big rguColumns[] is. OUT: specified how many slots got filled.
  2291. HRESULT CDefView::_GetImportantColumns(LPCITEMIDLIST pidl, UINT rguColumns[], UINT *pcColumns)
  2292. {
  2293. TCHAR szPath[MAX_PATH];
  2294. // We need to ensure that the columns are loaded here
  2295. if (!_bLoadedColumns)
  2296. {
  2297. DWORD_PTR lRes = 0;
  2298. if (!SendMessageTimeout(_hwndView, WM_DSV_ENSURE_COLUMNS_LOADED, 0, 0, SMTO_NORMAL, 5000, &lRes) || lRes == 0)
  2299. return E_FAIL;
  2300. }
  2301. HRESULT hr = _PeekColumnsCache(szPath, ARRAYSIZE(szPath), pidl, rguColumns, pcColumns);
  2302. if (FAILED(hr))
  2303. {
  2304. IQueryAssociations *pqa;
  2305. hr = _pshf->GetUIObjectOf(_hwndMain, 1, &pidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
  2306. if (SUCCEEDED(hr))
  2307. {
  2308. IPropertyUI *ppui;
  2309. hr = _GetPropertyUI(&ppui);
  2310. if (SUCCEEDED(hr))
  2311. {
  2312. TCHAR szProps[INFOTIPSIZE];
  2313. DWORD cchOut = ARRAYSIZE(szProps);
  2314. hr = pqa->GetString(0, ASSOCSTR_TILEINFO, NULL, szProps, &cchOut);
  2315. if (SUCCEEDED(hr))
  2316. {
  2317. UINT cNumColumns = 0; // # of items in rguColumns
  2318. UINT cSCID = 0; // # of items in rgscid
  2319. SHCOLUMNID rgscid[64]; // reasonable upper bound
  2320. ULONG chEaten = 0; // loop variable ParsePropertyName updates this
  2321. while ((cSCID < ARRAYSIZE(rgscid)) &&
  2322. SUCCEEDED(ppui->ParsePropertyName(szProps, &rgscid[cSCID].fmtid, &rgscid[cSCID].pid, &chEaten)))
  2323. {
  2324. // Map SCID to a column (while there are more column slots)
  2325. if ((cNumColumns < *pcColumns) &&
  2326. _MapSCIDToColumn(&rgscid[cSCID], &rguColumns[cNumColumns]))
  2327. {
  2328. cNumColumns++;
  2329. cSCID++;
  2330. }
  2331. }
  2332. *pcColumns = cNumColumns;
  2333. LPCWSTR pszExt = _Attributes(pidl, SFGAO_FOLDER) ? NULL : PathFindExtension(szPath);
  2334. if (pszExt)
  2335. {
  2336. // cache for future use, except if there's no extension (cache key)
  2337. ENTERCRITICAL;
  2338. AddFileSCIDs(pszExt, rgscid, cSCID);
  2339. LEAVECRITICAL;
  2340. }
  2341. }
  2342. ppui->Release();
  2343. }
  2344. pqa->Release();
  2345. }
  2346. }
  2347. return hr;
  2348. }
  2349. void CDefView::_FixupColumnsForTileview(UINT *rguColumns, UINT cColumns)
  2350. {
  2351. // Make sure these columns are added to listview (ie. visible).
  2352. // And then map the columns in rguColumns from real columns to visible columns
  2353. for (UINT i = 0; i < cColumns; i++)
  2354. {
  2355. _AddTileColumn(rguColumns[i]);
  2356. }
  2357. // Now, also add the sorted by column, if it hasn't been added yet.
  2358. if (!_fSetTileViewSortedCol)
  2359. {
  2360. _fSetTileViewSortedCol = TRUE;
  2361. // It's ok if we don't actually set it. It's the thought that counts.
  2362. if (_vs._lParamSort != -1)
  2363. {
  2364. _AddTileColumn(_vs._lParamSort);
  2365. // And set it selected, if we're not in groupview
  2366. if (!_fGroupView)
  2367. {
  2368. ListView_SetSelectedColumn(_hwndListview, _RealToVisibleCol(_vs._lParamSort));
  2369. }
  2370. }
  2371. }
  2372. // This must be done after all the _AddTileColumns, or else the visible col #'s will be off.
  2373. for (UINT i = 0; i < cColumns; i++)
  2374. {
  2375. rguColumns[i] = _RealToVisibleCol(rguColumns[i]);
  2376. }
  2377. }
  2378. void CDefView::_SetImportantColumns(CBackgroundTileInfo *pbgTileInfo)
  2379. {
  2380. UINT cColumns = pbgTileInfo->GetColumnCount();
  2381. UINT *rguColumns = pbgTileInfo->GetColumns();
  2382. LVTILEINFO ti = {0};
  2383. ti.cbSize = sizeof(ti);
  2384. ti.cColumns = cColumns;
  2385. ti.puColumns = rguColumns;
  2386. ti.iItem = ListView_MapIDToIndex(_hwndListview, pbgTileInfo->GetId());
  2387. if (ti.iItem != -1)
  2388. {
  2389. _FixupColumnsForTileview(rguColumns, cColumns);
  2390. // have the listview store the per item tile info that we have computed
  2391. ListView_SetTileInfo(_hwndListview, &ti);
  2392. }
  2393. delete pbgTileInfo;
  2394. }
  2395. // Ensures if we're in tileview, that the tileviewinfo is set.
  2396. void CDefView::_SetView(UINT fvm)
  2397. {
  2398. // Update our internal state
  2399. _fs.ViewMode = fvm;
  2400. // Map the ViewMode into a listview mode
  2401. DWORD iView = LV_VIEW_ICON;
  2402. // Now switch the listview
  2403. switch (fvm)
  2404. {
  2405. case FVM_ICON:
  2406. case FVM_SMALLICON:
  2407. case FVM_THUMBNAIL:
  2408. case FVM_THUMBSTRIP:
  2409. iView = LV_VIEW_ICON;
  2410. break;
  2411. case FVM_LIST:
  2412. iView = LV_VIEW_LIST;
  2413. break;
  2414. case FVM_TILE:
  2415. iView = LV_VIEW_TILE;
  2416. break;
  2417. case FVM_DETAILS:
  2418. iView = LV_VIEW_DETAILS;
  2419. break;
  2420. default:
  2421. ASSERTMSG(FALSE, "_SetView got an invalid ViewMode!");
  2422. break;
  2423. }
  2424. if (iView == LV_VIEW_TILE)
  2425. {
  2426. RECT rcLabelMargin = {1, 1, 1, 1}; // This gives us some room around the label, so the focus rect doesn't clip part of the text
  2427. LVTILEVIEWINFO lvtvi = {0};
  2428. lvtvi.cbSize = sizeof(lvtvi);
  2429. lvtvi.dwMask = LVTVIM_TILESIZE | LVTVIM_COLUMNS | LVTVIM_LABELMARGIN;
  2430. lvtvi.dwFlags = LVTVIF_AUTOSIZE;
  2431. lvtvi.cLines = TILEVIEWLINES;
  2432. lvtvi.rcLabelMargin = rcLabelMargin;
  2433. ListView_SetTileViewInfo(_hwndListview, &lvtvi);
  2434. }
  2435. ListView_SetView(_hwndListview, iView);
  2436. _FireEvent(DISPID_VIEWMODECHANGED);
  2437. }
  2438. // rename the selection based on the new name for the renamed item
  2439. // this makes it easy to rename groups of files to a common base name
  2440. HRESULT CDefView::_DoBulkRename(LPCITEMIDLIST pidlNewName)
  2441. {
  2442. LPCITEMIDLIST *apidl;
  2443. UINT cItems;
  2444. HRESULT hr = _GetItemObjects(&apidl, SVGIO_SELECTION, &cItems);
  2445. if (SUCCEEDED(hr))
  2446. {
  2447. if (cItems > 1) // only interesting if more than 1
  2448. {
  2449. TCHAR szBase[MAX_PATH]; // seed file name used to generate other names
  2450. hr = DisplayNameOf(_pshf, pidlNewName, SHGDN_INFOLDER | SHGDN_FORPARSING, szBase, ARRAYSIZE(szBase));
  2451. if (SUCCEEDED(hr))
  2452. {
  2453. if (!SHGetAttributes(_pshf, pidlNewName, SFGAO_FOLDER))
  2454. PathRemoveExtension(szBase); // remove the extension, if it is a file
  2455. UINT cBase = 1; // one based counter, start at "File (1)"
  2456. // if input contains (#) use that as the sequence # base
  2457. LPWSTR psz = StrChr(szBase, TEXT('('));
  2458. if (psz)
  2459. {
  2460. cBase = StrToInt(psz + 1) + 1; // start at this in sequence
  2461. *psz = 0; // remove the (#) from the base name
  2462. }
  2463. PathRemoveBlanks(szBase); // clean away leading/trailing blanks
  2464. // start at 1, skipping the focused item, renaming all others in the array
  2465. for (UINT i = 1; (i < cItems) && SUCCEEDED(hr); i++)
  2466. {
  2467. TCHAR szOld[MAX_PATH];
  2468. hr = DisplayNameOf(_pshf, apidl[i], SHGDN_INFOLDER | SHGDN_FORPARSING, szOld, ARRAYSIZE(szOld));
  2469. if (SUCCEEDED(hr))
  2470. {
  2471. // Clone the pidl since isf->SetNameOf can result in synchronous update item
  2472. // that can free the ListView owned apidl[i].
  2473. LPITEMIDLIST pidlOldName = ILClone(apidl[i]);
  2474. if (pidlOldName)
  2475. {
  2476. // if the new name we produce conflicts with a name that
  2477. // already exists we will retry up to 100 times
  2478. for (UINT cRetry = 0; cRetry < 100; cRetry++)
  2479. {
  2480. WCHAR szName[MAX_PATH];
  2481. wnsprintf(szName, ARRAYSIZE(szName), TEXT("%s (%d)%s"), szBase, cBase, PathFindExtension(szOld));
  2482. hr = _pshf->SetNameOf(NULL, pidlOldName, szName, SHGDN_INFOLDER | SHGDN_FORPARSING, NULL);
  2483. if (SUCCEEDED(hr))
  2484. {
  2485. // force sync change notify update to make sure
  2486. // all renames come through (avoid UPDATEDIR)
  2487. SHChangeNotifyHandleEvents();
  2488. cBase++;
  2489. break; // did this one successfully
  2490. }
  2491. else if (HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr ||
  2492. HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
  2493. {
  2494. cBase++;
  2495. hr = S_OK; // and keep trying
  2496. }
  2497. else
  2498. {
  2499. break; // other error, exit
  2500. }
  2501. }
  2502. ILFree(pidlOldName);
  2503. }
  2504. }
  2505. }
  2506. }
  2507. }
  2508. LocalFree(apidl);
  2509. }
  2510. return hr;
  2511. }
  2512. LRESULT CDefView::_OnLVNotify(NM_LISTVIEW *plvn)
  2513. {
  2514. switch (plvn->hdr.code)
  2515. {
  2516. case NM_KILLFOCUS:
  2517. // force update on inactive to not ruin save bits
  2518. _OnStateChange(CDBOSC_KILLFOCUS);
  2519. if (GetForegroundWindow() != _hwndMain)
  2520. UpdateWindow(_hwndListview);
  2521. _fHasListViewFocus = FALSE;
  2522. _EnableDisableTBButtons();
  2523. break;
  2524. case NM_SETFOCUS:
  2525. {
  2526. if (!_fDestroying)
  2527. {
  2528. if (_cFrame.IsWebView()) // Do OLE stuff
  2529. {
  2530. UIActivate(SVUIA_ACTIVATE_FOCUS);
  2531. }
  2532. else
  2533. {
  2534. // We should call IShellBrowser::OnViewWindowActive() before
  2535. // calling its InsertMenus().
  2536. _OnViewWindowActive();
  2537. _OnStateChange(CDBOSC_SETFOCUS);
  2538. OnActivate(SVUIA_ACTIVATE_FOCUS);
  2539. _FocusOnSomething();
  2540. _UpdateStatusBar(FALSE);
  2541. }
  2542. _fHasListViewFocus = TRUE;
  2543. _EnableDisableTBButtons();
  2544. }
  2545. break;
  2546. }
  2547. case NM_RCLICK:
  2548. // on the shift+right-click case we want to deselect everything and select just our item if it is
  2549. // not already selected. if we dont do this, then listview gets confused (because he thinks
  2550. // shift means extend selection, but in the right click case it dosent!) and will bring up the
  2551. // context menu for whatever is currently selected instead of what the user just right clicked on.
  2552. if ((GetKeyState(VK_SHIFT) < 0) &&
  2553. (plvn->iItem >= 0) &&
  2554. !(ListView_GetItemState(_hwndListview, plvn->iItem, LVIS_SELECTED) & LVIS_SELECTED))
  2555. {
  2556. // clear any currently slected items
  2557. ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
  2558. // select the guy that was just right-clicked on
  2559. ListView_SetItemState(_hwndListview, plvn->iItem, LVIS_SELECTED, LVIS_SELECTED);
  2560. }
  2561. break;
  2562. case LVN_ENDSCROLL:
  2563. {
  2564. // This means we're scrolling. Ignore requests for LVIF_COLUMNS while we're
  2565. // scrolling to speed things up.
  2566. // We don't want to ignore requests for LVIF_COLUMNS when we're owner data, because
  2567. // owner data listviews always callback for info on what to display. (The result would
  2568. // be already-present tileinfo vanishing while scrolling, since we'd be ignoring requests
  2569. // for what to display)
  2570. if ((_fs.ViewMode == FVM_TILE) && !_IsOwnerData())
  2571. {
  2572. SetTimer(_hwndView, DV_IDTIMER_SCROLL_TIMEOUT, 250, NULL);
  2573. if (!_fScrolling)
  2574. {
  2575. _fScrolling = TRUE;
  2576. // We don't reset this on every LVN_ENDSCROLL - only if this is the first time
  2577. // we've scrolled since a stable (non-scrolling) state
  2578. _fRequestedTileDuringScroll = FALSE;
  2579. }
  2580. }
  2581. }
  2582. break;
  2583. case LVN_GETINFOTIP:
  2584. _OnGetInfoTip((NMLVGETINFOTIP *)plvn);
  2585. break;
  2586. case LVN_ITEMACTIVATE:
  2587. if (!_fDisabled)
  2588. {
  2589. //in win95 if user left clicks on one click activate icon and then right
  2590. //clicks on it (within double click time interval), the icon is launched
  2591. //and context menu appears on top of it -- it does not disappear.
  2592. //furthermore the context menu cannot be destroyed but stays on top of
  2593. //any window and items on it are not accessible. to avoid this
  2594. //send cancel mode to itself to destroy context before the icon is
  2595. //launched
  2596. if (_hwndView)
  2597. SendMessage(_hwndView, WM_CANCELMODE, 0, 0);
  2598. _InvokeContextMenuVerbOnSelection(NULL, ((NMITEMACTIVATE *)plvn)->uKeyFlags, CMIC_MASK_FLAG_LOG_USAGE);
  2599. }
  2600. break;
  2601. case NM_CUSTOMDRAW:
  2602. {
  2603. LPNMLVCUSTOMDRAW pcd = (LPNMLVCUSTOMDRAW)plvn;
  2604. switch (pcd->nmcd.dwDrawStage)
  2605. {
  2606. case CDDS_PREPAINT:
  2607. {
  2608. return _fShowCompColor ? CDRF_NOTIFYITEMDRAW : CDRF_DODEFAULT;
  2609. }
  2610. case CDDS_ITEMPREPAINT:
  2611. {
  2612. LRESULT lres = CDRF_DODEFAULT;
  2613. LPCITEMIDLIST pidl = _GetPIDLParam(pcd->nmcd.lItemlParam, (int)pcd->nmcd.dwItemSpec);
  2614. if (pidl)
  2615. {
  2616. DWORD dwAttribs = _Attributes(pidl, SFGAO_COMPRESSED | SFGAO_ENCRYPTED);
  2617. // only one or the other, can never be both
  2618. if (dwAttribs & SFGAO_COMPRESSED)
  2619. {
  2620. // default value of Blue
  2621. pcd->clrText = GetRegColor(RGB(0, 0, 255), TEXT("AltColor"), &g_crAltColor);
  2622. }
  2623. else if (dwAttribs & SFGAO_ENCRYPTED)
  2624. {
  2625. // default value Luna Mid Green
  2626. pcd->clrText = GetRegColor(RGB(19, 146, 13), TEXT("AltEncryptionColor"), &g_crAltEncryptedColor);
  2627. }
  2628. }
  2629. if (_IsImageMode() && pcd->nmcd.hdc && (_dwRecClrDepth <= 8))
  2630. {
  2631. HPALETTE hpal = NULL;
  2632. if (SUCCEEDED(_GetBrowserPalette(&hpal)))
  2633. {
  2634. // Since we are a child of the browser, we should always take a back seat to thier palette selection
  2635. _hpalOld = SelectPalette(pcd->nmcd.hdc, hpal, TRUE);
  2636. RealizePalette(pcd->nmcd.hdc);
  2637. lres |= CDRF_NOTIFYPOSTPAINT;
  2638. }
  2639. }
  2640. return lres;
  2641. }
  2642. case CDDS_ITEMPOSTPAINT:
  2643. if (_IsImageMode() && _hpalOld && pcd->nmcd.hdc)
  2644. {
  2645. SelectPalette(pcd->nmcd.hdc, _hpalOld, TRUE);
  2646. _hpalOld = NULL;
  2647. }
  2648. break;
  2649. }
  2650. }
  2651. return CDRF_DODEFAULT;
  2652. case LVN_BEGINDRAG:
  2653. case LVN_BEGINRDRAG:
  2654. if (_fDisabled)
  2655. return FALSE; /* commdlg doesn't want user dragging */
  2656. return _OnBeginDrag(plvn);
  2657. case LVN_ITEMCHANGING:
  2658. if (_fDisabled)
  2659. return TRUE;
  2660. break;
  2661. // Something changed in the listview. Delete any data that
  2662. // we might have cached away.
  2663. case LVN_ITEMCHANGED:
  2664. if (plvn->uChanged & LVIF_STATE)
  2665. {
  2666. if (!_fIgnoreItemChanged)
  2667. {
  2668. // The rest only cares about SELCHANGE messages (avoid LVIS_DRAGSELECT, etc)
  2669. if ((plvn->uNewState ^ plvn->uOldState) & (LVIS_SELECTED | LVIS_FOCUSED))
  2670. {
  2671. //if we are the drag source then dont send selection change message
  2672. if (!_bDragSource)
  2673. {
  2674. _OnStateChange(CDBOSC_SELCHANGE);
  2675. }
  2676. OnLVSelectionChange(plvn);
  2677. }
  2678. else if ((plvn->uNewState ^ plvn->uOldState) & (LVIS_STATEIMAGEMASK))
  2679. {
  2680. if (!_bDragSource)
  2681. {
  2682. _OnStateChange(CDBOSC_STATECHANGE);
  2683. }
  2684. }
  2685. }
  2686. }
  2687. break;
  2688. // owner data state changed: e.g. search results
  2689. case LVN_ODSTATECHANGED:
  2690. {
  2691. NM_ODSTATECHANGE *pnm = (NM_ODSTATECHANGE *)plvn;
  2692. // for now handle only selection changes
  2693. if ((pnm->uOldState ^ pnm->uNewState) & (LVIS_SELECTED | LVIS_FOCUSED))
  2694. {
  2695. _OnLVSelectionChange(-1, pnm->uOldState, pnm->uNewState, 0);
  2696. }
  2697. }
  2698. break;
  2699. case LVN_DELETEITEM:
  2700. OnListViewDelete(plvn->iItem, (LPITEMIDLIST)plvn->lParam, TRUE);
  2701. break;
  2702. case LVN_COLUMNCLICK:
  2703. // allow clicking on columns to set the sort order
  2704. if (_fGroupView)
  2705. {
  2706. BOOL fAllowArrange = TRUE;
  2707. UINT iRealColumn = _VisibleToRealCol(plvn->iSubItem);
  2708. SHCOLUMNID scid;
  2709. if (SUCCEEDED(_pshf2->MapColumnToSCID(iRealColumn, &scid)))
  2710. {
  2711. ICategoryProvider* pcp = NULL;
  2712. if (SUCCEEDED(_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp))))
  2713. {
  2714. // returns S_FALSE to remove.
  2715. if (S_FALSE == pcp->CanCategorizeOnSCID(&scid))
  2716. {
  2717. fAllowArrange = FALSE;
  2718. }
  2719. }
  2720. }
  2721. if (fAllowArrange)
  2722. _ArrangeBy(iRealColumn + SFVIDM_GROUPSFIRST);
  2723. }
  2724. else if (_pshf2 || _psd || HasCB())
  2725. {
  2726. LPARAM lParamSort = _vs._lParamSort;
  2727. LONG iLastColumnClick = _vs._iLastColumnClick,
  2728. iLastSortDirection = _vs._iDirection; // push sort state
  2729. // Folder doesn't know which columns are on or off, so communication with folder uses real col #s
  2730. UINT iRealColumn = _VisibleToRealCol(plvn->iSubItem);
  2731. // seeral ways to do this... each can defer to the
  2732. // ultimate default that is defview calling itself.
  2733. HRESULT hr = S_FALSE;
  2734. if (_psd)
  2735. hr = _psd->ColumnClick(iRealColumn);
  2736. if (hr != S_OK)
  2737. hr = CallCB(SFVM_COLUMNCLICK, iRealColumn, 0);
  2738. if (hr != S_OK)
  2739. hr = Rearrange(iRealColumn);
  2740. // Allows iLastColumnClick to stay valid during the above calls
  2741. if (SUCCEEDED(hr))
  2742. _vs._iLastColumnClick = iRealColumn;
  2743. else
  2744. {
  2745. // We failed somewhere so pop the sort state.
  2746. _vs._iDirection = iLastSortDirection;
  2747. _vs._iLastColumnClick = (int)_vs._lParamSort;
  2748. _vs._lParamSort = lParamSort ;
  2749. _SetSortFeedback();
  2750. _vs._iLastColumnClick = iLastColumnClick;
  2751. }
  2752. }
  2753. break;
  2754. case LVN_KEYDOWN:
  2755. HandleKeyDown(((LV_KEYDOWN *)plvn));
  2756. break;
  2757. #define plvdi ((LV_DISPINFO *)plvn)
  2758. case LVN_BEGINLABELEDIT:
  2759. {
  2760. LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem);
  2761. if (!pidl || !_Attributes(pidl, SFGAO_CANRENAME))
  2762. {
  2763. MessageBeep(0);
  2764. return TRUE; // Don't allow label edit
  2765. }
  2766. _fInLabelEdit = TRUE;
  2767. HWND hwndEdit = ListView_GetEditControl(_hwndListview);
  2768. if (hwndEdit)
  2769. {
  2770. int cchMax = 0;
  2771. CallCB(SFVM_GETCCHMAX, (WPARAM)pidl, (LPARAM)&cchMax);
  2772. if (cchMax)
  2773. {
  2774. ASSERT(cchMax < 1024);
  2775. SendMessage(hwndEdit, EM_LIMITTEXT, cchMax, 0);
  2776. }
  2777. TCHAR szName[MAX_PATH];
  2778. if (SUCCEEDED(DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FOREDITING, szName, ARRAYSIZE(szName))))
  2779. {
  2780. SetWindowText(hwndEdit, szName);
  2781. }
  2782. SHLimitInputEdit(hwndEdit, _pshf);
  2783. }
  2784. }
  2785. break;
  2786. case LVN_ENDLABELEDIT:
  2787. _fInLabelEdit = FALSE;
  2788. if (plvdi->item.pszText)
  2789. {
  2790. LPCITEMIDLIST pidl = _GetPIDLParam(plvdi->item.lParam, plvdi->item.iItem);
  2791. if (pidl)
  2792. {
  2793. // this set site is questionable as folder should not have any state
  2794. // associated with the view. but this is needed for FTP so it can
  2795. // do an EnableModless for it's UI
  2796. IUnknown_SetSite(_pshf, SAFECAST(this, IOleCommandTarget *));
  2797. // Clone the pidl since isf->SetNameOf can result in a synchronous update item that
  2798. // will free the listview owned pidl.
  2799. LPITEMIDLIST pidlOldName = ILClone(pidl);
  2800. if (pidlOldName)
  2801. {
  2802. LPITEMIDLIST pidlNewName = NULL; // paranoid about bad SetNameOf() impls
  2803. if (SUCCEEDED(_pshf->SetNameOf(_hwndMain, pidlOldName, plvdi->item.pszText, SHGDN_INFOLDER, &pidlNewName)))
  2804. {
  2805. ASSERT(NULL != pidlNewName); // folders need to implement this
  2806. if (pidlNewName)
  2807. {
  2808. _DoBulkRename(pidlNewName);
  2809. ILFree(pidlNewName);
  2810. }
  2811. SHChangeNotifyHandleEvents();
  2812. _OnStateChange(CDBOSC_RENAME);
  2813. }
  2814. else
  2815. {
  2816. SendMessage(_hwndListview, LVM_EDITLABEL, plvdi->item.iItem, (LPARAM)plvdi->item.pszText);
  2817. }
  2818. ILFree(pidlOldName);
  2819. }
  2820. IUnknown_SetSite(_pshf, NULL);
  2821. }
  2822. }
  2823. else
  2824. {
  2825. // The user canceled. so return TRUE to let things like the mouse
  2826. // click be processed.
  2827. return TRUE;
  2828. }
  2829. break;
  2830. case LVN_GETDISPINFO:
  2831. return _GetDisplayInfo(plvdi);
  2832. case LVN_ODFINDITEM:
  2833. // We are owner data so we need to find the item for the user...
  2834. {
  2835. int iItem = -1;
  2836. if (SUCCEEDED(CallCB(SFVM_ODFINDITEM, (WPARAM)&iItem, (LPARAM)plvn)))
  2837. return iItem;
  2838. return -1; // Not Found
  2839. }
  2840. case LVN_ODCACHEHINT:
  2841. // Just a hint we don't care about return values
  2842. CallCB(SFVM_ODCACHEHINT, 0, (LPARAM)plvn);
  2843. break;
  2844. case LVN_GETEMPTYTEXT:
  2845. if (HasCB())
  2846. {
  2847. if ((plvdi->item.mask & LVIF_TEXT) &&
  2848. SUCCEEDED(CallCB(SFVM_GETEMPTYTEXT, (WPARAM)(plvdi->item.cchTextMax), (LPARAM)(plvdi->item.pszText))))
  2849. return TRUE;
  2850. }
  2851. break;
  2852. }
  2853. #undef lpdi
  2854. #undef plvdi
  2855. return 0;
  2856. }
  2857. // FEATURE -- implement enabling/disabling of other toolbar buttons. We can enable/disable
  2858. // based on the current selection, but the problem is that some of the buttons work
  2859. // for other guys when defview doesn't have focus. Specifically, cut/copy/paste work
  2860. // for the folders pane. If we're going to enable/disable these buttons based on the
  2861. // selection, then we'll need to have a mechanism that lets the active band (such as
  2862. // folders) also have a say about the button state. That is too much work right now.
  2863. static const UINT c_BtnCmds[] =
  2864. {
  2865. SFVIDM_EDIT_COPYTO,
  2866. SFVIDM_EDIT_MOVETO,
  2867. #ifdef ENABLEDISABLEBUTTONS
  2868. SFVIDM_EDIT_COPY,
  2869. SFVIDM_EDIT_CUT,
  2870. #endif
  2871. };
  2872. static const DWORD c_BtnAttr[] =
  2873. {
  2874. SFGAO_CANCOPY,
  2875. SFGAO_CANMOVE,
  2876. #ifdef ENABLEDISABLEBUTTONS
  2877. SFGAO_CANCOPY,
  2878. SFGAO_CANMOVE,
  2879. #endif
  2880. };
  2881. #define SFGAO_RELEVANT (SFGAO_CANCOPY | SFGAO_CANMOVE)
  2882. // Description:
  2883. // Called by toolbar infrastructure to determine whether to display a given
  2884. // toolbar button in the "enabled" or "disabled" state.
  2885. //
  2886. // Return:
  2887. // TRUE display toolbar button in enabled state
  2888. // FALSE display toolbar button in disabled state
  2889. //
  2890. BOOL CDefView::_ShouldEnableToolbarButton(UINT uiCmd, DWORD dwAttr, int iIndex)
  2891. {
  2892. COMPILETIME_ASSERT(sizeof(c_BtnCmds) == sizeof(c_BtnAttr));
  2893. BOOL bEnable;
  2894. switch (uiCmd)
  2895. {
  2896. case SFVIDM_VIEW_VIEWMENU:
  2897. bEnable = !_fBarrierDisplayed;
  2898. break;
  2899. default:
  2900. {
  2901. DWORD dwBtnAttr;
  2902. if (iIndex != -1)
  2903. {
  2904. // Caller was nice and figured out dest index for us
  2905. dwBtnAttr = c_BtnAttr[iIndex];
  2906. }
  2907. else
  2908. {
  2909. // Look for the command ourselves
  2910. dwBtnAttr = SHSearchMapInt((int*)c_BtnCmds, (int*)c_BtnAttr, ARRAYSIZE(c_BtnCmds), uiCmd);
  2911. if (dwBtnAttr == -1)
  2912. {
  2913. // We don't care about this button, just enable it.
  2914. return TRUE;
  2915. }
  2916. }
  2917. // Disable any button we care about while listview is inactive.
  2918. bEnable = BOOLIFY(dwAttr & dwBtnAttr) && _fHasListViewFocus;
  2919. break;
  2920. }
  2921. }
  2922. return bEnable;
  2923. }
  2924. // As a perf enhancement, we cache the attributes of the currently selected
  2925. // files/folders in a FS view only. This is to avoid n^2 traversals of the
  2926. // selected items as we select/unselect them. These cached attributes
  2927. // should not be used for anything other than determining toolbar button
  2928. // states and should be revisited if we add toolbar buttons that care about
  2929. // much more than the attributes used by Move to & Copy to.
  2930. BOOL CDefView::_GetCachedToolbarSelectionAttrs(ULONG *pdwAttr)
  2931. {
  2932. BOOL fResult = FALSE;
  2933. CLSID clsid;
  2934. HRESULT hr = IUnknown_GetClassID(_pshf, &clsid);
  2935. if (SUCCEEDED(hr) && IsEqualGUID(CLSID_ShellFSFolder, clsid))
  2936. {
  2937. UINT iCount;
  2938. if (SUCCEEDED(GetSelectedCount(&iCount)) &&
  2939. (iCount > 0) && (_uCachedSelCount > 0))
  2940. {
  2941. *pdwAttr = _uCachedSelAttrs;
  2942. fResult = TRUE;
  2943. }
  2944. }
  2945. return fResult;
  2946. }
  2947. void CDefView::_SetCachedToolbarSelectionAttrs(ULONG dwAttrs)
  2948. {
  2949. if (SUCCEEDED(GetSelectedCount(&_uCachedSelCount)))
  2950. _uCachedSelAttrs = dwAttrs;
  2951. else
  2952. _uCachedSelCount = 0;
  2953. }
  2954. void CDefView::_EnableDisableTBButtons()
  2955. {
  2956. if (!IsEqualGUID(_clsid, GUID_NULL))
  2957. {
  2958. IExplorerToolbar *piet;
  2959. if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet))))
  2960. {
  2961. ULONG dwAttr;
  2962. if (!_GetCachedToolbarSelectionAttrs(&dwAttr))
  2963. dwAttr = _AttributesFromSel(SFGAO_RELEVANT);
  2964. for (int i = 0; i < ARRAYSIZE(c_BtnCmds); i++)
  2965. _EnableToolbarButton(piet, c_BtnCmds[i], _ShouldEnableToolbarButton(c_BtnCmds[i], dwAttr, i));
  2966. _SetCachedToolbarSelectionAttrs(dwAttr);
  2967. piet->Release();
  2968. }
  2969. }
  2970. }
  2971. // Description:
  2972. // Enables or disables a specified button on the toolbar.
  2973. //
  2974. void CDefView::EnableToolbarButton(UINT uiCmd, BOOL bEnable)
  2975. {
  2976. if (!IsEqualGUID(_clsid, GUID_NULL))
  2977. {
  2978. IExplorerToolbar *piet;
  2979. if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet))))
  2980. {
  2981. _EnableToolbarButton(piet, uiCmd, bEnable);
  2982. piet->Release();
  2983. }
  2984. }
  2985. }
  2986. // Description:
  2987. // Enables or disables a specified button on the toolbar.
  2988. //
  2989. // Note:
  2990. // This is an _internal_ method only.
  2991. // External calls should use EnableToolbarButton().
  2992. // Caller is responsible for ensuring this object uses IExplorerToolbar mechanism.
  2993. //
  2994. void CDefView::_EnableToolbarButton(IExplorerToolbar *piet, UINT uiCmd, BOOL bEnable)
  2995. {
  2996. ASSERT(!IsEqualGUID(_clsid, GUID_NULL)); // Required or piet cannot be valid.
  2997. ASSERT(piet); // Required or we're not using IExplorerToolbar mechanism.
  2998. UINT uiState;
  2999. if (SUCCEEDED(piet->GetState(&_clsid, uiCmd, &uiState)))
  3000. {
  3001. if (bEnable)
  3002. uiState |= TBSTATE_ENABLED;
  3003. else
  3004. uiState &= ~TBSTATE_ENABLED;
  3005. piet->SetState(&_clsid, uiCmd, uiState);
  3006. }
  3007. }
  3008. void CDefView::_OnContentsChanged()
  3009. {
  3010. // use a timer to delay sending a gazillion content change messages to automation.
  3011. // todo: see what duiview has to do with this stuff.
  3012. // only fire event if someone is listening
  3013. if (_pauto || _pDUIView)
  3014. {
  3015. // delay for 100ms
  3016. SetTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED, 100, NULL);
  3017. }
  3018. if (!_pDUIView)
  3019. {
  3020. _fRcvdContentsChangeBeforeDuiViewCreated = TRUE;
  3021. }
  3022. }
  3023. void CDefView::_OnDelayedContentsChanged()
  3024. {
  3025. KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED);
  3026. // update dui, would be better if there were different handlers in CDUIView
  3027. // but go through selection changed for now.
  3028. ATOMICRELEASE(_pSelectionShellItemArray);
  3029. _pSelectionShellItemArray = _CreateSelectionShellItemArray();
  3030. if (_pDUIView)
  3031. {
  3032. if (_fBarrierDisplayed != _QueryBarricadeState())
  3033. {
  3034. //
  3035. // Yet another DUI special-case.
  3036. // If the barrier state has changed, we need to
  3037. // tell DUIView about it so that the DUI right-pane
  3038. // content is reconstructed. This is required to make
  3039. // Control Panel update it's right-pane content when
  3040. // webview is turned on/off.
  3041. //
  3042. _fBarrierDisplayed = !_fBarrierDisplayed;
  3043. _pDUIView->EnableBarrier (_fBarrierDisplayed);
  3044. }
  3045. _pDUIView->OnContentsChange(_pSelectionShellItemArray);
  3046. }
  3047. _FireEvent(DISPID_CONTENTSCHANGED);
  3048. }
  3049. // WARNING: don't add any code here that is expensive in anyway!
  3050. // we get many many of these notifies and if we slow this routine down
  3051. // we mess select all and large selection perf.
  3052. //
  3053. // you can add expensive code to the WM_DSV_SENDSELECTIONCHANGED handler _OnSelectionChanged,
  3054. // that happens after all of the sel change notifies go through.
  3055. //
  3056. // or you can add really expensive code to the double-click-timeout delayed _OnDelayedSelectionChange.
  3057. //
  3058. void CDefView::OnLVSelectionChange(NM_LISTVIEW *plvn)
  3059. {
  3060. _OnLVSelectionChange(plvn->iItem, plvn->uOldState, plvn->uNewState, plvn->lParam);
  3061. }
  3062. void CDefView::_OnLVSelectionChange(int iItem, UINT uOldState, UINT uNewState, LPARAM lParam)
  3063. {
  3064. // Do selection changed stuff on a selection change only
  3065. if ((uOldState ^ uNewState) & LVIS_SELECTED)
  3066. {
  3067. // Tell the defview client that the selection may have changed
  3068. SFVM_SELCHANGE_DATA dvsci;
  3069. dvsci.uNewState = uNewState;
  3070. dvsci.uOldState = uOldState;
  3071. dvsci.lParamItem = lParam;
  3072. CallCB(SFVM_SELCHANGE, MAKEWPARAM(SFVIDM_CLIENT_FIRST, iItem), (LPARAM)&dvsci);
  3073. }
  3074. // Notify the dispach that the focus changed..
  3075. _PostSelectionChangedMessage(uOldState ^ uNewState);
  3076. }
  3077. void CDefView::_PostSelectionChangedMessage(UINT uSelectionStateChanged)
  3078. {
  3079. if (!_fSelectionChangePending)
  3080. {
  3081. _uSelectionStateChanged = uSelectionStateChanged;
  3082. // RACE CONDITION FIX (edwardp & buzzr)
  3083. // It is imperative to set _fSelectionChangePending _before_ posting
  3084. // WM_DSV_SENDSELECTIONCHANGED. Otherwise, a race condition ensues
  3085. // whereby we could handle the message via _OnSelectionChanged()
  3086. // whose first line sets _fSelectionChangePending = FALSE before we
  3087. // have set it to TRUE here. This means _fSelectionChangePending
  3088. // will never again be set to FALSE (since the this thread will be
  3089. // rescheduled, set it to TRUE, and the action of clearing it will
  3090. // already be past). This was happening with 100% reproducability
  3091. // with our background CGetCommandStateTask for WIA devices. The
  3092. // symptom most noticeable was that the DUI pane (task lists and
  3093. // details) no longer updated with each selection change.
  3094. _fSelectionChangePending = TRUE;
  3095. PostMessage(_hwndView, WM_DSV_SENDSELECTIONCHANGED, 0, 0);
  3096. }
  3097. else
  3098. {
  3099. _uSelectionStateChanged |= uSelectionStateChanged;
  3100. }
  3101. }
  3102. void CDefView::_OnSelectionChanged() // handles WM_DSV_SENDSELECTIONCHANGED
  3103. {
  3104. _fSelectionChangePending = FALSE; // release this first so code we call doesn't think we're "pending" any more
  3105. if (_uSelectionStateChanged & LVIS_SELECTED)
  3106. {
  3107. // Get and cache the data object for the current selection
  3108. ATOMICRELEASE(_pSelectionShellItemArray);
  3109. _pSelectionShellItemArray = _CreateSelectionShellItemArray();
  3110. // Update DUIView
  3111. if (_pDUIView)
  3112. _pDUIView->OnSelectionChange(_pSelectionShellItemArray);
  3113. _UpdateStatusBar(FALSE);
  3114. _EnableDisableTBButtons();
  3115. }
  3116. // Only fire selection change events if someone is listening
  3117. // and if the selection changed event was not caused by going into Edit mode (why?)
  3118. if (_pauto && !_fInLabelEdit)
  3119. {
  3120. // Send out the selection changed notification to the automation after a delay.
  3121. if (!_bAutoSelChangeTimerSet)
  3122. {
  3123. _bAutoSelChangeTimerSet = TRUE;
  3124. _uAutoSelChangeState = _uSelectionStateChanged;
  3125. }
  3126. else
  3127. {
  3128. _uAutoSelChangeState |= _uSelectionStateChanged;
  3129. }
  3130. // But not too long, since parts of our UI update when they receive this event.
  3131. // (Update the timer every time to keep delaying it during rapid selection change events)
  3132. SetTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE, GetDoubleClickTime()/2, NULL);
  3133. }
  3134. }
  3135. void CDefView::_OnDelayedSelectionChange() // handles DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE
  3136. {
  3137. if (_bAutoSelChangeTimerSet)
  3138. {
  3139. KillTimer(_hwndView, DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE);
  3140. if (_uAutoSelChangeState & LVIS_SELECTED)
  3141. _FireEvent(DISPID_SELECTIONCHANGED);
  3142. if (_uAutoSelChangeState & LVIS_FOCUSED)
  3143. _FireEvent(DISPID_FOCUSCHANGED);
  3144. _bAutoSelChangeTimerSet = FALSE;
  3145. }
  3146. }
  3147. void CDefView::_PostNoItemStateChangedMessage()
  3148. {
  3149. if (_pauto && !_fNoItemStateChangePending)
  3150. {
  3151. PostMessage(_hwndView, WM_DSV_SENDNOITEMSTATECHANGED, 0, 0);
  3152. _fNoItemStateChangePending = TRUE;
  3153. }
  3154. }
  3155. void CDefView::_OnNoItemStateChanged()
  3156. {
  3157. _FireEvent(DISPID_NOITEMSTATE_CHANGED);
  3158. _fNoItemStateChangePending = FALSE;
  3159. }
  3160. void CDefView::_PostEnumDoneMessage()
  3161. {
  3162. PostMessage(_hwndView, WM_DSV_FILELISTENUMDONE, 0, 0);
  3163. }
  3164. void CDefView::_PostFillDoneMessage()
  3165. {
  3166. _ShowSearchUI(TRUE);
  3167. PostMessage(_hwndView, WM_DSV_FILELISTFILLDONE, 0, 0);
  3168. }
  3169. void CDefView::_OnEnumDoneMessage()
  3170. {
  3171. if (_pauto)
  3172. _FireEvent(DISPID_FILELISTENUMDONE);
  3173. if (_pfnEnumReadyCallback)
  3174. _pfnEnumReadyCallback(_pvEnumCallbackData);
  3175. }
  3176. #define IN_VIEW_BMP 0x8000
  3177. #define EXT_VIEW_GOES_HERE 0x4000
  3178. #define PRIVATE_TB_FLAGS (IN_VIEW_BMP | EXT_VIEW_GOES_HERE)
  3179. #define IN_STD_BMP 0x0000
  3180. LRESULT CDefView::_OnNotify(NMHDR *pnm)
  3181. {
  3182. switch (pnm->idFrom)
  3183. {
  3184. case ID_LISTVIEW:
  3185. return _OnLVNotify((NM_LISTVIEW *)pnm);
  3186. case FCIDM_TOOLBAR:
  3187. return _TBNotify(pnm);
  3188. default:
  3189. switch (pnm->code)
  3190. {
  3191. case TTN_NEEDTEXT:
  3192. #define ptt ((LPTOOLTIPTEXT)pnm)
  3193. _GetToolTipText(ptt->hdr.idFrom, ptt->szText, ARRAYSIZE(ptt->szText));
  3194. #undef ptt
  3195. break;
  3196. case NM_RCLICK:
  3197. if (GetParent(pnm->hwndFrom) == _hwndListview)
  3198. {
  3199. POINT p;
  3200. GetMsgPos(&p);
  3201. _DoColumnsMenu(p.x, p.y);
  3202. return 1; // To keep normal context menu from appearing
  3203. }
  3204. }
  3205. }
  3206. return 0;
  3207. }
  3208. // ask the folder for the default column state
  3209. DWORD CDefView::_DefaultColumnState(UINT iCol)
  3210. {
  3211. DWORD dwState;
  3212. if (_pshf2)
  3213. {
  3214. if (FAILED(_pshf2->GetDefaultColumnState(iCol, &dwState)))
  3215. {
  3216. dwState = SHCOLSTATE_ONBYDEFAULT; // deal with E_NOTIMPL GetDefaultColumState implementations
  3217. }
  3218. }
  3219. else
  3220. {
  3221. dwState = SHCOLSTATE_ONBYDEFAULT;
  3222. }
  3223. return dwState;
  3224. }
  3225. // SHCOLSTATE_ONBYDEFAULT
  3226. //
  3227. // columns that are turn on for this view (are displayed in the UI)
  3228. BOOL CDefView::_IsDetailsColumn(UINT iCol)
  3229. {
  3230. return (_vs.GetColumnState(iCol) & SHCOLSTATE_ONBYDEFAULT) ? TRUE : FALSE;
  3231. }
  3232. BOOL CDefView::_IsColumnInListView(UINT iCol)
  3233. {
  3234. return ((_vs.GetColumnState(iCol) & SHCOLSTATE_ONBYDEFAULT) ||
  3235. (_vs.GetTransientColumnState(iCol) & SHTRANSCOLSTATE_TILEVIEWCOLUMN)) ? TRUE : FALSE;
  3236. }
  3237. BOOL CDefView::_IsTileViewColumn(UINT iCol)
  3238. {
  3239. return (_vs.GetTransientColumnState(iCol) & SHTRANSCOLSTATE_TILEVIEWCOLUMN) ? TRUE : FALSE;
  3240. }
  3241. // SHCOLSTATE_HIDDEN
  3242. //
  3243. // columns that should not be displayed in the UI, but are exposed from
  3244. // the psf2->GetDetailsEx(). this is a way to have programtic access to properties
  3245. // that don't show up in details view
  3246. BOOL CDefView::_IsColumnHidden(UINT uCol)
  3247. {
  3248. return (_vs.GetColumnState(uCol) & SHCOLSTATE_HIDDEN) ? TRUE : FALSE;
  3249. }
  3250. #define COL_CM_MAXITEMS 25 // how many item show up in context menu before more ... is inserted
  3251. HRESULT CDefView::AddColumnsToMenu(HMENU hm, DWORD dwBase)
  3252. {
  3253. BOOL bNeedMoreMenu = FALSE;
  3254. HRESULT hr = E_FAIL;
  3255. if (_vs._hdsaColumns)
  3256. {
  3257. AppendMenu(hm, MF_STRING | MF_CHECKED | MF_GRAYED, dwBase, _vs.GetColumnName(0));
  3258. for (UINT i = 1; i < min(COL_CM_MAXITEMS, _vs.GetColumnCount()); i++)
  3259. {
  3260. DWORD dwFlags = _vs.GetColumnState(i);
  3261. if (!(dwFlags & SHCOLSTATE_HIDDEN))
  3262. {
  3263. if (dwFlags & SHCOLSTATE_SECONDARYUI)
  3264. bNeedMoreMenu = TRUE;
  3265. else
  3266. AppendMenu(hm, MF_STRING | (dwFlags & SHCOLSTATE_ONBYDEFAULT) ? MF_CHECKED : 0,
  3267. dwBase + i, _vs.GetColumnName(i));
  3268. }
  3269. }
  3270. if (bNeedMoreMenu || (_vs.GetColumnCount() > COL_CM_MAXITEMS))
  3271. {
  3272. TCHAR szMore[MAX_COLUMN_NAME_LEN];
  3273. LoadString(HINST_THISDLL, IDS_COL_CM_MORE, szMore, ARRAYSIZE(szMore));
  3274. AppendMenu(hm, MF_SEPARATOR, 0, NULL);
  3275. AppendMenu(hm, MF_STRING, SFVIDM_VIEW_COLSETTINGS, szMore);
  3276. }
  3277. hr = S_OK;
  3278. }
  3279. return hr;
  3280. }
  3281. UINT CDefView::_RealToVisibleCol(UINT iReal)
  3282. {
  3283. ASSERT(_bLoadedColumns && _vs.GetColumnCount());
  3284. int iVisible = -1; // start here to get zero based result
  3285. int cMax = min(_vs.GetColumnCount() - 1, iReal);
  3286. for (int i = 0; i <= cMax; i++)
  3287. {
  3288. if (_IsColumnInListView(i))
  3289. {
  3290. iVisible++;
  3291. }
  3292. }
  3293. ASSERT(-1 != iVisible);
  3294. return iVisible;
  3295. }
  3296. // map listview (zero based) column indexes
  3297. // indexs (zero based)
  3298. UINT CDefView::_VisibleToRealCol(UINT iVisible)
  3299. {
  3300. ASSERT(_bLoadedColumns && _vs.GetColumnCount());
  3301. for (UINT i = 0, cVisibleSeen = 0; i < _vs.GetColumnCount(); i++)
  3302. {
  3303. if (_IsColumnInListView(i))
  3304. {
  3305. if (cVisibleSeen == iVisible)
  3306. {
  3307. return i;
  3308. }
  3309. cVisibleSeen++;
  3310. }
  3311. }
  3312. ASSERT(0); // should never get a vis col not in the real
  3313. return 0;
  3314. }
  3315. void CDefView::_AddTileColumn(UINT uCol)
  3316. {
  3317. if (_IsColumnInListView(uCol))
  3318. {
  3319. // All we need to do is make sure it's marked as a tile column
  3320. _vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, SHTRANSCOLSTATE_TILEVIEWCOLUMN);
  3321. return;
  3322. }
  3323. _vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, SHTRANSCOLSTATE_TILEVIEWCOLUMN);
  3324. // Now that we set the transient state, we can get the new visible column index
  3325. // for this guy, and add it to the listview.
  3326. UINT uColVis = _RealToVisibleCol(uCol);
  3327. _AddColumnToListView(uCol, uColVis);
  3328. // We now need to reset the tile info for each item. We can make an optimization:
  3329. // if this column was added at the end (i.e. biggest visible column), it won't affect
  3330. // any of the current tiles, so we don't need to do this. Passing -1 gives us the
  3331. // largest visible index.
  3332. if (_RealToVisibleCol(-1) != uColVis)
  3333. {
  3334. _ResetTileInfo(uColVis, TRUE);
  3335. }
  3336. }
  3337. // Remove all columns that were added because of tileview (unless they were also
  3338. // added for other reasons).
  3339. // Note: This should only be called when leaving tileview, since we do not reset the
  3340. // items' tileinfo.
  3341. void CDefView::_RemoveTileColumns()
  3342. {
  3343. for (UINT uCol = 0; uCol < _vs.GetColumnCount(); uCol++)
  3344. {
  3345. if (_IsTileViewColumn(uCol))
  3346. {
  3347. // First nuke the tile bit.
  3348. UINT uColVis = _RealToVisibleCol(uCol);
  3349. _vs.SetTransientColumnState(uCol, SHTRANSCOLSTATE_TILEVIEWCOLUMN, 0);
  3350. // Then go ahead and remove it from listview if it wasn't a details column
  3351. if (!_IsDetailsColumn(uCol))
  3352. {
  3353. ListView_DeleteColumn(_hwndListview, uColVis);
  3354. }
  3355. }
  3356. }
  3357. }
  3358. // This method resets the tileinfo for each item in the listview, based on which
  3359. // visible column we just added or removed.
  3360. // uColVis = the visible column that was added or removed.
  3361. // Note: This must be called prior to there being any tileinfo in the listview containing
  3362. // a reference to this new column.
  3363. void CDefView::_ResetTileInfo(UINT uColVis, BOOL bAdded)
  3364. {
  3365. if (!_IsOwnerData())
  3366. {
  3367. UINT rguColumns[TILEVIEWLINES];
  3368. for (int i = 0; i < ListView_GetItemCount(_hwndListview); i++)
  3369. {
  3370. UINT uColBoundary = uColVis;
  3371. LVITEM lvi;
  3372. lvi.mask = LVIF_COLUMNS | LVIF_NORECOMPUTE;
  3373. lvi.iSubItem = 0;
  3374. lvi.iItem = i;
  3375. lvi.cColumns = ARRAYSIZE(rguColumns);
  3376. lvi.puColumns = rguColumns;
  3377. if (!ListView_GetItem(_hwndListview, &lvi))
  3378. continue;
  3379. if ((lvi.cColumns == 0) || (lvi.cColumns == I_COLUMNSCALLBACK))
  3380. {
  3381. continue;
  3382. }
  3383. ASSERT(lvi.cColumns <= ARRAYSIZE(rguColumns)); // If for some reason listview has more, there's a problem
  3384. // guard just in case
  3385. if (lvi.cColumns > ARRAYSIZE(rguColumns))
  3386. {
  3387. lvi.cColumns = ARRAYSIZE(rguColumns);
  3388. }
  3389. UINT *puColumn = lvi.puColumns;
  3390. BOOL bChange = FALSE;
  3391. // Adjust the column numbers as needed: up for added, down for removed.
  3392. int iIncDec = bAdded ? 1 : -1;
  3393. if (!bAdded)
  3394. {
  3395. // What is this doing? If we've added a column X, we need to adjust columns
  3396. // from X on up. If we've removed a column X, we need to adjust columns from
  3397. // X+1 on up. So basically, instead of doing (*puColumn > uColBoundary), we're
  3398. // doing (*puColumn >= (uColBoundary+1)). So we can do the same ">=" expression
  3399. // whether or not bAdded, avoiding an if check in the loop.
  3400. uColBoundary++;
  3401. }
  3402. for (UINT uCol = 0; uCol < lvi.cColumns; uCol++, puColumn++)
  3403. {
  3404. if (*puColumn >= uColBoundary)
  3405. {
  3406. (*puColumn) = (UINT)(iIncDec + (int)(*puColumn)); // Inc or dec.
  3407. bChange = TRUE;
  3408. }
  3409. }
  3410. if (bChange) // If there were any changes, set the ti back.
  3411. {
  3412. LVTILEINFO ti;
  3413. ti.cbSize = sizeof(ti);
  3414. ti.iItem = lvi.iItem;
  3415. ti.cColumns = lvi.cColumns;
  3416. ti.puColumns = lvi.puColumns;
  3417. ListView_SetTileInfo(_hwndListview, &ti);
  3418. }
  3419. }
  3420. }
  3421. }
  3422. // Called when leaving tileview, this "cleans the slate" so that we reload the
  3423. // columns properly when re-entering tileview at a later time.
  3424. void CDefView::_RemoveTileInfo()
  3425. {
  3426. if (!_IsOwnerData())
  3427. {
  3428. for (int i = 0; i < ListView_GetItemCount(_hwndListview); i++)
  3429. {
  3430. LVTILEINFO ti = {0};
  3431. ti.cbSize = sizeof(ti);
  3432. ti.iItem = i;
  3433. ti.cColumns = I_COLUMNSCALLBACK;
  3434. ListView_SetTileInfo(_hwndListview, &ti);
  3435. }
  3436. }
  3437. }
  3438. // uCol is a real column number, not visible column number
  3439. // This method toggles the SHCOLSTATE_ONBYDEFAULT bit of the column,
  3440. // and adds or removes the column as necessary.
  3441. BOOL CDefView::_HandleColumnToggle(UINT uCol, BOOL bRefresh)
  3442. {
  3443. BOOL fWasOn = _IsColumnInListView(uCol); // if its off now, we are adding it
  3444. BOOL fWasDetailsColumn = _IsDetailsColumn(uCol);
  3445. UINT uColVisOld = _RealToVisibleCol(uCol);
  3446. _vs.SetColumnState(uCol, SHCOLSTATE_ONBYDEFAULT, fWasDetailsColumn ? 0 : SHCOLSTATE_ONBYDEFAULT);
  3447. BOOL fIsOn = _IsColumnInListView(uCol); // This could == fWasOn if it's a tileview column
  3448. UINT uColVis = _RealToVisibleCol(uCol);
  3449. if (fIsOn != fWasOn)
  3450. {
  3451. if (!fWasOn)
  3452. {
  3453. _AddColumnToListView(uCol, uColVis);
  3454. if (_fs.ViewMode == FVM_TILE)
  3455. {
  3456. _ResetTileInfo(uColVis, TRUE);
  3457. }
  3458. }
  3459. else
  3460. {
  3461. _vs.RemoveColumn(uColVisOld);
  3462. ListView_DeleteColumn(_hwndListview, uColVisOld);
  3463. if (_fs.ViewMode == FVM_TILE)
  3464. {
  3465. _ResetTileInfo(uColVisOld, FALSE);
  3466. }
  3467. if (_vs._lParamSort == (int) uCol)
  3468. {
  3469. UINT iNewVis = _VisibleToRealCol(0);
  3470. Rearrange(iNewVis);
  3471. }
  3472. if (ListView_GetSelectedColumn(_hwndListview) == (UINT)uCol)
  3473. ListView_SetSelectedColumn(_hwndListview, -1);
  3474. }
  3475. }
  3476. if (bRefresh)
  3477. {
  3478. ListView_RedrawItems(_hwndListview, 0, 0x7fff);
  3479. InvalidateRect(_hwndListview, NULL, TRUE);
  3480. UpdateWindow(_hwndListview);
  3481. }
  3482. return TRUE;
  3483. }
  3484. // uCol = Real column number. uColVis = add it as this visible column.
  3485. void CDefView::_AddColumnToListView(UINT uCol, UINT uColVis)
  3486. {
  3487. LV_COLUMN col = {0};
  3488. // Adding a column
  3489. col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  3490. col.fmt = _vs.GetColumnFormat(uCol);
  3491. col.cx = _vs.GetColumnCharCount(uCol) * _cxChar; // Use default width
  3492. col.pszText = _vs.GetColumnName(uCol);
  3493. col.cchTextMax = MAX_COLUMN_NAME_LEN;
  3494. col.iSubItem = uCol; // not vis
  3495. // This is all odd... Find Files uses this, but i think it should be LVCFMT_COL_IMAGE
  3496. if (col.fmt & LVCFMT_COL_HAS_IMAGES)
  3497. {
  3498. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SUBITEMIMAGES, LVS_EX_SUBITEMIMAGES);
  3499. col.fmt &= ~LVCFMT_COL_HAS_IMAGES;
  3500. }
  3501. if (-1 != ListView_InsertColumn(_hwndListview, uColVis, &col))
  3502. {
  3503. // now add it to our DSA
  3504. _vs.AppendColumn(uColVis, (USHORT) col.cx, uColVis);
  3505. if (!_fGroupView && (_vs._lParamSort == (int)uCol))
  3506. {
  3507. ListView_SetSelectedColumn(_hwndListview, uColVis);
  3508. }
  3509. }
  3510. }
  3511. void SetHeaderSort(HWND hwndHead, int iCol, UINT sortFlags)
  3512. {
  3513. HDITEM hdi = {HDI_FORMAT};
  3514. Header_GetItem(hwndHead, iCol, &hdi);
  3515. hdi.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
  3516. hdi.fmt |= sortFlags;
  3517. Header_SetItem(hwndHead, iCol, &hdi);
  3518. }
  3519. void CDefView::_SetSortFeedback()
  3520. {
  3521. HWND hwndHead = ListView_GetHeader(_hwndListview);
  3522. // the _IsOwnerData() is bad. this keeps search from getting sort UI feedback.
  3523. // to fix this implement a mode where the sort has not been determined and thus we don't
  3524. // display any sort feedback. regular folders could use this too as after items have
  3525. // been added the view is not really sorted
  3526. if (!hwndHead || _IsOwnerData())
  3527. return;
  3528. BOOL fRemoveBitmapFromLastHeader = TRUE;
  3529. int iColLast = _RealToVisibleCol(_vs._iLastColumnClick);
  3530. int iCol = _RealToVisibleCol((UINT)_vs._lParamSort);
  3531. if (_fGroupView)
  3532. {
  3533. SetHeaderSort(hwndHead, iCol, 0);
  3534. }
  3535. else
  3536. {
  3537. ListView_SetSelectedColumn(_hwndListview, iCol);
  3538. SetHeaderSort(hwndHead, iCol, _vs._iDirection > 0 ? HDF_SORTUP : HDF_SORTDOWN);
  3539. // Only remove the bitmap if the last header is not the one we are currently sorting by
  3540. if (iColLast == iCol)
  3541. fRemoveBitmapFromLastHeader = FALSE;
  3542. }
  3543. if (fRemoveBitmapFromLastHeader && iColLast != -1)
  3544. {
  3545. SetHeaderSort(hwndHead, iColLast, 0);
  3546. }
  3547. }
  3548. // use the folder to compare two items, falling back if the lParam is not understood by
  3549. // that folder.
  3550. // 99/05/18 #341468 vtan: If the first comparison fails it may be because
  3551. // lParamSort is not understood by IShellFolder::CompareIDs (perhaps it's
  3552. // an extended column that might not be installed any more)
  3553. // In this case get the default comparison method
  3554. // and use that. If that fails use 0 which should hopefully not fail. If
  3555. // the 0 case fails we are toast with an assert.
  3556. HRESULT CDefView::_CompareIDsFallback(LPARAM lParam, LPCITEMIDLIST p1, LPCITEMIDLIST p2)
  3557. {
  3558. HRESULT hr = _pshf->CompareIDs(lParam, p1, p2);
  3559. if (FAILED(hr))
  3560. {
  3561. LPARAM lParamSort;
  3562. _vs.GetDefaults(this, &lParamSort, NULL, NULL);
  3563. hr = _pshf->CompareIDs(lParamSort | (SHCIDS_ALLFIELDS & lParam), p1, p2);
  3564. if (FAILED(hr))
  3565. {
  3566. // even that did not work, fall back to zero based compare (pluse the all fields flag)
  3567. hr = _pshf->CompareIDs((SHCIDS_ALLFIELDS & lParam), p1, p2);
  3568. }
  3569. }
  3570. return hr;
  3571. }
  3572. // compare two items, taking into account the sort direction
  3573. int CDefView::_CompareIDsDirection(LPARAM lParam, LPCITEMIDLIST p1, LPCITEMIDLIST p2)
  3574. {
  3575. ASSERT(_vs._iDirection != 0);
  3576. HRESULT hr = _CompareIDsFallback(lParam, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
  3577. return ShortFromResult(hr) * _vs._iDirection;
  3578. }
  3579. // p1 and p2 are pointers to the lv_item's LPARAM, which is currently the pidl
  3580. int CALLBACK CDefView::_Compare(void *p1, void *p2, LPARAM lParam)
  3581. {
  3582. CDefView *pdv = (CDefView *)lParam;
  3583. return pdv->_CompareIDsDirection(pdv->_vs._lParamSort, (LPITEMIDLIST)p1, (LPITEMIDLIST)p2);
  3584. }
  3585. typedef struct
  3586. {
  3587. VARIANT var;
  3588. BOOL fIsFolder;
  3589. } VARIANT_AND_FOLDER;
  3590. typedef struct
  3591. {
  3592. VARIANT_AND_FOLDER *pvars;
  3593. SHCOLUMNID scid;
  3594. CDefView *pdv;
  3595. } VARIANT_SORT_INFO;
  3596. int CALLBACK _CompareVariantCallback(LPARAM dw1, LPARAM dw2, LPARAM lParam)
  3597. {
  3598. VARIANT_SORT_INFO *psi = (VARIANT_SORT_INFO *)lParam;
  3599. int iRet = 0;
  3600. // Always put the folders first
  3601. if (psi->pvars[dw1].fIsFolder)
  3602. {
  3603. if (!psi->pvars[dw2].fIsFolder)
  3604. iRet = -1;
  3605. }
  3606. else if (psi->pvars[dw2].fIsFolder)
  3607. {
  3608. iRet = 1;
  3609. }
  3610. if (0 == iRet)
  3611. {
  3612. iRet = CompareVariants(psi->pvars[dw1].var, psi->pvars[dw2].var);
  3613. }
  3614. return iRet * psi->pdv->_vs._iDirection;
  3615. }
  3616. #define LV_NOFROZENITEM -1
  3617. HRESULT CDefView::_Sort(void)
  3618. {
  3619. HRESULT hr = CallCB(SFVM_ARRANGE, 0, _vs._lParamSort);
  3620. if (FAILED(hr))
  3621. {
  3622. hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE);
  3623. int iIndexRecycleBin = LV_NOFROZENITEM;
  3624. POINT ptRecycleBin;
  3625. _SetSortFeedback();
  3626. // For desktop, we need to freeze the recycle bin position before we arrage other icons.
  3627. if (_fPositionRecycleBin)
  3628. {
  3629. iIndexRecycleBin = _FreezeRecycleBin(&ptRecycleBin);
  3630. _fPositionRecycleBin = FALSE;
  3631. }
  3632. // This is semi-bogus for defview to care whether the column is extended or not.
  3633. // We could have modified the ISF::CompareIDs() to handle extended columns, but
  3634. // then it would only have the pidls, and would have to re-extract any data, so
  3635. // its much faster if we separate out the extended columns, and take advantage
  3636. // of listview's caching abilities.
  3637. DWORD dwState = _DefaultColumnState((UINT)_vs._lParamSort);
  3638. SHCOLUMNID scid;
  3639. HRESULT hrMapColumn = E_FAIL;
  3640. if (_pshf2)
  3641. hrMapColumn = _pshf2->MapColumnToSCID((UINT)_vs._lParamSort, &scid);
  3642. // SHCOLSTATE_PREFER_VARCMP tells us that the folder's CompareIDs()
  3643. // produces the same result as comparing the variants. this is an optimization
  3644. // for folders who's CompareIDs() are slow (bit bucket)
  3645. if (_IsOwnerData() || (dwState & (SHCOLSTATE_EXTENDED | SHCOLSTATE_PREFER_VARCMP)))
  3646. {
  3647. if (_GetBackgroundTaskCount(TOID_DVBackgroundEnum) == 0)
  3648. {
  3649. int cItems = ListView_GetItemCount(_hwndListview);
  3650. if (cItems)
  3651. {
  3652. VARIANT_SORT_INFO vsi;
  3653. BOOL fOkToProceed = TRUE;
  3654. if ((UINT)_vs._lParamSort == 0)
  3655. {
  3656. vsi.scid = SCID_NAME;
  3657. }
  3658. else if (SUCCEEDED(hrMapColumn))
  3659. {
  3660. vsi.scid = scid;
  3661. }
  3662. else
  3663. {
  3664. fOkToProceed = FALSE;
  3665. hr = hrMapColumn;
  3666. }
  3667. if (fOkToProceed)
  3668. {
  3669. vsi.pvars = new VARIANT_AND_FOLDER[cItems];
  3670. if (vsi.pvars)
  3671. {
  3672. vsi.pdv = this;
  3673. for (int i = 0; i < cItems; i++)
  3674. {
  3675. LPCITEMIDLIST pidl = _GetPIDL(i);
  3676. if (pidl)
  3677. {
  3678. DWORD dwAttrib = SHGetAttributes(_pshf, pidl, SFGAO_FOLDER);
  3679. vsi.pvars[i].fIsFolder = dwAttrib & SFGAO_FOLDER;
  3680. if ((UINT)_vs._lParamSort == 0) // This is the NAME column
  3681. {
  3682. WCHAR szName[MAX_PATH];
  3683. if (SUCCEEDED(DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_NORMAL, szName, ARRAYSIZE(szName))))
  3684. {
  3685. InitVariantFromStr(&vsi.pvars[i].var, szName);
  3686. }
  3687. }
  3688. else
  3689. {
  3690. _pshf2->GetDetailsEx(pidl, &vsi.scid, &vsi.pvars[i].var);
  3691. }
  3692. }
  3693. }
  3694. hr = CallCB(SFVM_SORTLISTDATA, (LPARAM)_CompareVariantCallback, (LPARAM)&vsi);
  3695. // dont send a LVM_SORTITEMS to an ownerdraw or comctl32 will rip
  3696. if (FAILED(hr) && !_IsOwnerData() && ListView_SortItemsEx(_hwndListview, _CompareVariantCallback, (LPARAM)&vsi))
  3697. hr = S_OK;
  3698. for (int i = 0; i < cItems; i++)
  3699. {
  3700. VariantClear(&vsi.pvars[i].var);
  3701. }
  3702. delete [] vsi.pvars;
  3703. }
  3704. }
  3705. }
  3706. }
  3707. }
  3708. else
  3709. {
  3710. ASSERT(!_IsOwnerData()) // dont send a LVM_SORTITEMS to an ownerdraw or comctl32 will rip
  3711. if (ListView_SortItems(_hwndListview, _Compare, (LPARAM)this))
  3712. hr = S_OK;
  3713. }
  3714. //If we froze recycle-bin earlier, now is the time to put it in it's default position.
  3715. if (iIndexRecycleBin != LV_NOFROZENITEM)
  3716. _SetRecycleBinInDefaultPosition(&ptRecycleBin);
  3717. }
  3718. return hr;
  3719. }
  3720. // this should NOT check for whether the item is already in the listview
  3721. // if it does, we'll have some serious performance problems
  3722. int CDefView::_AddObject(LPITEMIDLIST pidl) // takes ownership of pidl.
  3723. {
  3724. int iItem = -1;
  3725. // Check the commdlg hook to see if we should include this
  3726. // object.
  3727. if ((S_OK == _IncludeObject(pidl)) &&
  3728. (S_FALSE != CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidl)))
  3729. {
  3730. LV_ITEM item = {0};
  3731. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_COLUMNS;
  3732. item.iItem = INT_MAX; // add at end
  3733. item.iImage = I_IMAGECALLBACK;
  3734. item.pszText = LPSTR_TEXTCALLBACK;
  3735. item.lParam = (LPARAM)pidl; // Takes pidl ownership.
  3736. item.cColumns = I_COLUMNSCALLBACK; // REVIEW: why not fill this in like the _UpdateObject call? That would fix the problem where GroupBy doesn't keep the "Searching UI" going...
  3737. iItem = ListView_InsertItem(_hwndListview, &item);
  3738. if (iItem < 0)
  3739. {
  3740. ILFree(pidl);
  3741. }
  3742. else if (_bBkFilling)
  3743. {
  3744. _pEnumTask->_AddToPending(pidl);
  3745. }
  3746. _OnContentsChanged();
  3747. if (iItem == 0)
  3748. {
  3749. _PostNoItemStateChangedMessage();
  3750. }
  3751. }
  3752. else
  3753. {
  3754. ILFree(pidl);
  3755. }
  3756. return iItem;
  3757. }
  3758. // Find an item in the view
  3759. int CDefView::_FindItem(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlFound, BOOL fSamePtr, BOOL fForwards)
  3760. {
  3761. RIP(ILFindLastID(pidl) == pidl);
  3762. int cItems = ListView_GetItemCount(_hwndListview);
  3763. if (_iLastFind >= cItems)
  3764. _iLastFind = 0;
  3765. int iItem = _iLastFind;
  3766. if (SUCCEEDED(CallCB(SFVM_INDEXOFITEMIDLIST, (WPARAM)&iItem, (LPARAM)pidl)))
  3767. {
  3768. if (ppidlFound)
  3769. *ppidlFound = (LPITEMIDLIST)_GetPIDL(iItem); // cast as caller knows how to free this
  3770. }
  3771. else
  3772. {
  3773. iItem = -1; // assume failure
  3774. for (int cCounter = 0, i = _iLastFind; cCounter < cItems; cCounter++)
  3775. {
  3776. LPCITEMIDLIST pidlT = _GetPIDL(i);
  3777. ASSERT(pidlT);
  3778. if (pidlT)
  3779. {
  3780. if ((pidlT == pidl) ||
  3781. (!fSamePtr && (0 == ResultFromShort(_pshf->CompareIDs(0, pidl, pidlT)))))
  3782. {
  3783. if (ppidlFound)
  3784. *ppidlFound = (LPITEMIDLIST)pidlT; // cast as callers know how to free
  3785. _iLastFind = iItem = i; // success
  3786. // TraceMsg(TF_DEFVIEW, "####FIND CACHE RESULT --- %s by %d", cCounter < iItem ? TEXT("WIN") : TEXT("LOSE"), iItem - cCounter);
  3787. break;
  3788. }
  3789. }
  3790. if (fForwards)
  3791. {
  3792. i = (i+1)%cItems;
  3793. }
  3794. else
  3795. {
  3796. i = (i > 0)?(i - 1):(cItems-1);
  3797. }
  3798. }
  3799. if (-1 == iItem)
  3800. {
  3801. _iLastFind = 0; // didn't find it, reset this for next time
  3802. }
  3803. }
  3804. return iItem;
  3805. }
  3806. int CDefView::_FindItemHint(LPCITEMIDLIST pidl, int iItem)
  3807. {
  3808. _iLastFind = iItem;
  3809. return _FindItem(pidl, NULL, FALSE, FALSE);
  3810. }
  3811. // Function to process the SFVM_REMOVEOBJECT message, by searching
  3812. // through the list for a match of the pidl. If a match is found, the
  3813. // item is removed from the list and the index number is returned, else
  3814. // -1 is returned.
  3815. int CDefView::_RemoveObject(LPCITEMIDLIST pidl, BOOL fSamePtr)
  3816. {
  3817. int i = 0;
  3818. // Docfind will pass in a null pointer to tell us that it wants
  3819. // to refresh the window by deleting all of the items from it.
  3820. if (pidl == NULL)
  3821. {
  3822. CallCB(SFVM_DELETEITEM, 0, 0); // view callback notify
  3823. ListView_DeleteAllItems(_hwndListview);
  3824. _PostNoItemStateChangedMessage();
  3825. _OnContentsChanged();
  3826. }
  3827. else
  3828. {
  3829. // Non null go look for item.
  3830. i = _FindItem(pidl, NULL, fSamePtr);
  3831. if (i >= 0)
  3832. {
  3833. RECT rc;
  3834. UINT uState = ListView_GetItemState(_hwndListview, i, LVIS_ALL);
  3835. if (uState & LVIS_FOCUSED)
  3836. ListView_GetItemRect(_hwndListview, i, &rc, LVIR_ICON);
  3837. if (_bBkFilling)
  3838. _pEnumTask->_DeleteFromPending(pidl); // removes the pointer from the pending list.
  3839. ListView_DeleteItem(_hwndListview, i);
  3840. // we deleted the focused item.. replace the focus to the nearest item.
  3841. if (uState & LVIS_FOCUSED)
  3842. {
  3843. int iFocus = i;
  3844. if (_IsPositionedView() || _fGroupView)
  3845. {
  3846. LV_FINDINFO lvfi = {0};
  3847. lvfi.flags = LVFI_NEARESTXY;
  3848. lvfi.pt.x = rc.left;
  3849. lvfi.pt.y = rc.top;
  3850. iFocus = ListView_FindItem(_hwndListview, -1, &lvfi);
  3851. }
  3852. else
  3853. {
  3854. if (ListView_GetItemCount(_hwndListview) >= iFocus)
  3855. iFocus--;
  3856. }
  3857. if (iFocus != -1)
  3858. {
  3859. ListView_SetItemState(_hwndListview, iFocus, LVIS_FOCUSED, LVIS_FOCUSED);
  3860. ListView_EnsureVisible(_hwndListview, iFocus, FALSE);
  3861. }
  3862. else
  3863. {
  3864. // RAID 372130
  3865. // Notify image preview control to update its image (to
  3866. // nothing). The image preview control uses focus change
  3867. // events to track when it should update the image it is
  3868. // displaying. When it receives a focus change event, it
  3869. // queries the listview to see which item has focus, then
  3870. // displays that item in the image preview window. When
  3871. // the last item in the listview is deleted, we need to
  3872. // fire a focus change event to the image preview control
  3873. // even though the focus has not changed to another item.
  3874. // This way, the image preview control realizes there is
  3875. // nothing with focus, and correctly displays as empty.
  3876. if (_fs.ViewMode == FVM_THUMBSTRIP)
  3877. _ThumbstripSendImagePreviewFocusChangeEvent();
  3878. }
  3879. }
  3880. // Notify automation if the listview is now empty
  3881. UINT uCount = 0;
  3882. GetObjectCount(&uCount);
  3883. if (!uCount)
  3884. {
  3885. _PostNoItemStateChangedMessage();
  3886. }
  3887. _OnContentsChanged();
  3888. }
  3889. }
  3890. return i;
  3891. }
  3892. // search the list for a match of the first pidl. If a match is found,
  3893. // the item is updated to the second pidl...
  3894. int CDefView::_UpdateObject(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidlNew)
  3895. {
  3896. LPITEMIDLIST pidlOldToFree;
  3897. int i = _FindItem(pidlOld, &pidlOldToFree, FALSE);
  3898. if (i >= 0)
  3899. {
  3900. if (_IsOwnerData())
  3901. {
  3902. if (SUCCEEDED(CallCB(SFVM_SETITEMIDLIST, i, (LPARAM)pidlNew)))
  3903. {
  3904. // Invalidate the rectangle so we update the item...
  3905. RECT rc;
  3906. ListView_GetItemRect(_hwndListview, i, &rc, LVIR_BOUNDS);
  3907. InvalidateRect(_hwndListview, &rc, FALSE);
  3908. ListView_Update(_hwndListview, i);
  3909. _OnContentsChanged();
  3910. }
  3911. else
  3912. {
  3913. i = -1; // we failed, try to cleanup and bail.
  3914. }
  3915. }
  3916. else
  3917. {
  3918. LPITEMIDLIST pidlNewClone = ILClone(pidlNew);
  3919. if (pidlNewClone)
  3920. {
  3921. LV_ITEM item = {0};
  3922. // We found the item so lets now update it in the
  3923. // the view.
  3924. item.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE;
  3925. item.iItem = i;
  3926. item.pszText = LPSTR_TEXTCALLBACK;
  3927. item.iImage = I_IMAGECALLBACK;
  3928. item.lParam = (LPARAM)pidlNewClone;
  3929. // if selected, deselect it
  3930. UINT uState = ListView_GetItemState(_hwndListview, i, LVIS_FOCUSED|LVIS_SELECTED);
  3931. if (uState & (LVIS_FOCUSED|LVIS_SELECTED))
  3932. {
  3933. _OnLVSelectionChange(i, uState, 0, (LPARAM)pidlOldToFree);
  3934. }
  3935. // remove the item.
  3936. CallCB(SFVM_DELETEITEM, 0, (LPARAM)pidlOldToFree);
  3937. // now insert it with a new pidl
  3938. CallCB(SFVM_INSERTITEM, 0, (LPARAM)pidlNewClone);
  3939. // if it was selected, select it again
  3940. if (uState & (LVIS_FOCUSED|LVIS_SELECTED))
  3941. {
  3942. _OnLVSelectionChange(i, 0, uState, (LPARAM)pidlNewClone);
  3943. }
  3944. if (_fGroupView)
  3945. {
  3946. item.mask |= LVIF_GROUPID;
  3947. item.iGroupId = (int)_GetGroupForItem(item.iItem, pidlNewClone);
  3948. }
  3949. ListView_SetItem(_hwndListview, &item);
  3950. if (_bBkFilling)
  3951. {
  3952. _pEnumTask->_DeleteFromPending(pidlOld);
  3953. _pEnumTask->_AddToPending(pidlNewClone);
  3954. }
  3955. int cCols = _GetHeaderCount();
  3956. for (item.iSubItem++; item.iSubItem < cCols; item.iSubItem++)
  3957. {
  3958. ListView_SetItemText(_hwndListview, item.iItem, item.iSubItem,
  3959. LPSTR_TEXTCALLBACK);
  3960. }
  3961. //
  3962. // Warning!!! Only free pidlOldToFree *after* calling ListView_SetItem. ListView_SetItem
  3963. // can call back asking for image info on the old pidl!
  3964. //
  3965. // Now delete the item but don't call the callback since we did that already.
  3966. OnListViewDelete(i, pidlOldToFree, FALSE);
  3967. _OnContentsChanged();
  3968. }
  3969. else
  3970. {
  3971. i = -1;
  3972. }
  3973. }
  3974. }
  3975. return i;
  3976. }
  3977. //
  3978. // invalidates all items with the given image index.
  3979. //
  3980. // or update all items if iImage == -1
  3981. //
  3982. void CDefView::_UpdateImage(int iImage)
  3983. {
  3984. // -1 means update all
  3985. // reset the imagelists incase the size has changed, and do
  3986. // a full update.
  3987. if (iImage == -1)
  3988. {
  3989. if (_IsImageMode())
  3990. {
  3991. _RemoveThumbviewTasks();
  3992. _pImageCache->Flush(TRUE);
  3993. _SetThumbview();
  3994. }
  3995. else if (_IsTileMode())
  3996. {
  3997. _SetTileview();
  3998. }
  3999. else
  4000. {
  4001. _SetSysImageList();
  4002. }
  4003. _ReloadContent();
  4004. }
  4005. else
  4006. {
  4007. // get a dc so we can optimize for visible/not visible cases
  4008. HDC hdcLV = GetDC(_hwndListview);
  4009. // scan the listview updating any items which match
  4010. LV_ITEM item = {0};
  4011. int cItems = ListView_GetItemCount(_hwndListview);
  4012. for (item.iItem = 0; item.iItem < cItems; item.iItem++)
  4013. {
  4014. item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_NORECOMPUTE;
  4015. ListView_GetItem(_hwndListview, &item);
  4016. int iImageOld = item.iImage;
  4017. if (item.iImage == iImage) // this filters I_IMAGECALLBACK for us
  4018. {
  4019. RECT rc;
  4020. LPCITEMIDLIST pidl = _GetPIDLParam(item.lParam, item.iItem);
  4021. CFSFolder_UpdateIcon(_pshf, pidl);
  4022. //
  4023. // if the item is visible then we don't want to flicker so just
  4024. // kick off an async extract. if the item is not visible then
  4025. // leave it for later by slamming in I_IMAGECALLBACK.
  4026. //
  4027. item.iImage = I_IMAGECALLBACK;
  4028. if (!_IsImageMode() && ListView_GetItemRect(_hwndListview, item.iItem, &rc, LVIR_ICON) &&
  4029. RectVisible(hdcLV, &rc))
  4030. {
  4031. int iImageNew;
  4032. HRESULT hr = _GetIconAsync(pidl, &iImageNew, FALSE);
  4033. if (hr == S_FALSE)
  4034. continue;
  4035. if (SUCCEEDED(hr))
  4036. {
  4037. if (iImageNew == iImageOld)
  4038. {
  4039. ListView_RedrawItems(_hwndListview, item.iItem, item.iItem);
  4040. continue;
  4041. }
  4042. item.iImage = iImageNew;
  4043. }
  4044. }
  4045. item.mask = LVIF_IMAGE;
  4046. item.iSubItem = 0;
  4047. ListView_SetItem(_hwndListview, &item);
  4048. }
  4049. }
  4050. ReleaseDC(_hwndListview, hdcLV);
  4051. }
  4052. }
  4053. // Function to process the SFVM_REFRESHOBJECT message, by searching
  4054. // through the list for a match of the first pidl. If a match is found,
  4055. // the item is redrawn.
  4056. int CDefView::_RefreshObject(LPITEMIDLIST *ppidl)
  4057. {
  4058. int i = _FindItem(ppidl[0], NULL, FALSE);
  4059. if (i >= 0)
  4060. ListView_RedrawItems(_hwndListview, i, i);
  4061. return i;
  4062. }
  4063. HRESULT CDefView::_GetItemObjects(LPCITEMIDLIST **ppidl, UINT uWhat, UINT *pcItems)
  4064. {
  4065. *pcItems = _GetItemArray(NULL, 0, uWhat);
  4066. if (ppidl)
  4067. {
  4068. *ppidl = NULL;
  4069. if (*pcItems)
  4070. {
  4071. *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, sizeof(*ppidl) * (*pcItems));
  4072. if (*ppidl)
  4073. _GetItemArray(*ppidl, *pcItems, uWhat);
  4074. else
  4075. return E_OUTOFMEMORY;
  4076. }
  4077. }
  4078. return S_OK;
  4079. }
  4080. void CDefView::_SetItemPosition(int i, int x, int y)
  4081. {
  4082. ListView_SetItemPosition32(_hwndListview, i, x, y);
  4083. _fUserPositionedItems = TRUE;
  4084. }
  4085. void CDefView::_SetItemPos(LPSFV_SETITEMPOS psip)
  4086. {
  4087. int i = _FindItem(psip->pidl, NULL, FALSE);
  4088. if (i >= 0)
  4089. {
  4090. _SetItemPosition(i, psip->pt.x, psip->pt.y);
  4091. }
  4092. }
  4093. // "View State" here refers to column information and icon positions
  4094. BOOL CDefView::GetViewState()
  4095. {
  4096. BOOL bRet = FALSE;
  4097. IPropertyBag* ppb;
  4098. if (SUCCEEDED(IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
  4099. {
  4100. DWORD dw;
  4101. // Check if we've saved state before (first check) or if we may want to
  4102. // try upgrading some settings if we haven't saved state before (second check)
  4103. if (SUCCEEDED(SHPropertyBag_ReadDWORD(ppb, VS_PROPSTR_MODE, &dw)) ||
  4104. SUCCEEDED(SHPropertyBag_ReadDWORD(ppb, VS_PROPSTR_FFLAGS, &dw)))
  4105. {
  4106. bRet = SUCCEEDED(_vs.LoadFromPropertyBag(this, ppb));
  4107. }
  4108. else
  4109. {
  4110. IStream* pstm;
  4111. if (SUCCEEDED(_LoadGlobalViewState(&pstm)))
  4112. {
  4113. _vs.LoadFromStream(this, pstm);
  4114. bRet = TRUE;
  4115. pstm->Release();
  4116. }
  4117. }
  4118. ppb->Release();
  4119. }
  4120. else
  4121. {
  4122. // 99/02/05 #226140 vtan: Try to get the view state stream
  4123. // from ShellBrowser. If that fails then look for a global
  4124. // view state stream that is stored when the user clicks on
  4125. // the "Like Current Folder" in the View tab of folder settings.
  4126. // IShellBrowser::GetViewStateStream() match the dwDefRevCount
  4127. // of the cabinet state to make sure that it's valid.
  4128. IStream *pstm;
  4129. if (SUCCEEDED(_psb->GetViewStateStream(STGM_READ, &pstm)) ||
  4130. SUCCEEDED(_LoadGlobalViewState(&pstm)))
  4131. {
  4132. _vs.LoadFromStream(this, pstm);
  4133. pstm->Release();
  4134. bRet = TRUE;
  4135. }
  4136. }
  4137. return bRet;
  4138. }
  4139. void CDefView::_UpdateEnumerationFlags()
  4140. {
  4141. SHELLSTATE ss;
  4142. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS | SSF_SHOWCOMPCOLOR, FALSE);
  4143. _fShowAllObjects = ss.fShowAllObjects;
  4144. // Don't allow compression coloring on the desktop proper
  4145. _fShowCompColor = _IsDesktop() ? FALSE : ss.fShowCompColor;
  4146. }
  4147. // starts and stops the spinning Globe animation
  4148. // indicating that we are in the process of navigating to
  4149. // a directory
  4150. void CDefView::_GlobeAnimation(BOOL fStartSpinning, BOOL fForceStop)
  4151. {
  4152. if (_fGlobeCanSpin)
  4153. {
  4154. DWORD dwCmdID = 0;
  4155. if (fStartSpinning)
  4156. {
  4157. if (_crefGlobeSpin++ == 0)
  4158. {
  4159. dwCmdID = CBRANDIDM_STARTGLOBEANIMATION;
  4160. }
  4161. }
  4162. else
  4163. {
  4164. ASSERT(_crefGlobeSpin > 0);
  4165. if (fForceStop || (--_crefGlobeSpin == 0))
  4166. {
  4167. dwCmdID = CBRANDIDM_STOPGLOBEANIMATION;
  4168. // our navigation is over, never spin again
  4169. _fGlobeCanSpin = FALSE;
  4170. }
  4171. }
  4172. if (dwCmdID)
  4173. {
  4174. IUnknown_QueryServiceExec(_psb, SID_SBrandBand, &CGID_BrandCmdGroup, dwCmdID, 0, NULL, NULL);
  4175. }
  4176. }
  4177. }
  4178. LRESULT SearchingUIWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4179. {
  4180. switch (uMsg)
  4181. {
  4182. case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC):
  4183. SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg),
  4184. GetSysColor(COLOR_WINDOW));
  4185. return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
  4186. default:
  4187. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  4188. }
  4189. return 0;
  4190. }
  4191. void CDefView::_ShowSearchUI(BOOL fStartSearchWindow)
  4192. {
  4193. if (_fAllowSearchingWindow || _crefSearchWindow) // once started, make sure our refcount finishes
  4194. {
  4195. if (fStartSearchWindow)
  4196. {
  4197. if (_crefSearchWindow++ == 0)
  4198. {
  4199. // The static window could already exist during a refresh
  4200. if (!_hwndStatic)
  4201. {
  4202. _hwndStatic = SHCreateWorkerWindowW((WNDPROC)SearchingUIWndProc, _hwndView, 0,
  4203. WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  4204. NULL, NULL);
  4205. if (_hwndStatic)
  4206. {
  4207. HWND hAnimate = CreateWindowEx(0, ANIMATE_CLASS, c_szNULL,
  4208. WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | ACS_TRANSPARENT | ACS_AUTOPLAY | ACS_CENTER,
  4209. 0, 0, 0, 0, _hwndStatic, (HMENU)ID_STATIC, HINST_THISDLL, NULL);
  4210. if (hAnimate)
  4211. {
  4212. RECT rc;
  4213. GetClientRect(_hwndView, &rc);
  4214. // Move this window to the top so the user sees the "looking" icon
  4215. // We are in a "normal" view. We need to do this always or the
  4216. // Flashlight doesn't appear. It tested safe with WebView on.
  4217. SetWindowPos(_hwndStatic, HWND_TOP, 0, 0, rc.right, rc.bottom, 0);
  4218. SetWindowPos(hAnimate, HWND_TOP, 0, 0, rc.right, rc.bottom, SWP_NOZORDER);
  4219. _OnMoveWindowToTop(_hwndStatic);
  4220. SetTimer(_hwndView, DV_IDTIMER_START_ANI, 2000, NULL); // 2 second timer
  4221. }
  4222. }
  4223. }
  4224. ShowHideListView();
  4225. }
  4226. }
  4227. else
  4228. {
  4229. if (0 == _crefSearchWindow) // if _ShowSearchUI(FALSE) gets called before _ShowSearchUI(TRUE)
  4230. {
  4231. _fAllowSearchingWindow = FALSE;
  4232. }
  4233. else if (--_crefSearchWindow == 0)
  4234. {
  4235. _fAllowSearchingWindow = FALSE;
  4236. ShowHideListView();
  4237. }
  4238. }
  4239. }
  4240. }
  4241. // this is only called from within SHCNE_* don't put up ui on the enum error.
  4242. void CDefView::_FullViewUpdate(BOOL fUpdateItem)
  4243. {
  4244. if (fUpdateItem)
  4245. _ReloadContent(); // the folder we're looking at has changed
  4246. else
  4247. FillObjectsShowHide(FALSE); // our contents have changed
  4248. }
  4249. void CDefView::_ShowControl(UINT idControl, int idCmd)
  4250. {
  4251. IBrowserService *pbs;
  4252. if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs))))
  4253. {
  4254. pbs->ShowControlWindow(idControl, idCmd);
  4255. pbs->Release();
  4256. }
  4257. }
  4258. // This function does three things:
  4259. // 1 - Alter the size of the parent to best fit around the items we have.
  4260. // 2 - Set the default icon view mode
  4261. // 3 - Make sure the correct toolbars are showing
  4262. //
  4263. void CDefView::_BestFit()
  4264. {
  4265. // Only bestfit once
  4266. if (_fs.fFlags & FWF_BESTFITWINDOW)
  4267. {
  4268. _fs.fFlags &= ~FWF_BESTFITWINDOW;
  4269. // Make sure the correct toolbars are showing the first time this folder is displayed
  4270. //
  4271. int iITbar = SBSC_HIDE;
  4272. int iStdBar = SBSC_HIDE;
  4273. switch (_uDefToolbar)
  4274. {
  4275. case HIWORD(TBIF_INTERNETBAR):
  4276. iITbar = SBSC_SHOW;
  4277. goto ShowToolbar;
  4278. case HIWORD(TBIF_STANDARDTOOLBAR):
  4279. iStdBar = SBSC_SHOW;
  4280. goto ShowToolbar;
  4281. case HIWORD(TBIF_NOTOOLBAR):
  4282. ShowToolbar:
  4283. _ShowControl(FCW_INTERNETBAR, iITbar);
  4284. _ShowControl(FCW_TOOLBAR, iStdBar);
  4285. break;
  4286. }
  4287. }
  4288. }
  4289. void CDefView::_ClearPostedMsgs(HWND hwnd)
  4290. {
  4291. MSG msg;
  4292. while (PeekMessage(&msg, hwnd, WM_DSV_UPDATEICON, WM_DSV_UPDATEICON, PM_REMOVE))
  4293. {
  4294. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4295. // Verify that the message was really for us.
  4296. //
  4297. if (msg.hwnd == hwnd)
  4298. {
  4299. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATEICON after WM_DESTROY!!!");
  4300. LPITEMIDLIST pidl = (LPITEMIDLIST) msg.wParam;
  4301. ILFree(pidl);
  4302. }
  4303. }
  4304. while (PeekMessage(&msg, hwnd, WM_DSV_UPDATECOLDATA, WM_DSV_UPDATECOLDATA, PM_REMOVE))
  4305. {
  4306. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4307. // Verify that the message was really for us.
  4308. //
  4309. if (msg.hwnd == hwnd)
  4310. {
  4311. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATECOLDATA after WM_DESTROY!!!");
  4312. delete (CBackgroundColInfo*)msg.lParam;
  4313. }
  4314. }
  4315. while (PeekMessage(&msg, hwnd, WM_DSV_DELAYSTATUSBARUPDATE, WM_DSV_DELAYSTATUSBARUPDATE, PM_REMOVE))
  4316. {
  4317. if (msg.hwnd == hwnd)
  4318. {
  4319. LocalFree((void *)msg.lParam);
  4320. }
  4321. }
  4322. while (PeekMessage(&msg, hwnd, WM_DSV_SETIMPORTANTCOLUMNS, WM_DSV_SETIMPORTANTCOLUMNS, PM_REMOVE))
  4323. {
  4324. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4325. // Verify that the message was really for us.
  4326. //
  4327. if (msg.hwnd == hwnd)
  4328. {
  4329. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_SETIMPORTANTCOLUMNS after WM_DESTROY!!!");
  4330. delete (CBackgroundTileInfo*)msg.lParam;
  4331. }
  4332. }
  4333. while (PeekMessage(&msg, hwnd, WM_DSV_SETITEMGROUP, WM_DSV_SETITEMGROUP, PM_REMOVE))
  4334. {
  4335. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4336. // Verify that the message was really for us.
  4337. //
  4338. if (msg.hwnd == hwnd)
  4339. {
  4340. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_SETITEMGROUP after WM_DESTROY!!!");
  4341. delete (CBackgroundGroupInfo*)msg.lParam;
  4342. }
  4343. }
  4344. while (PeekMessage(&msg, hwnd, WM_DSV_UPDATETHUMBNAIL, WM_DSV_UPDATETHUMBNAIL, PM_REMOVE))
  4345. {
  4346. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4347. // Verify that the message was really for us.
  4348. //
  4349. if (msg.hwnd == hwnd)
  4350. {
  4351. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_UPDATETHUMBNAIL after WM_DESTROY!!!");
  4352. _CleanupUpdateThumbnail((DSV_UPDATETHUMBNAIL*)msg.lParam);
  4353. }
  4354. }
  4355. while (PeekMessage(&msg, hwnd, WM_DSV_POSTCREATEINFOTIP, WM_DSV_POSTCREATEINFOTIP, PM_REMOVE))
  4356. {
  4357. // PeekMessage(hwnd) can return messages posted to CHILDREN of this hwnd...
  4358. // Verify that the message was really for us.
  4359. //
  4360. if (msg.hwnd == hwnd)
  4361. {
  4362. TraceMsg(TF_DEFVIEW, "DefView: WM_DSV_POSTCREATEINFOTIP after WM_DESTROY!!!");
  4363. _OnPostCreateInfotipCleanup((TOOLINFO *)msg.wParam);
  4364. }
  4365. }
  4366. }
  4367. void CDefView::_CallRefresh(BOOL fPreRefresh)
  4368. {
  4369. if (fPreRefresh)
  4370. {
  4371. IUnknown_Exec(_pshf, NULL, OLECMDID_REFRESH, 0, NULL, NULL);
  4372. }
  4373. CallCB(SFVM_REFRESH, fPreRefresh, 0);
  4374. }
  4375. void CDefView::FillDone()
  4376. {
  4377. SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0);
  4378. _fListviewRedraw = TRUE;
  4379. AddRef(); // hold a ref to ourself while in this function.
  4380. _fAllowSearchingWindow = FALSE;
  4381. _PostFillDoneMessage();
  4382. if (_bBkFilling)
  4383. _OnStopBackgroundEnum();
  4384. HRESULT hr = _pEnumTask->FillObjectsDoneToView();
  4385. _pEnumTask->Release();
  4386. _pEnumTask = NULL;
  4387. if (SUCCEEDED(hr))
  4388. {
  4389. // Clear our error state, if we were in one
  4390. _fEnumFailed = FALSE;
  4391. if (_fSyncOnFillDone)
  4392. {
  4393. _vs.Sync(this, TRUE);
  4394. _fSyncOnFillDone = FALSE;
  4395. }
  4396. ShowHideListView();
  4397. // set the focus on the first item.
  4398. _FocusOnSomething();
  4399. _DoThumbnailReadAhead();
  4400. }
  4401. else
  4402. {
  4403. // The fill objects failed for some reason, go into error mode
  4404. TraceMsg(TF_WARNING, "::FillObjects failed to enumerate for some reason");
  4405. _fEnumFailed = TRUE;
  4406. ShowHideListView();
  4407. }
  4408. // Tell the defview client that this window has been refreshed
  4409. _CallRefresh(FALSE);
  4410. _OnContentsChanged();
  4411. _UpdateStatusBar(TRUE);
  4412. _PostEnumDoneMessage();
  4413. Release();
  4414. SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0);
  4415. _fListviewRedraw = FALSE;
  4416. }
  4417. HRESULT CDefView::_OnStartBackgroundEnum()
  4418. {
  4419. _GlobeAnimation(TRUE);
  4420. _ShowSearchUI(TRUE);
  4421. _bBkFilling = TRUE;
  4422. return S_OK;
  4423. }
  4424. HRESULT CDefView::_OnStopBackgroundEnum()
  4425. {
  4426. _bBkFilling = FALSE;
  4427. _GlobeAnimation(FALSE);
  4428. _ShowSearchUI(FALSE);
  4429. return S_OK;
  4430. }
  4431. HRESULT CDefView::_OnBackgroundEnumDone()
  4432. {
  4433. FillDone();
  4434. _UpdateStatusBar(FALSE);
  4435. CallCB(SFVM_BACKGROUNDENUMDONE, 0, 0);
  4436. return S_OK;
  4437. }
  4438. HRESULT EmptyBkgrndThread(IShellTaskScheduler *pScheduler)
  4439. {
  4440. HRESULT hr = S_OK;
  4441. if (pScheduler)
  4442. {
  4443. // empty the queue and wait until it is empty.....
  4444. hr = pScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, TRUE);
  4445. }
  4446. return hr;
  4447. }
  4448. DWORD CDefView::_GetEnumFlags()
  4449. {
  4450. // Setup the enum flags.
  4451. DWORD grfEnumFlags = SHCONTF_NONFOLDERS;
  4452. if (_fShowAllObjects)
  4453. grfEnumFlags |= SHCONTF_INCLUDEHIDDEN;
  4454. //Is this View in Common Dialog
  4455. if (!(grfEnumFlags & SHCONTF_INCLUDEHIDDEN))
  4456. {
  4457. // Ask Common dialog if its wants to show all files
  4458. ICommDlgBrowser2 *pcdb2;
  4459. if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser2, &pcdb2))))
  4460. {
  4461. DWORD dwFlags = 0;
  4462. pcdb2->GetViewFlags(&dwFlags);
  4463. if (dwFlags & CDB2GVF_SHOWALLFILES)
  4464. grfEnumFlags |= SHCONTF_INCLUDEHIDDEN;
  4465. pcdb2->Release();
  4466. }
  4467. }
  4468. if (!(_fs.fFlags & FWF_NOSUBFOLDERS))
  4469. grfEnumFlags |= SHCONTF_FOLDERS;
  4470. return grfEnumFlags;
  4471. }
  4472. HRESULT CDefView::FillObjectsShowHide(BOOL fInteractive)
  4473. {
  4474. HRESULT hr = S_OK;
  4475. DECLAREWAITCURSOR;
  4476. SetWaitCursor(); // This is a potentially long operation
  4477. // To get here we're either not enumerating at all,
  4478. // or we are enumerating on the background thread,
  4479. // or we got re-entered
  4480. ASSERT((!_pEnumTask&&!_bBkFilling) || (_pEnumTask));
  4481. if (_pEnumTask)
  4482. {
  4483. if (fInteractive)
  4484. {
  4485. // This is in response to the user pressing F5,
  4486. // assume the current enumeration will be valid
  4487. hr = S_FALSE;
  4488. }
  4489. else if (!_bBkFilling)
  4490. {
  4491. // We're not on the background but we have a _pEnumTask, this means
  4492. // that we got re-entered during the below call to FillObjectsToDPA.
  4493. // Assume the current enumeration attempt will be valid
  4494. hr = S_FALSE;
  4495. }
  4496. else
  4497. {
  4498. if (_pScheduler)
  4499. {
  4500. // An UPDATEDIR or equivalent happened, anything already enumerated could be bad.
  4501. // Tell the current enumeration task to give up
  4502. _pScheduler->RemoveTasks(TOID_DVBackgroundEnum, ITSAT_DEFAULT_LPARAM, FALSE);
  4503. _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE);
  4504. }
  4505. ASSERT(_bBkFilling);
  4506. _OnStopBackgroundEnum();
  4507. _pEnumTask->Release();
  4508. _pEnumTask = NULL;
  4509. }
  4510. }
  4511. if (S_OK==hr)
  4512. {
  4513. _pEnumTask = new CDefviewEnumTask(this, ++_dwEnumId);
  4514. if (_pEnumTask)
  4515. {
  4516. // Note: It is possible for us to get re-entered during FillObjectsToDPA,
  4517. // since we pass our HWND to the enumerator.
  4518. _pEnumTask->FillObjectsToDPA(fInteractive);
  4519. hr = _pEnumTask->FillObjectsDPAToDone();
  4520. }
  4521. else
  4522. {
  4523. _fEnumFailed = TRUE;
  4524. ShowHideListView();
  4525. hr = E_OUTOFMEMORY;
  4526. }
  4527. }
  4528. ResetWaitCursor();
  4529. return hr;
  4530. }
  4531. // This implementation uses following assumptions.
  4532. // (1) The IShellFolder uses CDefFolderMenu.
  4533. // (2) The CDefFolderMenu always add the folder at the top.
  4534. #define EC_SELECTION 0
  4535. #define EC_BACKGROUND 1
  4536. #define EC_EITHER 3
  4537. HRESULT CDefView::_ExplorerCommand(UINT idFCIDM)
  4538. {
  4539. HRESULT hr = E_FAIL;
  4540. static struct {
  4541. UINT idmFC;
  4542. UINT fBackground;
  4543. LPCTSTR pszVerb;
  4544. } const c_idMap[] = {
  4545. { SFVIDM_FILE_RENAME, EC_SELECTION, c_szRename },
  4546. { SFVIDM_FILE_DELETE, EC_SELECTION, c_szDelete },
  4547. { SFVIDM_FILE_PROPERTIES, EC_EITHER, c_szProperties },
  4548. { SFVIDM_EDIT_COPY, EC_SELECTION, c_szCopy },
  4549. { SFVIDM_EDIT_CUT, EC_SELECTION, c_szCut },
  4550. { SFVIDM_FILE_LINK, EC_SELECTION, c_szLink },
  4551. { SFVIDM_EDIT_PASTE, EC_BACKGROUND, c_szPaste },
  4552. { SFVIDM_EDIT_PASTELINK, EC_BACKGROUND, c_szPasteLink },
  4553. };
  4554. for (int i = 0; i < ARRAYSIZE(c_idMap); i++)
  4555. {
  4556. if (c_idMap[i].idmFC == idFCIDM)
  4557. {
  4558. IContextMenu *pcm;
  4559. if (c_idMap[i].fBackground == EC_BACKGROUND)
  4560. {
  4561. hr = _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcm));
  4562. }
  4563. else
  4564. {
  4565. hr = _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcm));
  4566. if (FAILED(hr) && (c_idMap[i].fBackground == EC_EITHER) && !ListView_GetSelectedCount(_hwndListview))
  4567. {
  4568. hr = _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcm));
  4569. }
  4570. }
  4571. if (SUCCEEDED(hr))
  4572. {
  4573. CMINVOKECOMMANDINFOEX ici = {0};
  4574. ici.cbSize = sizeof(ici);
  4575. ici.hwnd = _hwndMain;
  4576. ici.nShow = SW_NORMAL;
  4577. // record if shift or control was being held down
  4578. SetICIKeyModifiers(&ici.fMask);
  4579. // Fill in both the ansi verb and the unicode verb since we
  4580. // don't know who is going to be processing this thing.
  4581. CHAR szVerbAnsi[40];
  4582. SHUnicodeToAnsi(c_idMap[i].pszVerb, szVerbAnsi, ARRAYSIZE(szVerbAnsi));
  4583. ici.lpVerb = szVerbAnsi;
  4584. ici.lpVerbW = c_idMap[i].pszVerb;
  4585. ici.fMask |= CMIC_MASK_UNICODE;
  4586. HMENU hmenu = CreatePopupMenu();
  4587. if (hmenu)
  4588. {
  4589. IUnknown_SetSite(pcm, SAFECAST(this, IOleCommandTarget *));
  4590. pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, 0);
  4591. _bContextMenuMode = TRUE;
  4592. hr = _InvokeContextMenu(pcm, &ici);
  4593. _bContextMenuMode = FALSE;
  4594. DestroyMenu(hmenu);
  4595. IUnknown_SetSite(pcm, NULL);
  4596. }
  4597. pcm->Release();
  4598. }
  4599. else
  4600. {
  4601. // keys are pressed when there is no selection.
  4602. MessageBeep(0);
  4603. }
  4604. break;
  4605. }
  4606. ASSERT(i < ARRAYSIZE(c_idMap));
  4607. }
  4608. return hr;
  4609. }
  4610. STDAPI_(BOOL) Def_IsPasteAvailable(IDropTarget *pdtgt, DWORD *pdwEffect);
  4611. BOOL CDefView::_AllowCommand(UINT uID)
  4612. {
  4613. DWORD dwAttribsIn;
  4614. DWORD dwEffect;
  4615. switch (uID)
  4616. {
  4617. case SFVIDM_EDIT_PASTE:
  4618. return Def_IsPasteAvailable(_pdtgtBack, &dwEffect);
  4619. case SFVIDM_EDIT_PASTELINK:
  4620. Def_IsPasteAvailable(_pdtgtBack, &dwEffect);
  4621. return dwEffect & DROPEFFECT_LINK;
  4622. case SFVIDM_EDIT_COPY:
  4623. dwAttribsIn = SFGAO_CANCOPY;
  4624. break;
  4625. case SFVIDM_EDIT_CUT:
  4626. dwAttribsIn = SFGAO_CANMOVE;
  4627. break;
  4628. case SFVIDM_FILE_DELETE:
  4629. dwAttribsIn = SFGAO_CANDELETE;
  4630. break;
  4631. case SFVIDM_FILE_LINK:
  4632. dwAttribsIn = SFGAO_CANLINK;
  4633. break;
  4634. case SFVIDM_FILE_PROPERTIES:
  4635. dwAttribsIn = SFGAO_HASPROPSHEET;
  4636. break;
  4637. default:
  4638. ASSERT(FALSE);
  4639. return FALSE;
  4640. }
  4641. return _AttributesFromSel(dwAttribsIn) & dwAttribsIn;
  4642. }
  4643. // return copy of pidl of folder we're viewing
  4644. LPITEMIDLIST CDefView::_GetViewPidl()
  4645. {
  4646. LPITEMIDLIST pidl;
  4647. if (SHGetIDListFromUnk(_pshf, &pidl) != S_OK) // S_FALSE is success by empty
  4648. {
  4649. if (SUCCEEDED(CallCB(SFVM_THISIDLIST, 0, (LPARAM)&pidl)))
  4650. {
  4651. ASSERT(pidl);
  4652. }
  4653. else if (_SetupNotifyData() && _pidlMonitor)
  4654. {
  4655. pidl = ILClone(_pidlMonitor);
  4656. }
  4657. }
  4658. return pidl;
  4659. }
  4660. inline BOOL CDefView::_ItemsDeferred()
  4661. {
  4662. return _hdsaSelect != NULL;
  4663. }
  4664. BOOL CDefView::_IsListviewVisible()
  4665. {
  4666. return _fListViewShown;
  4667. }
  4668. inline BOOL CDefView::_IsOwnerData()
  4669. {
  4670. return _fs.fFlags & FWF_OWNERDATA;
  4671. }
  4672. inline BOOL CDefView::_IsCommonDialog()
  4673. {
  4674. return NULL != _pcdb;
  4675. }
  4676. BOOL CDefView::_IsDesktop()
  4677. {
  4678. return _fs.fFlags & FWF_DESKTOP;
  4679. }
  4680. BOOL CDefView::_IsViewDesktop()
  4681. {
  4682. BOOL bDesktop = FALSE;
  4683. LPITEMIDLIST pidl = _GetViewPidl();
  4684. if (pidl)
  4685. {
  4686. bDesktop = ILIsEmpty(pidl);
  4687. ILFree(pidl);
  4688. }
  4689. return bDesktop;
  4690. }
  4691. // access to the current views name ala IShellFolder::GetDisplayNameOf()
  4692. HRESULT CDefView::_GetNameAndFlags(UINT gdnFlags, LPTSTR pszPath, UINT cch, DWORD *pdwFlags)
  4693. {
  4694. *pszPath = 0;
  4695. HRESULT hr;
  4696. LPITEMIDLIST pidl = _GetViewPidl();
  4697. if (pidl)
  4698. {
  4699. hr = SHGetNameAndFlags(pidl, gdnFlags, pszPath, cch, pdwFlags);
  4700. ILFree(pidl);
  4701. }
  4702. else
  4703. hr = E_OUTOFMEMORY;
  4704. return hr;
  4705. }
  4706. // returns TRUE if the current view is a file system folder, returns the path
  4707. BOOL CDefView::_GetPath(LPTSTR pszPath)
  4708. {
  4709. *pszPath = 0;
  4710. LPITEMIDLIST pidl = _GetViewPidl();
  4711. if (pidl)
  4712. {
  4713. SHGetPathFromIDList(pidl, pszPath);
  4714. ILFree(pidl);
  4715. }
  4716. return *pszPath != 0;
  4717. }
  4718. EXTERN_C TCHAR const c_szHtmlWindowsHlp[] = TEXT("windows.chm");
  4719. // web view background colors, click mode, etc have changed
  4720. //
  4721. void CDefView::_UpdateListviewColors()
  4722. {
  4723. // First clear out our state
  4724. for (int i = 0; i < ARRAYSIZE(_crCustomColors); i++)
  4725. _crCustomColors[i] = CLR_MYINVALID;
  4726. // Then read the registry/desktop.ini
  4727. LPCTSTR pszLegacyWatermark = NULL;
  4728. SFVM_CUSTOMVIEWINFO_DATA cvi = {0};
  4729. if (SUCCEEDED(CallCB(SFVM_GETCUSTOMVIEWINFO, (WPARAM)0, (LPARAM)&cvi)))
  4730. {
  4731. if (!_IsCommonDialog() && !_IsDesktop())
  4732. {
  4733. // Set up the listview image, if any
  4734. if (*cvi.szIconAreaImage)
  4735. {
  4736. pszLegacyWatermark = cvi.szIconAreaImage;
  4737. }
  4738. // change the differing stuff
  4739. //
  4740. if (!_fClassic)
  4741. {
  4742. for (i = 0; i < ARRAYSIZE(_crCustomColors); i++)
  4743. {
  4744. COLORREF cr = cvi.crCustomColors[i];
  4745. if (ISVALIDCOLOR(cr))
  4746. {
  4747. _crCustomColors[i] = PALETTERGB(0, 0, 0) | cr;
  4748. }
  4749. }
  4750. // if there was an image specified but no custom text background,
  4751. // set to CLR_NONE so the listview text is transparent
  4752. // get combined view custom colors
  4753. if (!ISVALIDCOLOR(_crCustomColors[CRID_CUSTOMTEXTBACKGROUND]) && cvi.szIconAreaImage[0])
  4754. {
  4755. _crCustomColors[CRID_CUSTOMTEXTBACKGROUND] = CLR_NONE;
  4756. }
  4757. }
  4758. }
  4759. }
  4760. _SetLegacyWatermark(pszLegacyWatermark);
  4761. _SetFolderColors();
  4762. _UpdateSelectionMode();
  4763. }
  4764. BOOL CDefView::HasCurrentViewWindowFocus()
  4765. {
  4766. BOOL fRet = false;
  4767. HWND hwndCurrentFocus = GetFocus();
  4768. if (hwndCurrentFocus)
  4769. {
  4770. fRet = (SHIsChildOrSelf(_hwndListview, hwndCurrentFocus) == S_OK);
  4771. }
  4772. return fRet;
  4773. }
  4774. HWND CDefView::ViewWindowSetFocus()
  4775. {
  4776. SetFocus(_hwndListview);
  4777. if (!_IsDesktop())
  4778. {
  4779. _cFrame._uState = SVUIA_ACTIVATE_FOCUS;
  4780. }
  4781. return _hwndListview;
  4782. }
  4783. HRESULT CDefView::_GetSFVMViewState(UINT uViewMode, SFVM_VIEW_DATA* pvi)
  4784. {
  4785. HRESULT hr = CallCB(SFVM_GETVIEWDATA, (WPARAM)uViewMode, (LPARAM)pvi);
  4786. if (FAILED(hr))
  4787. {
  4788. pvi->dwOptions = SFVMQVI_NORMAL;
  4789. }
  4790. return hr;
  4791. }
  4792. HRESULT CDefView::_GetSFVMViewInfoTemplate(UINT uViewMode, SFVM_WEBVIEW_TEMPLATE_DATA* pvit)
  4793. {
  4794. return CallCB(SFVM_GETWEBVIEW_TEMPLATE, (WPARAM)uViewMode, (LPARAM)pvit);
  4795. }
  4796. HRESULT CDefView::_GetWebViewMoniker(LPWSTR pszMoniker, DWORD cchMoniker)
  4797. {
  4798. SFVM_WEBVIEW_TEMPLATE_DATA vit;
  4799. if (SUCCEEDED(_GetSFVMViewInfoTemplate(_fs.ViewMode, &vit)))
  4800. {
  4801. StrCpyN(pszMoniker, vit.szWebView, cchMoniker);
  4802. }
  4803. else
  4804. {
  4805. pszMoniker[0] = L'\0';
  4806. }
  4807. return *pszMoniker ? S_OK : E_FAIL;
  4808. }
  4809. // Show or hide Web View content
  4810. //
  4811. // This does not affect the View Mode of the listview (it does tweak desktop listview for _fCombinedView stuff)
  4812. //
  4813. // fShow==TRUE -> hr is success/fail of showing web view
  4814. // fShow==FALSE -> hr is E_FAIL (nobody looks at return code of turning web view off)
  4815. //
  4816. HRESULT CDefView::_SwitchToWebView(BOOL fShow)
  4817. {
  4818. HRESULT hr = E_FAIL;
  4819. // Cache the focus/select state across this transition
  4820. BOOL bSetFocusRequired = HasCurrentViewWindowFocus();
  4821. if (fShow)
  4822. {
  4823. // For now, the desktop is always a combined view...
  4824. if (_IsDesktop())
  4825. {
  4826. BOOL fCombinedViewOld = (BOOL)_fCombinedView;
  4827. SHELLSTATE ss;
  4828. SHGetSetSettings(&ss, SSF_HIDEICONS | SSF_DESKTOPHTML | SSF_STARTPANELON, FALSE);
  4829. // Does the user want desktop in HyperText view?
  4830. if (ss.fDesktopHTML)
  4831. _fCombinedView = TRUE;
  4832. if (ss.fHideIcons)
  4833. _fs.fFlags |= FWF_NOICONS;
  4834. else
  4835. _fs.fFlags &= ~FWF_NOICONS;
  4836. if (_fCombinedView && !fCombinedViewOld)
  4837. {
  4838. EnableCombinedView(this, TRUE);
  4839. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, LVS_EX_REGIONAL);
  4840. _SetFolderColors();
  4841. }
  4842. }
  4843. WCHAR wszMoniker[MAX_PATH];
  4844. hr = _GetWebViewMoniker(wszMoniker, ARRAYSIZE(wszMoniker));
  4845. if (SUCCEEDED(hr))
  4846. {
  4847. if (_IsDesktop())
  4848. {
  4849. IActiveDesktopP *piadp;
  4850. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IActiveDesktopP, &piadp))))
  4851. {
  4852. piadp->EnsureUpdateHTML();
  4853. piadp->Release();
  4854. }
  4855. hr = _cFrame.ShowWebView(wszMoniker);
  4856. }
  4857. else if (SHRestricted(REST_REVERTWEBVIEWSECURITY))
  4858. {
  4859. hr = _cFrame.ShowWebView(wszMoniker);
  4860. }
  4861. else if (!_fUserRejectedWebViewTemplate)
  4862. {
  4863. WCHAR szTemplate[MAX_PATH];
  4864. DWORD cchTemplate = ARRAYSIZE(szTemplate);
  4865. if (PathIsURL(wszMoniker))
  4866. {
  4867. hr = PathCreateFromUrl(wszMoniker, szTemplate, &cchTemplate, 0);
  4868. }
  4869. else
  4870. {
  4871. StringCchCopy(szTemplate, ARRAYSIZE(szTemplate), wszMoniker);
  4872. }
  4873. if (SUCCEEDED(hr))
  4874. {
  4875. DWORD dwFlags = SHRVT_VALIDATE | SHRVT_ALLOW_INTRANET;
  4876. if (SHRestricted(REST_ALLOWUNHASHEDWEBVIEW))
  4877. {
  4878. dwFlags |= SHRVT_PROMPTUSER | SHRVT_REGISTERIFPROMPTOK;
  4879. }
  4880. hr = SHRegisterValidateTemplate(szTemplate, dwFlags);
  4881. if (SUCCEEDED(hr))
  4882. {
  4883. hr = _cFrame.ShowWebView(wszMoniker);
  4884. }
  4885. else
  4886. {
  4887. _fUserRejectedWebViewTemplate = TRUE;
  4888. }
  4889. }
  4890. }
  4891. }
  4892. if (FAILED(hr))
  4893. {
  4894. fShow = FALSE;
  4895. }
  4896. else
  4897. {
  4898. RECT rcClient;
  4899. // Make sure the new view is the correct size
  4900. GetClientRect(_hwndView, &rcClient);
  4901. _cFrame.SetRect(&rcClient);
  4902. ShowHideListView();
  4903. }
  4904. }
  4905. if (!fShow)
  4906. {
  4907. _cFrame.HideWebView();
  4908. // If we were combined, then get the listview out of region mode and
  4909. // reset the color scheme. Also, turn off the combined bit.
  4910. if (_fCombinedView)
  4911. {
  4912. _fCombinedView = FALSE;
  4913. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, 0);
  4914. EnableCombinedView(this, FALSE);
  4915. _SetFolderColors();
  4916. }
  4917. }
  4918. // restore focus/select state -- if we switched to web view it will put much of
  4919. // this into a "pending" state until the listview is re-shown inside the web content
  4920. //
  4921. if (bSetFocusRequired)
  4922. {
  4923. CallCB(SFVM_SETFOCUS, 0, 0);
  4924. ViewWindowSetFocus();
  4925. }
  4926. CheckToolbar();
  4927. _EnableDisableTBButtons();
  4928. // make sure that the listview settings get refreshed anyway (back image)
  4929. _UpdateListviewColors();
  4930. return hr;
  4931. }
  4932. void CDefView::_RemoveThumbviewTasks()
  4933. {
  4934. if (_pScheduler)
  4935. {
  4936. _pScheduler->RemoveTasks(TOID_ExtractImageTask, ITSAT_DEFAULT_LPARAM, FALSE);
  4937. _pScheduler->RemoveTasks(TOID_CheckCacheTask, ITSAT_DEFAULT_LPARAM, FALSE);
  4938. _pScheduler->RemoveTasks(TOID_ReadAheadHandler, ITSAT_DEFAULT_LPARAM, FALSE);
  4939. _fReadAhead = FALSE;
  4940. }
  4941. }
  4942. //
  4943. // This function checkes to see if the list view needs to be shown; then shows it.
  4944. // If it needs to be hidden, hides it! You must call this function every time
  4945. // you change a bit of state that could change the show/hide state of listview.
  4946. //
  4947. // Let me repeat that: call this function EVERY TIME you change state that
  4948. // affects our show/hide.
  4949. //
  4950. HRESULT CDefView::ShowHideListView()
  4951. {
  4952. // NOTE: this is where most of the flicker bugs come from -- showing the
  4953. // listview too early. This is touchy code, so be careful when you change it.
  4954. // And plese document all changes for future generations. Thanks.
  4955. //
  4956. // Standard "is listview shown" check
  4957. //
  4958. // If our view hasn't been UIActivate()d yet, then we are waiting until
  4959. // the IShellBrowser selects us as the active view.
  4960. //
  4961. // App compat for above UIActivate() change:
  4962. // Adaptec Easy CD Creator never calls IShellView::UIActivate.
  4963. // They got away with it because UIActivate didn't used to do much,
  4964. // but now we use UIActivate to decide when to show our icons. They forget
  4965. // to call it and the icons never show up.
  4966. // So if we are in Win95 Defview compatibility mode, then
  4967. // go ahead and show the icons now. The app gets flicker, but at least
  4968. // the icons show up at all.
  4969. //
  4970. // Don't show the listview if we're told to not show it, or we see an error during enum.
  4971. //
  4972. // If we're enumerating in the background, don't show
  4973. //
  4974. // Potential problem: We used to defer SelectPendingSelectedItems until:
  4975. // "_fListViewShown && (_cFrame._dwConnectionCookie /*&& !_cFrame._fReadyStateInteractiveProcessed*/)"
  4976. // Selecting before readystatedone may pose a problem, but I don't see how it could
  4977. // be a problem unless showing the view early is a problem as well, which this code didn't check.
  4978. //
  4979. if ((!_cFrame.IsWebView() || _fGetWindowLV || _fCombinedView) // we think icons should be visible
  4980. && (_uState != SVUIA_DEACTIVATE || (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW)) // async defview means we don't show before we transition out of DEACTIVE
  4981. && !(BOOLIFY(_fs.fFlags & FWF_NOICONS)) // check if we've been told to not show icons
  4982. && !_fEnumFailed // failed enumeration wants _hwndView to show through, not _hwndListview
  4983. && !(_crefSearchWindow && _hwndStatic) // keep the listview hidden while we show the "searching" window
  4984. )
  4985. {
  4986. // Make sure we do each transition only once - we do more than just show the window
  4987. if (!_fListViewShown)
  4988. {
  4989. _fListViewShown = TRUE;
  4990. // Bring this to the top while showing it to avoid a second paint when
  4991. // _hwndStatic is destroyed (listview has optimizations when hidden,
  4992. // and it will repaint when once shown even if though it may be obscured)
  4993. //
  4994. SetWindowPos(_hwndListview, HWND_TOP, 0, 0, 0, 0,
  4995. SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
  4996. _OnMoveWindowToTop(_hwndListview);
  4997. // Remove _hwndStatic after listview is moved to top to avoid a re-paint
  4998. if (_hwndStatic)
  4999. {
  5000. DestroyWindow(_hwndStatic);
  5001. _hwndStatic = NULL;
  5002. }
  5003. // if we need to select items, do it now that the window is shown
  5004. SelectPendingSelectedItems();
  5005. }
  5006. }
  5007. else
  5008. {
  5009. if (_fListViewShown)
  5010. {
  5011. _fListViewShown = FALSE;
  5012. ShowWindow(_hwndListview, SW_HIDE);
  5013. }
  5014. // If FWF_NOICONS is set and the enumertion went to the background thread we need
  5015. // to make sure that we turn of the searchui.
  5016. if (BOOLIFY(_fs.fFlags & FWF_NOICONS) && _hwndStatic && 0 == _crefSearchWindow)
  5017. {
  5018. DestroyWindow(_hwndStatic);
  5019. _hwndStatic = NULL;
  5020. }
  5021. }
  5022. return S_OK;
  5023. }
  5024. IShellItemArray* CDefView::_GetFolderAsShellItemArray()
  5025. {
  5026. if (!_pFolderShellItemArray && _pshfParent && _pidlRelative)
  5027. {
  5028. SHCreateShellItemArray(NULL, _pshfParent, 1, (LPCITEMIDLIST *)&_pidlRelative, &_pFolderShellItemArray);
  5029. }
  5030. return _pFolderShellItemArray;
  5031. }
  5032. // if the attributes dwAttribMask for pdo exactly match dwAttribValue, this item should be enabled
  5033. HRESULT CDefView::_CheckAttribs(IShellItemArray *psiItemArray, DWORD dwAttribMask, DWORD dwAttribValue, UISTATE* puisState)
  5034. {
  5035. DWORD dwAttrib = 0;
  5036. HRESULT hr;
  5037. if (NULL == psiItemArray)
  5038. {
  5039. psiItemArray = _GetFolderAsShellItemArray();
  5040. }
  5041. if (psiItemArray)
  5042. {
  5043. hr = psiItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, dwAttribMask, &dwAttrib);
  5044. if (FAILED(hr))
  5045. dwAttrib = 0;
  5046. }
  5047. else
  5048. hr = S_OK;
  5049. *puisState = (dwAttribValue == dwAttrib) ? UIS_ENABLED : UIS_HIDDEN;
  5050. return hr;
  5051. }
  5052. HRESULT CDefView::_CanWrite(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5053. {
  5054. CDefView* pThis = (CDefView*)(void*)pv;
  5055. return pThis->_CheckAttribs(psiItemArray, SFGAO_READONLY|SFGAO_STORAGE, SFGAO_STORAGE, puisState);
  5056. }
  5057. HRESULT CDefView::_CanRename(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5058. {
  5059. CDefView* pThis = (CDefView*)(void*)pv;
  5060. return pThis->_CheckAttribs(psiItemArray, SFGAO_CANRENAME, SFGAO_CANRENAME, puisState);
  5061. }
  5062. HRESULT CDefView::_CanMove(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5063. {
  5064. CDefView* pThis = (CDefView*)(void*)pv;
  5065. return pThis->_CheckAttribs(psiItemArray, SFGAO_CANMOVE, SFGAO_CANMOVE, puisState);
  5066. }
  5067. HRESULT CDefView::_CanCopy(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5068. {
  5069. CDefView* pThis = (CDefView*)(void*)pv;
  5070. return pThis->_CheckAttribs(psiItemArray,SFGAO_CANCOPY, SFGAO_CANCOPY, puisState);
  5071. }
  5072. HRESULT CDefView::_CanPublish(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5073. {
  5074. CDefView* pThis = (CDefView*)(void*)pv;
  5075. *puisState = UIS_HIDDEN;
  5076. if ((pThis->_wvLayout.dwLayout & SFVMWVL_NOPUBLISH) || SHRestricted(REST_NOPUBLISHWIZARD))
  5077. {
  5078. // bail out early with UIS_HIDDEN, we dont show the verb
  5079. return S_OK;
  5080. }
  5081. // Iterate first 10 items because that is what old code did before
  5082. // switching to IShellItemArray. Since the attribs that are
  5083. // being requested for are already being cached in the ShellItemArray
  5084. // may as well always ask for all.
  5085. if (psiItemArray)
  5086. {
  5087. IEnumShellItems *pEnumShellItems;
  5088. if (SUCCEEDED(psiItemArray->EnumItems(&pEnumShellItems)))
  5089. {
  5090. IShellItem *pShellItem;
  5091. DWORD dwIterationCount = 0;
  5092. BOOL fHide = FALSE, fHasStreams = FALSE, fHasStorages = FALSE;
  5093. while (!fHide && (dwIterationCount < 10) && (S_OK == pEnumShellItems->Next(1, &pShellItem, NULL)))
  5094. {
  5095. SFGAOF dwAttribs = SFGAO_STORAGE | SFGAO_STREAM;
  5096. HRESULT hrAttribs = pShellItem->GetAttributes(dwAttribs, &dwAttribs);
  5097. pShellItem->Release();
  5098. pShellItem = NULL; // null to catch if we use it again.
  5099. if (SUCCEEDED(hrAttribs))
  5100. {
  5101. if (!(dwAttribs & (SFGAO_STORAGE | SFGAO_STREAM)))
  5102. {
  5103. // if this item doesn't have either storage or stream, hide the task.
  5104. fHide = TRUE;
  5105. }
  5106. else if (dwAttribs & SFGAO_STREAM)
  5107. {
  5108. // if we have a folder and files, hide the task.
  5109. fHide = fHasStorages;
  5110. fHasStreams = TRUE;
  5111. }
  5112. else if (dwAttribs & SFGAO_STORAGE)
  5113. {
  5114. // if we have multiple folders or a folder and files, hide the task.
  5115. fHide = fHasStorages || fHasStreams;
  5116. fHasStorages = TRUE;
  5117. }
  5118. }
  5119. ++dwIterationCount;
  5120. }
  5121. if (!fHide)
  5122. *puisState = UIS_ENABLED;
  5123. pEnumShellItems->Release();
  5124. }
  5125. }
  5126. else
  5127. {
  5128. // if nothing is selected, enable the task if the current folder is a storage.
  5129. LPITEMIDLIST pidl = pThis->_GetViewPidl();
  5130. if (pidl)
  5131. {
  5132. if (SHGetAttributes(NULL, pidl, SFGAO_STORAGE))
  5133. {
  5134. *puisState = UIS_ENABLED;
  5135. }
  5136. ILFree(pidl);
  5137. }
  5138. }
  5139. return S_OK;
  5140. }
  5141. // Note - _DoesStaticMenuHaveVerb only checks the first pidl in the data object for now
  5142. // So only use it for single-selections
  5143. // -DSheldon
  5144. BOOL CDefView::_DoesStaticMenuHaveVerb(IShellItemArray *psiItemArray, LPCWSTR pszVerb)
  5145. {
  5146. BOOL fHasVerb = FALSE;
  5147. IShellItem *pshItem;
  5148. // get first shellItem in the array.
  5149. if (SUCCEEDED(psiItemArray->GetItemAt(0,&pshItem)))
  5150. {
  5151. IQueryAssociations* pqa;
  5152. if (SUCCEEDED(pshItem->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARG(IQueryAssociations, &pqa))))
  5153. {
  5154. DWORD cch = 0;
  5155. fHasVerb = SUCCEEDED(pqa->GetString(0, ASSOCSTR_COMMAND, pszVerb, NULL, &cch));
  5156. pqa->Release();
  5157. }
  5158. pshItem->Release();
  5159. }
  5160. return fHasVerb;
  5161. }
  5162. HRESULT CDefView::_GetFullPathNameAt(IShellItemArray *psiItemArray,DWORD dwIndex,LPOLESTR *ppszPath)
  5163. {
  5164. HRESULT hr = E_FAIL;
  5165. IShellItem *pShellItem;
  5166. if (NULL == psiItemArray || NULL == ppszPath)
  5167. {
  5168. ASSERT(psiItemArray);
  5169. ASSERT(ppszPath);
  5170. return E_INVALIDARG;
  5171. }
  5172. // get the path of the first item in the ShellArray.
  5173. hr = psiItemArray->GetItemAt(dwIndex,&pShellItem);
  5174. if (SUCCEEDED(hr))
  5175. {
  5176. hr = pShellItem->GetDisplayName(SIGDN_FILESYSPATH,ppszPath);
  5177. pShellItem->Release();
  5178. }
  5179. return hr;
  5180. }
  5181. HRESULT CDefView::_CanShare(IUnknown* pv,IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5182. {
  5183. HRESULT hr = E_FAIL;
  5184. CDefView* pThis = (CDefView*)(void*)pv;
  5185. *puisState = UIS_DISABLED;
  5186. if (!psiItemArray)
  5187. {
  5188. psiItemArray = pThis->_GetFolderAsShellItemArray();
  5189. }
  5190. if (psiItemArray)
  5191. {
  5192. #ifdef DEBUG
  5193. // Sanity check.
  5194. DWORD dwNumItems;
  5195. ASSERT(S_OK == psiItemArray->GetCount(&dwNumItems));
  5196. ASSERT(1 == dwNumItems);
  5197. #endif
  5198. IShellItem *psi;
  5199. hr = psiItemArray->GetItemAt(0, &psi);
  5200. if (SUCCEEDED(hr))
  5201. {
  5202. // Retrieve pidl.
  5203. LPITEMIDLIST pidl;
  5204. hr = SHGetIDListFromUnk(psi, &pidl);
  5205. if (SUCCEEDED(hr))
  5206. {
  5207. // Retrieve path and attributes.
  5208. WCHAR szPath[MAX_PATH];
  5209. DWORD dwAttributes = SFGAO_LINK;
  5210. hr = SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &dwAttributes);
  5211. if (SUCCEEDED(hr) && !(dwAttributes & SFGAO_LINK) && !PathIsRemote(szPath))
  5212. {
  5213. if (!g_hmodNTSHRUI)
  5214. {
  5215. g_hmodNTSHRUI = LoadLibrary(L"ntshrui.dll");
  5216. }
  5217. if (g_hmodNTSHRUI)
  5218. {
  5219. PFNCANSHAREFOLDERW pfnCanShareFolder = (PFNCANSHAREFOLDERW)GetProcAddress(g_hmodNTSHRUI, "CanShareFolderW");
  5220. if (pfnCanShareFolder)
  5221. {
  5222. *puisState = (S_OK == pfnCanShareFolder(szPath)) ? UIS_ENABLED : UIS_DISABLED;
  5223. }
  5224. }
  5225. }
  5226. ILFree(pidl);
  5227. }
  5228. psi->Release();
  5229. }
  5230. }
  5231. return hr;
  5232. }
  5233. HRESULT CDefView::_CanEmail(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5234. {
  5235. DWORD dwAttributes = 0;
  5236. // Prevent people from attempting to e-mail non-filesystem objects.
  5237. // Attempting to attach such objects to an e-mail message fails.
  5238. // An example of this type of failure is attempting to e-mail
  5239. // connectoids in the "Network Connections" folder.
  5240. if (psiItemArray)
  5241. {
  5242. psiItemArray->GetAttributes(SIATTRIBFLAGS_APPCOMPAT, SFGAO_FILESYSTEM, &dwAttributes);
  5243. }
  5244. *puisState = dwAttributes & SFGAO_FILESYSTEM ? UIS_ENABLED : UIS_DISABLED;
  5245. return S_OK;
  5246. }
  5247. HRESULT CDefView::_CanPrint(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5248. {
  5249. if (!(((CDefView*)(void*)pv)->_wvLayout.dwLayout & SFVMWVL_NOPRINT))
  5250. return _HasPrintVerb(pv, psiItemArray, fOkToBeSlow, puisState);
  5251. *puisState = UIS_HIDDEN;
  5252. return S_OK;
  5253. }
  5254. HRESULT CDefView::_HasPrintVerb(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5255. {
  5256. CDefView* pThis = (CDefView*)(void*)pv;
  5257. if (!psiItemArray)
  5258. {
  5259. psiItemArray = pThis->_GetFolderAsShellItemArray();
  5260. }
  5261. BOOL fHasPrint = _DoesStaticMenuHaveVerb(psiItemArray,c_szPrintW);
  5262. *puisState = (fHasPrint) ? UIS_ENABLED : UIS_HIDDEN;
  5263. return S_OK;
  5264. }
  5265. HRESULT CDefView::_CanDelete(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5266. {
  5267. CDefView* pThis = (CDefView*)(void*)pv;
  5268. return pThis->_CheckAttribs(psiItemArray, SFGAO_CANDELETE, SFGAO_CANDELETE, puisState);
  5269. }
  5270. // determines if defview is hosted over the system drive root or not
  5271. BOOL CDefView::_IsSystemDrive(void)
  5272. {
  5273. TCHAR szPath[MAX_PATH];
  5274. TCHAR szSystemDrive[4];
  5275. BOOL bResult = FALSE;
  5276. if (SUCCEEDED(_GetPath(szPath)))
  5277. {
  5278. SHExpandEnvironmentStrings (TEXT("%SystemDrive%\\"), szSystemDrive, ARRAYSIZE(szSystemDrive));
  5279. if (!lstrcmpi(szPath, szSystemDrive))
  5280. {
  5281. bResult = TRUE;
  5282. }
  5283. }
  5284. return bResult;
  5285. }
  5286. HRESULT CDefView::_CanViewDrives(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5287. {
  5288. CDefView* pThis = (CDefView*)(void*)pv;
  5289. *puisState = UIS_DISABLED;
  5290. if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5291. {
  5292. if (pThis->_fBarrierDisplayed)
  5293. {
  5294. if (pThis->_IsSystemDrive())
  5295. {
  5296. *puisState = UIS_ENABLED;
  5297. }
  5298. }
  5299. }
  5300. return S_OK;
  5301. }
  5302. HRESULT CDefView::_CanHideDrives(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5303. {
  5304. CDefView* pThis = (CDefView*)(void*)pv;
  5305. *puisState = UIS_DISABLED;
  5306. if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5307. {
  5308. if (!pThis->_fBarrierDisplayed)
  5309. {
  5310. if (pThis->_IsSystemDrive())
  5311. {
  5312. *puisState = UIS_ENABLED;
  5313. }
  5314. }
  5315. }
  5316. return S_OK;
  5317. }
  5318. HRESULT CDefView::_CanViewFolder(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5319. {
  5320. CDefView* pThis = (CDefView*)(void*)pv;
  5321. *puisState = UIS_DISABLED;
  5322. if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5323. {
  5324. if (pThis->_fBarrierDisplayed)
  5325. {
  5326. if (!pThis->_IsSystemDrive())
  5327. {
  5328. *puisState = UIS_ENABLED;
  5329. }
  5330. }
  5331. }
  5332. return S_OK;
  5333. }
  5334. HRESULT CDefView::_CanHideFolder(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5335. {
  5336. CDefView* pThis = (CDefView*)(void*)pv;
  5337. *puisState = UIS_DISABLED;
  5338. if (pThis->_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5339. {
  5340. if (!pThis->_fBarrierDisplayed)
  5341. {
  5342. if (!pThis->_IsSystemDrive())
  5343. {
  5344. *puisState = UIS_ENABLED;
  5345. }
  5346. }
  5347. }
  5348. return S_OK;
  5349. }
  5350. HRESULT CDefView::_HasPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
  5351. {
  5352. HRESULT hr = S_OK;
  5353. *puisState = UIS_HIDDEN;
  5354. if (!psiItemArray)
  5355. {
  5356. CDefView* pThis = (CDefView*)(void*)pv;
  5357. psiItemArray = pThis->_GetFolderAsShellItemArray();
  5358. }
  5359. if (NULL != psiItemArray)
  5360. {
  5361. #ifdef DEBUG
  5362. // Sanity check.
  5363. DWORD dwNumItems;
  5364. ASSERT(S_OK == psiItemArray->GetCount(&dwNumItems));
  5365. ASSERT(1 == dwNumItems);
  5366. #endif
  5367. BOOL bHasShadowCopy = FALSE;
  5368. // This returns E_PENDING if the answer is unknown
  5369. // and fOkToBeSlow is FALSE
  5370. hr = HavePreviousVersionsAt(psiItemArray, 0, fOkToBeSlow, &bHasShadowCopy);
  5371. if (S_OK == hr && bHasShadowCopy)
  5372. {
  5373. *puisState = UIS_ENABLED;
  5374. }
  5375. }
  5376. return hr;
  5377. }
  5378. HRESULT CDefView::_DoVerb(IShellItemArray *psiItemArray, LPCSTR pszVerbA)
  5379. {
  5380. HRESULT hr = E_FAIL;
  5381. if (NULL== psiItemArray)
  5382. {
  5383. IContextMenu* pcm;
  5384. hr = GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARG(IContextMenu, &pcm));
  5385. if (SUCCEEDED(hr))
  5386. {
  5387. hr = _InvokeContextMenuVerb(pcm, pszVerbA, 0, CMIC_MASK_FLAG_LOG_USAGE);
  5388. pcm->Release();
  5389. }
  5390. }
  5391. else
  5392. {
  5393. ASSERT(psiItemArray == _pSelectionShellItemArray);
  5394. hr = _InvokeContextMenuVerbOnSelection(pszVerbA, 0, CMIC_MASK_FLAG_LOG_USAGE);
  5395. }
  5396. return hr;
  5397. }
  5398. HRESULT CDefView::_DoDropOnClsid(REFCLSID clsidDrop, IDataObject* pdo)
  5399. {
  5400. HRESULT hr = E_FAIL;
  5401. IDataObject *pdoFree = NULL;
  5402. if (!pdo)
  5403. {
  5404. IShellItemArray *pFolder = _GetFolderAsShellItemArray();
  5405. if (pFolder)
  5406. {
  5407. hr = pFolder->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdoFree));
  5408. if (SUCCEEDED(hr))
  5409. {
  5410. pdo = pdoFree;
  5411. }
  5412. else
  5413. {
  5414. pdoFree = NULL;
  5415. }
  5416. }
  5417. }
  5418. if (pdo)
  5419. {
  5420. hr = SHSimulateDropOnClsid(clsidDrop, SAFECAST(this, IOleCommandTarget *), pdo);
  5421. }
  5422. ATOMICRELEASE(pdoFree); // may be NULL
  5423. return hr;
  5424. }
  5425. HRESULT CDefView::_OnNewFolder(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5426. {
  5427. CDefView* pThis = (CDefView*)(void*)pv;
  5428. return pThis->_DoVerb(psiItemArray,c_szNewFolderA);
  5429. }
  5430. HRESULT CDefView::_OnRename(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5431. {
  5432. CDefView* pThis = (CDefView*)(void*)pv;
  5433. return pThis->DoRename();
  5434. }
  5435. HRESULT CDefView::_OnMove(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5436. {
  5437. CDefView* pThis = (CDefView*)(void*)pv;
  5438. return pThis->_DoMoveOrCopyTo(CLSID_MoveToMenu, psiItemArray);
  5439. }
  5440. HRESULT CDefView::_OnCopy(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5441. {
  5442. CDefView* pThis = (CDefView*)(void*)pv;
  5443. return pThis->_DoMoveOrCopyTo(CLSID_CopyToMenu, psiItemArray);
  5444. }
  5445. HRESULT CDefView::_OnPublish(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5446. {
  5447. HRESULT hr = S_OK;
  5448. IDataObject *pdo = NULL;
  5449. CDefView* pThis = (CDefView*)(void*)pv;
  5450. if (psiItemArray)
  5451. {
  5452. hr = psiItemArray->BindToHandler(NULL, BHID_DataObject, IID_PPV_ARG(IDataObject, &pdo));
  5453. }
  5454. if (SUCCEEDED(hr))
  5455. {
  5456. hr = pThis->_DoDropOnClsid(CLSID_PublishDropTarget, pdo);
  5457. }
  5458. ATOMICRELEASE(pdo); // may be NULL
  5459. return hr;
  5460. }
  5461. HRESULT CDefView::_OnShare(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5462. {
  5463. HRESULT hr = E_FAIL;
  5464. CDefView* pThis = (CDefView*)(void*)pv;
  5465. if (!psiItemArray)
  5466. {
  5467. psiItemArray = pThis->_GetFolderAsShellItemArray();
  5468. }
  5469. if (NULL != psiItemArray)
  5470. {
  5471. LPOLESTR pszPath;
  5472. hr = pThis->_GetFullPathNameAt(psiItemArray, 0, &pszPath);
  5473. if (SUCCEEDED(hr))
  5474. {
  5475. if (!g_hmodNTSHRUI)
  5476. {
  5477. g_hmodNTSHRUI = LoadLibrary(L"ntshrui.dll");
  5478. }
  5479. if (g_hmodNTSHRUI)
  5480. {
  5481. PFNSHOWSHAREFOLDERUIW pfnShowShareFolderUI = (PFNSHOWSHAREFOLDERUIW) GetProcAddress(g_hmodNTSHRUI, "ShowShareFolderUIW");
  5482. if (pfnShowShareFolderUI)
  5483. {
  5484. pfnShowShareFolderUI(pThis->_hwndMain, pszPath);
  5485. }
  5486. }
  5487. CoTaskMemFree(pszPath);
  5488. }
  5489. }
  5490. return hr;
  5491. }
  5492. HRESULT CDefView::_OnEmail(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5493. {
  5494. HRESULT hr = E_FAIL;
  5495. IDataObject *pdo = NULL;
  5496. if (psiItemArray)
  5497. {
  5498. hr = psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject,&pdo));
  5499. }
  5500. if (SUCCEEDED(hr))
  5501. {
  5502. CDefView* pThis = (CDefView*)(void*)pv;
  5503. BOOL bNoFilesFoundToEmail = TRUE;
  5504. INamespaceWalk *pnsw;
  5505. hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(INamespaceWalk, &pnsw));
  5506. if (SUCCEEDED(hr))
  5507. {
  5508. // Note:
  5509. // To mirror the behaviour of the selection context menu's "Send To->
  5510. // Mail Recipient", don't traverse links, mail the link file itself.
  5511. hr = pnsw->Walk(pdo, NSWF_DONT_TRAVERSE_LINKS, 0, NULL);
  5512. if (SUCCEEDED(hr))
  5513. {
  5514. UINT cItems;
  5515. LPITEMIDLIST *ppidls;
  5516. hr = pnsw->GetIDArrayResult(&cItems, &ppidls);
  5517. if (SUCCEEDED(hr))
  5518. {
  5519. if (cItems)
  5520. {
  5521. IDataObject* pdoWalk;
  5522. hr = SHCreateFileDataObject(&c_idlDesktop, cItems, (LPCITEMIDLIST *)ppidls, NULL, (IDataObject **)&pdoWalk);
  5523. if (SUCCEEDED(hr))
  5524. {
  5525. hr = pThis->_DoDropOnClsid(CLSID_MailRecipient, pdoWalk);
  5526. bNoFilesFoundToEmail = FALSE;
  5527. pdoWalk->Release();
  5528. }
  5529. }
  5530. FreeIDListArray(ppidls, cItems);
  5531. }
  5532. }
  5533. pnsw->Release();
  5534. }
  5535. if (bNoFilesFoundToEmail)
  5536. {
  5537. // No items found to e-mail (selected folders contained no files).
  5538. ShellMessageBox(
  5539. HINST_THISDLL,
  5540. pThis->_hwndMain,
  5541. MAKEINTRESOURCE(IDS_NOFILESTOEMAIL),
  5542. NULL,
  5543. MB_OK | MB_ICONERROR);
  5544. }
  5545. pdo->Release();
  5546. }
  5547. return hr;
  5548. }
  5549. HRESULT CDefView::_OnPrint(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5550. {
  5551. CDefView* pThis = (CDefView*)(void*)pv;
  5552. return pThis->_DoVerb(psiItemArray,c_szPrintA);
  5553. }
  5554. HRESULT CDefView::_OnDelete(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5555. {
  5556. CDefView* pThis = (CDefView*)(void*)pv;
  5557. return pThis->_DoVerb(psiItemArray,c_szDeleteA);
  5558. }
  5559. HRESULT CDefView::RemoveBarricade (void)
  5560. {
  5561. LPITEMIDLIST pidl = _GetViewPidl();
  5562. if (pidl)
  5563. {
  5564. TCHAR szValueName[MAX_PATH];
  5565. if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName)))
  5566. {
  5567. SetBarricadeStatus (szValueName, VARIANT_FALSE);
  5568. }
  5569. ILFree(pidl);
  5570. }
  5571. // Restore "View" menu commands which were stripped.
  5572. RecreateMenus();
  5573. // Enable "View Menu" button on the toolbar.
  5574. EnableToolbarButton(SFVIDM_VIEW_VIEWMENU, TRUE);
  5575. _fBarrierDisplayed = FALSE;
  5576. return _pDUIView->EnableBarrier(FALSE);
  5577. }
  5578. HRESULT CDefView::_OnView(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5579. {
  5580. CDefView* pThis = (CDefView*)(void*)pv;
  5581. return pThis->RemoveBarricade();
  5582. }
  5583. HRESULT CDefView::_OnHide(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5584. {
  5585. CDefView* pThis = (CDefView*)(void*)pv;
  5586. LPITEMIDLIST pidl = pThis->_GetViewPidl();
  5587. if (pidl)
  5588. {
  5589. TCHAR szValueName[MAX_PATH];
  5590. if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName)))
  5591. {
  5592. SetBarricadeStatus(szValueName, VARIANT_TRUE);
  5593. }
  5594. ILFree(pidl);
  5595. }
  5596. // Disable "View Menu" button on the toolbar.
  5597. pThis->EnableToolbarButton(SFVIDM_VIEW_VIEWMENU, FALSE);
  5598. pThis->_fBarrierDisplayed = TRUE;
  5599. return pThis->_pDUIView->EnableBarrier(TRUE);
  5600. }
  5601. HRESULT CDefView::_OnAddRemovePrograms(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5602. {
  5603. HCURSOR hcOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
  5604. SHRunControlPanel(L"appwiz.cpl", NULL);
  5605. SetCursor(hcOld);
  5606. return S_OK;
  5607. }
  5608. HRESULT CDefView::_OnSearchFiles(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5609. {
  5610. CDefView* pThis = (CDefView*)(void*)pv;
  5611. return IUnknown_ShowBrowserBar (pThis->_psb, CLSID_FileSearchBand, TRUE);
  5612. }
  5613. HRESULT CDefView::_OnPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5614. {
  5615. CDefView* pThis = (CDefView*)(void*)pv;
  5616. if (!psiItemArray)
  5617. {
  5618. psiItemArray = pThis->_GetFolderAsShellItemArray();
  5619. }
  5620. ShowPreviousVersionsAt(psiItemArray, 0, pThis->_hwndMain);
  5621. return S_OK;
  5622. }
  5623. const WVTASKITEM c_DefviewBlockadeTaskHeader = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE, IDS_HEADER_DEFVIEW_BLOCKADE_TT);
  5624. const WVTASKITEM c_DefviewBlockadeTasks[] =
  5625. {
  5626. WVTI_ENTRY_ALL(UICID_ViewContents, L"shell32.dll", IDS_TASK_DEFVIEW_VIEWCONTENTS_DRIVE, IDS_TASK_DEFVIEW_VIEWCONTENTS_DRIVE_TT, IDI_STSPROGS, CDefView::_CanViewDrives, CDefView::_OnView),
  5627. WVTI_ENTRY_ALL(UICID_HideContents, L"shell32.dll", IDS_TASK_DEFVIEW_HIDECONTENTS_DRIVE, IDS_TASK_DEFVIEW_HIDECONTENTS_DRIVE_TT, IDI_STSPROGS, CDefView::_CanHideDrives, CDefView::_OnHide),
  5628. WVTI_ENTRY_ALL(UICID_ViewContents, L"shell32.dll", IDS_TASK_DEFVIEW_VIEWCONTENTS_FOLDER, IDS_TASK_DEFVIEW_VIEWCONTENTS_FOLDER_TT,IDI_STSPROGS, CDefView::_CanViewFolder, CDefView::_OnView),
  5629. WVTI_ENTRY_ALL(UICID_HideContents, L"shell32.dll", IDS_TASK_DEFVIEW_HIDECONTENTS_FOLDER, IDS_TASK_DEFVIEW_HIDECONTENTS_FOLDER_TT,IDI_STSPROGS, CDefView::_CanHideFolder, CDefView::_OnHide),
  5630. WVTI_ENTRY_ALL(UICID_AddRemovePrograms, L"shell32.dll", IDS_TASK_ARP, IDS_TASK_ARP_TT, IDI_CPCAT_ARP, NULL, CDefView::_OnAddRemovePrograms),
  5631. WVTI_ENTRY_ALL(UICID_SearchFiles, L"shell32.dll", IDS_TASK_SEARCHFORFILES, IDS_TASK_SEARCHFORFILES_TT, IDI_STFIND, NULL, CDefView::_OnSearchFiles),
  5632. };
  5633. const WVTASKITEM c_DefviewFileFolderTasksHeaders = WVTI_HEADER(L"shell32.dll", IDS_HEADER_FILEFOLDER, IDS_HEADER_FILEFOLDER_TT);
  5634. const WVTASKITEM c_DefviewItemFolderTasksHeaders = WVTI_HEADER(L"shell32.dll", IDS_HEADER_ITEMFOLDER, IDS_HEADER_ITEMFOLDER_TT);
  5635. const WVTASKITEM c_DefviewFileFolderTasks[] =
  5636. {
  5637. WVTI_ENTRY_NOSELECTION(UICID_NewFolder, L"shell32.dll", IDS_TASK_CURFOLDER_NEWFOLDER, IDS_TASK_CURFOLDER_NEWFOLDER_TT, IDI_TASK_NEWFOLDER, CDefView::_CanWrite,
  5638. CDefView::_OnNewFolder),
  5639. WVTI_ENTRY_TITLE(UICID_Rename, L"shell32.dll", IDS_TASK_RENAME_FILE, IDS_TASK_RENAME_FOLDER, 0, IDS_TASK_RENAME_FILE_TT, IDI_TASK_RENAME, CDefView::_CanRename,
  5640. CDefView::_OnRename),
  5641. WVTI_ENTRY_TITLE(UICID_Move, L"shell32.dll", IDS_TASK_MOVE_FILE, IDS_TASK_MOVE_FOLDER, IDS_TASK_MOVE_ITEMS, IDS_TASK_MOVE_TT, IDI_TASK_MOVE, CDefView::_CanMove,
  5642. CDefView::_OnMove),
  5643. WVTI_ENTRY_ALL_TITLE(UICID_Copy, L"shell32.dll", 0, IDS_TASK_COPY_FILE, IDS_TASK_COPY_FOLDER, IDS_TASK_COPY_ITEMS, IDS_TASK_COPY_TT, IDI_TASK_COPY, CDefView::_CanCopy,
  5644. CDefView::_OnCopy),
  5645. WVTI_ENTRY_ALL_TITLE(UICID_Publish, L"shell32.dll", IDS_TASK_PUBLISH_FOLDER, IDS_TASK_PUBLISH_FILE,IDS_TASK_PUBLISH_FOLDER, IDS_TASK_PUBLISH_ITEMS, IDS_TASK_PUBLISH_TT, IDI_TASK_PUBLISH, CDefView::_CanPublish,
  5646. CDefView::_OnPublish),
  5647. WVTI_ENTRY_ALL_TITLE(UICID_Share, L"shell32.dll", IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_TT, IDI_TASK_SHARE, CDefView::_CanShare,
  5648. CDefView::_OnShare),
  5649. WVTI_ENTRY_TITLE(UICID_Email, L"shell32.dll", IDS_TASK_EMAIL_FILE, IDS_TASK_EMAIL_FOLDER, IDS_TASK_EMAIL_ITEMS, IDS_TASK_EMAIL_TT, IDI_TASK_EMAILFILE, CDefView::_CanEmail,
  5650. CDefView::_OnEmail),
  5651. WVTI_ENTRY_TITLE(UICID_Print, L"shell32.dll", IDS_TASK_PRINT_FILE, 0, 0, IDS_TASK_PRINT_TT, IDI_TASK_PRINT, CDefView::_CanPrint,
  5652. CDefView::_OnPrint),
  5653. WVTI_ENTRY_TITLE(UICID_Delete, L"shell32.dll", IDS_TASK_DELETE_FILE, IDS_TASK_DELETE_FOLDER, IDS_TASK_DELETE_ITEMS, IDS_TASK_DELETE_TT, IDI_TASK_DELETE, CDefView::_CanDelete,
  5654. CDefView::_OnDelete),
  5655. WVTI_ENTRY_ALL_TITLE(UICID_PreviousVersions, L"shell32.dll", IDS_TASK_SHADOW, IDS_TASK_SHADOW, IDS_TASK_SHADOW, 0, IDS_TASK_SHADOW_TT, IDI_TASK_SHADOW, CDefView::_HasPreviousVersions,
  5656. CDefView::_OnPreviousVersions),
  5657. };
  5658. const size_t c_cDefviewFileFolderTasks = ARRAYSIZE(c_DefviewFileFolderTasks);
  5659. const WVTASKITEM c_DefviewItemFolderTasks[] =
  5660. {
  5661. WVTI_ENTRY_NOSELECTION(UICID_NewFolder, L"shell32.dll", IDS_TASK_CURFOLDER_NEWFOLDER, IDS_TASK_CURFOLDER_NEWFOLDER_TT, IDI_TASK_NEWFOLDER, CDefView::_CanWrite,
  5662. CDefView::_OnNewFolder),
  5663. WVTI_ENTRY_TITLE(UICID_Rename, L"shell32.dll", IDS_TASK_RENAME_ITEM, IDS_TASK_RENAME_FOLDER, 0, IDS_TASK_RENAME_ITEM_TT, IDI_TASK_RENAME, CDefView::_CanRename,
  5664. CDefView::_OnRename),
  5665. WVTI_ENTRY_TITLE(UICID_Move, L"shell32.dll", IDS_TASK_MOVE_ITEM, IDS_TASK_MOVE_FOLDER, IDS_TASK_MOVE_ITEMS, IDS_TASK_MOVE_TT, IDI_TASK_MOVE, CDefView::_CanMove,
  5666. CDefView::_OnMove),
  5667. WVTI_ENTRY_ALL_TITLE(UICID_Copy, L"shell32.dll", 0, IDS_TASK_COPY_ITEM, IDS_TASK_COPY_FOLDER, IDS_TASK_COPY_ITEMS, IDS_TASK_COPY_TT, IDI_TASK_COPY, CDefView::_CanCopy,
  5668. CDefView::_OnCopy),
  5669. WVTI_ENTRY_ALL_TITLE(UICID_Share, L"shell32.dll", IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_FOLDER, 0, IDS_TASK_SHARE_TT, IDI_TASK_SHARE, CDefView::_CanShare,
  5670. CDefView::_OnShare),
  5671. WVTI_ENTRY_TITLE(UICID_Delete, L"shell32.dll", IDS_TASK_DELETE_ITEM, IDS_TASK_DELETE_FOLDER, IDS_TASK_DELETE_ITEMS, IDS_TASK_DELETE_TT, IDI_TASK_DELETE, CDefView::_CanDelete,
  5672. CDefView::_OnDelete),
  5673. };
  5674. const size_t c_cDefviewItemFolderTasks = ARRAYSIZE(c_DefviewItemFolderTasks);
  5675. const WVTASKITEM c_DefviewOtherPlaces = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES, IDS_HEADER_OTHER_PLACES_TT);
  5676. const WVTASKITEM c_DefviewDetails = WVTI_HEADER_ENTRY(L"shell32.dll", IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS, IDS_HEADER_DETAILS_TT);
  5677. const WVTASKITEM* CDefView::_FindTaskItem(REFGUID guidCanonicalName)
  5678. {
  5679. const BOOL bFileFolderTasks = _wvLayout.dwLayout & SFVMWVL_FILES;
  5680. const WVTASKITEM *paTasks = bFileFolderTasks ? c_DefviewFileFolderTasks : c_DefviewItemFolderTasks;
  5681. const size_t cTasks = bFileFolderTasks ? c_cDefviewFileFolderTasks : c_cDefviewItemFolderTasks;
  5682. for (size_t i = 0; i < cTasks; i++)
  5683. if (IsEqualGUID(*(paTasks[i].pguidCanonicalName), guidCanonicalName))
  5684. return &paTasks[i];
  5685. return NULL;
  5686. }
  5687. HRESULT CDefView::get_Name(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszName)
  5688. {
  5689. const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName);
  5690. if (pTask)
  5691. return CWVTASKITEM::get_Name(pTask, psiItemArray, ppszName);
  5692. return E_FAIL;
  5693. }
  5694. HRESULT CDefView::get_Icon(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszIcon)
  5695. {
  5696. const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName);
  5697. if (pTask)
  5698. return CWVTASKITEM::get_Icon(pTask, psiItemArray, ppszIcon);
  5699. return E_FAIL;
  5700. }
  5701. HRESULT CDefView::get_Tooltip(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, LPWSTR *ppszInfotip)
  5702. {
  5703. const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName);
  5704. if (pTask)
  5705. return CWVTASKITEM::get_Tooltip(pTask, psiItemArray, ppszInfotip);
  5706. return E_FAIL;
  5707. }
  5708. HRESULT CDefView::get_State(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, UISTATE* puisState)
  5709. {
  5710. const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName);
  5711. if (pTask)
  5712. return CWVTASKITEM::get_State(pTask, SAFECAST(this, IShellView2*), psiItemArray, TRUE, puisState);
  5713. return E_FAIL;
  5714. }
  5715. HRESULT CDefView::Invoke(REFGUID guidCanonicalName, IShellItemArray *psiItemArray, IBindCtx *pbc)
  5716. {
  5717. const WVTASKITEM* pTask = _FindTaskItem(guidCanonicalName);
  5718. if (pTask)
  5719. return CWVTASKITEM::Invoke(pTask, SAFECAST(this, IShellView2*), psiItemArray, pbc);
  5720. return E_FAIL;
  5721. }
  5722. HRESULT CDefView::_GetDefaultWebviewContent(BOOL bFileFolderTasks)
  5723. {
  5724. if (!_wvTasks.penumSpecialTasks)
  5725. {
  5726. if (_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5727. {
  5728. // defview provides a default penumSpecialTasks for barricaded folders
  5729. Create_IUIElement(&c_DefviewBlockadeTaskHeader, &(_wvContent.pSpecialTaskHeader));
  5730. Create_IEnumUICommand((IUnknown*)(void*)this, c_DefviewBlockadeTasks, ARRAYSIZE(c_DefviewBlockadeTasks), &(_wvTasks.penumSpecialTasks));
  5731. }
  5732. }
  5733. if (!_wvTasks.penumFolderTasks)
  5734. {
  5735. if (_wvContent.pFolderTaskHeader)
  5736. _wvContent.pFolderTaskHeader->Release();
  5737. Create_IUIElement(bFileFolderTasks ? &c_DefviewFileFolderTasksHeaders : &c_DefviewItemFolderTasksHeaders, &(_wvContent.pFolderTaskHeader));
  5738. Create_IEnumUICommand(
  5739. (IUnknown*)(void*)this,
  5740. bFileFolderTasks ? c_DefviewFileFolderTasks : c_DefviewItemFolderTasks,
  5741. bFileFolderTasks ? c_cDefviewFileFolderTasks : c_cDefviewItemFolderTasks,
  5742. &(_wvTasks.penumFolderTasks));
  5743. }
  5744. if (!_wvContent.penumOtherPlaces)
  5745. {
  5746. LPCTSTR rgCSIDLs[] = { MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_NETWORK) };
  5747. LPITEMIDLIST pidl = _GetViewPidl();
  5748. CreateIEnumIDListOnCSIDLs(pidl, rgCSIDLs, ARRAYSIZE(rgCSIDLs), &_wvContent.penumOtherPlaces);
  5749. if (pidl)
  5750. ILFree(pidl);
  5751. }
  5752. ASSERT(NULL==_pOtherPlacesHeader);
  5753. Create_IUIElement(&c_DefviewOtherPlaces, &_pOtherPlacesHeader);
  5754. ASSERT(NULL==_pDetailsHeader);
  5755. Create_IUIElement(&c_DefviewDetails, &_pDetailsHeader);
  5756. return S_OK;
  5757. }
  5758. void CDefView::_FreeWebViewContentData()
  5759. {
  5760. ATOMICRELEASE(_wvContent.pSpecialTaskHeader);
  5761. ATOMICRELEASE(_wvContent.pFolderTaskHeader);
  5762. ATOMICRELEASE(_wvContent.penumOtherPlaces);
  5763. ATOMICRELEASE(_wvTasks.penumSpecialTasks);
  5764. ATOMICRELEASE(_wvTasks.penumFolderTasks);
  5765. ATOMICRELEASE(_pOtherPlacesHeader);
  5766. ATOMICRELEASE(_pDetailsHeader);
  5767. _fQueryWebViewData = FALSE;
  5768. _wvLayout.dwLayout = -1; // an invalid value
  5769. }
  5770. BOOL CDefView::_QueryBarricadeState()
  5771. {
  5772. BOOL bResult = FALSE;
  5773. LPITEMIDLIST pidl = _GetViewPidl();
  5774. if (pidl)
  5775. {
  5776. //
  5777. // Control panel is a special case.
  5778. // The barricade is used to represent 'category view' which can
  5779. // be turned on/off by the user. We must always ask control panel
  5780. // if it's barricade is on or off.
  5781. //
  5782. BOOL bIsControlPanel = FALSE;
  5783. LPITEMIDLIST pidlControlPanel;
  5784. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidlControlPanel)))
  5785. {
  5786. bIsControlPanel = ILIsEqual(pidl, pidlControlPanel);
  5787. ILFree (pidlControlPanel);
  5788. }
  5789. if (bIsControlPanel)
  5790. {
  5791. SFVM_WEBVIEW_CONTENT_DATA wvc;
  5792. if (SUCCEEDED(CallCB(SFVM_GETWEBVIEWCONTENT, 0, (LPARAM)&wvc)))
  5793. {
  5794. //
  5795. // Control Panel doesn't provide all the standard
  5796. // webview content so it's doing nothing more than setting
  5797. // the dwFlags member. Assert to ensure this doesn't
  5798. // change in the future without us knowing about it.
  5799. //
  5800. ASSERT(NULL == wvc.pIntroText);
  5801. ASSERT(NULL == wvc.pSpecialTaskHeader);
  5802. ASSERT(NULL == wvc.pFolderTaskHeader);
  5803. ASSERT(NULL == wvc.penumOtherPlaces);
  5804. bResult = (0 != (SFVMWVF_BARRICADE & wvc.dwFlags));
  5805. }
  5806. }
  5807. else if (_wvContent.dwFlags & SFVMWVF_BARRICADE)
  5808. {
  5809. if (!IsBarricadeGloballyOff())
  5810. {
  5811. TCHAR szValueName[MAX_PATH];
  5812. if (GetBarricadeValueNameFromPidl(pidl, szValueName, ARRAYSIZE(szValueName)))
  5813. {
  5814. if (VARIANT_TRUE == GetBarricadeStatus(szValueName))
  5815. {
  5816. bResult = TRUE;
  5817. }
  5818. }
  5819. }
  5820. }
  5821. ILFree(pidl);
  5822. }
  5823. return bResult;
  5824. }
  5825. void CDefView::_ShowLegacyWatermark()
  5826. {
  5827. BOOL fShowLegacyWatermark = TRUE;
  5828. LVBKIMAGE lvbki = {0};
  5829. if (_pszLegacyWatermark)
  5830. {
  5831. lvbki.ulFlags = LVBKIF_SOURCE_URL | LVBKIF_STYLE_TILE;
  5832. lvbki.pszImage = _pszLegacyWatermark;
  5833. }
  5834. else
  5835. {
  5836. // this code path is used to clear the watermark
  5837. lvbki.ulFlags = LVBKIF_TYPE_WATERMARK;
  5838. // if we're turning off the legacy watermark, we may have to turn on the theme one
  5839. if (_idThemeWatermark && _pDUIView)
  5840. {
  5841. fShowLegacyWatermark = FALSE;
  5842. }
  5843. }
  5844. if (fShowLegacyWatermark)
  5845. ListView_SetBkImage(_hwndListview, &lvbki);
  5846. else
  5847. _ShowThemeWatermark();
  5848. }
  5849. void CDefView::_ShowThemeWatermark()
  5850. {
  5851. BOOL fShowLegacyWatermark = TRUE;
  5852. if (_idThemeWatermark && _pDUIView)
  5853. {
  5854. HINSTANCE hinstTheme = _pDUIView->_GetThemeHinst();
  5855. LVBKIMAGE lvbki = {0};
  5856. lvbki.ulFlags = LVBKIF_TYPE_WATERMARK;
  5857. lvbki.hbm = DUILoadBitmap(hinstTheme, _idThemeWatermark, LR_DEFAULTCOLOR);
  5858. if (lvbki.hbm)
  5859. {
  5860. // If the window color doesn't match the background color of the watermark,
  5861. // then we'll hide the watermark.
  5862. HDC hDC = CreateCompatibleDC(NULL);
  5863. if (hDC)
  5864. {
  5865. HBITMAP hOldBitmap;
  5866. hOldBitmap = (HBITMAP)SelectObject (hDC, lvbki.hbm);
  5867. if (GetPixel(hDC, 0, 0) != GetSysColor(COLOR_WINDOW))
  5868. {
  5869. _idThemeWatermark = 0;
  5870. }
  5871. SelectObject (hDC, hOldBitmap);
  5872. DeleteDC (hDC);
  5873. }
  5874. if (_idThemeWatermark && ListView_SetBkImage(_hwndListview, &lvbki))
  5875. {
  5876. fShowLegacyWatermark = FALSE;
  5877. }
  5878. else
  5879. {
  5880. DeleteObject(lvbki.hbm);
  5881. }
  5882. }
  5883. if (fShowLegacyWatermark)
  5884. _idThemeWatermark = 0; // something failed, pretend we don't have one
  5885. }
  5886. // usually this will just hide the previous watermark
  5887. if (fShowLegacyWatermark)
  5888. {
  5889. _ShowLegacyWatermark();
  5890. }
  5891. }
  5892. void CDefView::_SetThemeWatermark()
  5893. {
  5894. UINT idThemeWatermark = 0;
  5895. if (_pDUIView)
  5896. {
  5897. const WVTHEME* pwvTheme = _pDUIView->GetThemeInfo();
  5898. if (pwvTheme && pwvTheme->idListviewWatermark)
  5899. {
  5900. HINSTANCE hinstTheme = _pDUIView->_GetThemeHinst();
  5901. if (HINST_THISDLL != hinstTheme)
  5902. {
  5903. // Only add the watermark if the machine is fast enough...
  5904. if (SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewWatermark"),
  5905. FALSE, // Don't ignore HKCU
  5906. FALSE)) // Assume not fast enough
  5907. {
  5908. idThemeWatermark = pwvTheme->idListviewWatermark;
  5909. }
  5910. }
  5911. }
  5912. }
  5913. if (idThemeWatermark != _idThemeWatermark)
  5914. {
  5915. _idThemeWatermark = idThemeWatermark;
  5916. // Since DUI Document view isn't themed, legacy watermarks have precedence there.
  5917. // Might as well have them take precedence for My Pictures too...
  5918. if (!_pszLegacyWatermark)
  5919. {
  5920. _ShowThemeWatermark();
  5921. }
  5922. }
  5923. }
  5924. void CDefView::_SetLegacyWatermark(LPCTSTR pszLegacyWatermark)
  5925. {
  5926. Str_SetPtr(&_pszLegacyWatermark, pszLegacyWatermark);
  5927. _ShowLegacyWatermark();
  5928. }
  5929. HRESULT CDefView::_TryShowWebView(UINT fvmNew, UINT fvmOld)
  5930. {
  5931. SFVM_WEBVIEW_LAYOUT_DATA sfvmwvld = {0};
  5932. HRESULT hr = E_FAIL;
  5933. BOOL fShowDUI = FALSE;
  5934. // The desktop IShellFolder doesn't know if it's in-frame or in the real desktop,
  5935. // so only ask it for new DUIView if we're not the actual desktop.
  5936. if (!_IsDesktop())
  5937. {
  5938. // Supporting SFVM_GETWEBVIEWLAYOUT means the folder wants our new
  5939. // DUI View and they support SFVM_GETWEBVIEWCONTENT
  5940. //
  5941. hr = CallCB(SFVM_GETWEBVIEWLAYOUT, (WPARAM)fvmNew, (LPARAM)&sfvmwvld);
  5942. fShowDUI = SUCCEEDED(hr);
  5943. }
  5944. // This folder doesn't specify the new DUIView, try the old WebView stuff
  5945. if (!fShowDUI)
  5946. {
  5947. WCHAR wszMoniker[MAX_PATH];
  5948. hr = _GetWebViewMoniker(wszMoniker, ARRAYSIZE(wszMoniker));
  5949. if (SUCCEEDED(hr))
  5950. {
  5951. if(_pDUIView) //Hide it only if we are switching from DUI
  5952. _TryHideWebView(); // just in case we're switching from DUI to Web View (can happen when customizing)
  5953. if (wszMoniker[0])
  5954. {
  5955. hr = _SwitchToWebView(TRUE);
  5956. }
  5957. }
  5958. // Okay, we don't have Web View, use the default DUI View
  5959. if (FAILED(hr))
  5960. {
  5961. sfvmwvld.dwLayout = SFVMWVL_NORMAL;
  5962. fShowDUI = TRUE;
  5963. }
  5964. }
  5965. if (fShowDUI)
  5966. {
  5967. hr = S_OK;
  5968. _cFrame.HideWebView(); // just in case we're switching from Web View to DUI View (can happen when customizing)
  5969. if (sfvmwvld.dwLayout != _wvLayout.dwLayout)
  5970. {
  5971. if (!_fQueryWebViewData) // instead of this we could allow per-layout tasks...
  5972. {
  5973. CallCB(SFVM_GETWEBVIEWTHEME, 0, (LPARAM)&_wvTheme);
  5974. // _FreeWebViewContentData(); if we have per-layout tasks...
  5975. if (FAILED(CallCB(SFVM_GETWEBVIEWCONTENT, 0, (LPARAM)&_wvContent)))
  5976. {
  5977. ZeroMemory(&_wvContent, sizeof(_wvContent));
  5978. }
  5979. if (0 == (SFVMWVF_ENUMTASKS & _wvContent.dwFlags))
  5980. {
  5981. //
  5982. // View wants standard task sections.
  5983. // Non-standard task sections are enumerated in duiview.
  5984. //
  5985. if (FAILED(CallCB(SFVM_GETWEBVIEWTASKS, 0, (LPARAM)&_wvTasks)))
  5986. {
  5987. ZeroMemory(&_wvTasks, sizeof(_wvTasks));
  5988. }
  5989. _GetDefaultWebviewContent(sfvmwvld.dwLayout & SFVMWVL_FILES);
  5990. }
  5991. _fQueryWebViewData = TRUE;
  5992. }
  5993. CopyMemory(&_wvLayout, &sfvmwvld, sizeof(_wvLayout));
  5994. _wvLayout.punkPreview = NULL;
  5995. if (_pDUIView)
  5996. {
  5997. _pDUIView->EnablePreview(sfvmwvld.punkPreview);
  5998. }
  5999. else
  6000. {
  6001. _pDUIView = Create_CDUIView(this);
  6002. if (_pDUIView)
  6003. {
  6004. _fBarrierDisplayed = _QueryBarricadeState();
  6005. if (SUCCEEDED(_pDUIView->Initialize(_fBarrierDisplayed,
  6006. sfvmwvld.punkPreview)))
  6007. {
  6008. if (((SFVMWVF_ENUMTASKS | SFVMWVF_CONTENTSCHANGE) & _wvContent.dwFlags) &&
  6009. _fRcvdContentsChangeBeforeDuiViewCreated)
  6010. {
  6011. //
  6012. // If the webview provider dynamically enumerates
  6013. // tasks or wants to be refreshed when contents change,
  6014. // (i.e. Control Panel), AND we received a 'contents change'
  6015. // before DUI View was created, initiate a 'contents change' now.
  6016. // Otherwise, such providers will not receive a 'contents change'
  6017. // and thus will not display their dynamic webview content.
  6018. //
  6019. _OnContentsChanged();
  6020. }
  6021. }
  6022. else
  6023. {
  6024. _pDUIView->Release();
  6025. _pDUIView = NULL;
  6026. }
  6027. }
  6028. }
  6029. }
  6030. else
  6031. {
  6032. // except potentially refresh if we need to add/remove our DUI Details minipreview
  6033. if (_pDUIView && (_IsImageMode(fvmNew) != _IsImageMode(fvmOld)))
  6034. _pDUIView->OnSelectionChange(_pSelectionShellItemArray);
  6035. }
  6036. ATOMICRELEASE(sfvmwvld.punkPreview);
  6037. }
  6038. _SetThemeWatermark();
  6039. return hr;
  6040. }
  6041. HRESULT CDefView::_TryHideWebView()
  6042. {
  6043. if (_pDUIView)
  6044. {
  6045. _pDUIView->DetachListview(); // so we detach and re-parent the listview synchronously
  6046. //
  6047. // Ensure DUser has shut down and handled all DUser messages
  6048. // before we release our ref on CDUIView.
  6049. //
  6050. _pDUIView->UnInitializeDirectUI();
  6051. _pDUIView->Release();
  6052. _pDUIView = NULL; // * necessary * because this is used internally as a state (must be BEFORE WndSize() below)
  6053. _wvLayout.dwLayout = -1; // an invalid value
  6054. _fListViewShown = FALSE; // CDUIView::DetachListview() does a SW_HIDE on the listview
  6055. WndSize(_hwndView); // resize _hwndView to account for DUI disappearing (otherwise
  6056. // it will still be the smaller size expecting DUI to be drawn
  6057. // next to it)
  6058. ShowHideListView();
  6059. }
  6060. else
  6061. {
  6062. _SwitchToWebView(FALSE);
  6063. }
  6064. return S_OK;
  6065. }
  6066. // we are switching the listview view mode in this function, not dorking with web view content.
  6067. //
  6068. HRESULT CDefView::_SwitchToViewFVM(UINT fvmNew, UINT uiType)
  6069. {
  6070. HRESULT hr = S_OK;
  6071. UINT fvmOld = _fs.ViewMode;
  6072. ASSERT(_hwndListview);
  6073. HWND hwndCurrentFocus = GetFocus();
  6074. BOOL bSetFocusRequired = HasCurrentViewWindowFocus();
  6075. if (SWITCHTOVIEW_WEBVIEWONLY != uiType)
  6076. {
  6077. // if we haven't loaded the columns yet, do that now
  6078. // Don't pre-load the columns for TileView, we are delaying the load on purpose for perf reasons.
  6079. if (fvmNew == FVM_DETAILS)
  6080. {
  6081. AddColumns();
  6082. _SetSortFeedback();
  6083. }
  6084. else if (fvmNew == FVM_THUMBSTRIP)
  6085. {
  6086. // Thumbstrip makes no sense in non-webview, fall back to thumbnail
  6087. if (!_ShouldShowWebView())
  6088. {
  6089. fvmNew = FVM_THUMBNAIL;
  6090. }
  6091. }
  6092. // Combined view only applies to large icon view
  6093. if (_fCombinedView && fvmNew != FVM_ICON)
  6094. {
  6095. _fCombinedView = FALSE;
  6096. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_REGIONAL, 0);
  6097. _SetFolderColors();
  6098. }
  6099. // First we turn OFF view specific stuff that is no longer needed
  6100. switch (fvmOld)
  6101. {
  6102. case FVM_THUMBSTRIP:
  6103. if (FVM_THUMBSTRIP != fvmNew)
  6104. {
  6105. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SINGLEROW, 0);
  6106. // we may have forced thumbstrip to auto-arrange, undo that if so
  6107. if (!(_fs.fFlags & FWF_AUTOARRANGE))
  6108. {
  6109. SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, 0);
  6110. }
  6111. }
  6112. // fall through
  6113. case FVM_THUMBNAIL:
  6114. if (!_IsImageMode(fvmNew))
  6115. {
  6116. _ResetThumbview();
  6117. // Since we are switching from thumbnail view, remove any thumbnail extraction tasks
  6118. _RemoveThumbviewTasks();
  6119. if (_fs.fFlags & FWF_OWNERDATA)
  6120. {
  6121. InvalidateRect(_hwndListview, NULL, TRUE);
  6122. }
  6123. else
  6124. {
  6125. ListView_InvalidateImageIndexes(_hwndListview);
  6126. }
  6127. }
  6128. break;
  6129. case FVM_TILE:
  6130. if (!_IsTileMode(fvmNew))
  6131. {
  6132. if (_pScheduler)
  6133. _pScheduler->RemoveTasks(TOID_DVFileTypeProperties, ITSAT_DEFAULT_LPARAM, TRUE);
  6134. // Remove the columns that
  6135. // were pulled in because of tileview.
  6136. _RemoveTileColumns();
  6137. }
  6138. break;
  6139. }
  6140. _SetView(fvmNew); // we can now switch the listview around
  6141. // Now that'we no longer in tileview, we can reset the tileinfo. If we were to do it
  6142. // prior to changing the view, then listview would start asking us for the tileinformation
  6143. // for each item again, and we'd pull in the tile columns again.
  6144. if (fvmOld == FVM_TILE)
  6145. {
  6146. _RemoveTileInfo();
  6147. }
  6148. // Third, turn ON view specific stuff
  6149. //
  6150. switch (fvmNew)
  6151. {
  6152. case FVM_THUMBSTRIP:
  6153. if (FVM_THUMBSTRIP!=fvmOld)
  6154. {
  6155. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_SINGLEROW, LVS_EX_SINGLEROW);
  6156. // thumbstrip can not be in group view
  6157. if (_fGroupView)
  6158. _ToggleGrouping();
  6159. // thumbstrip is always in auto-arrange
  6160. if (!(_fs.fFlags & FWF_AUTOARRANGE))
  6161. {
  6162. _ClearItemPositions();
  6163. SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, LVS_AUTOARRANGE);
  6164. }
  6165. }
  6166. // fall through
  6167. case FVM_THUMBNAIL:
  6168. if (!_IsImageMode(fvmOld))
  6169. {
  6170. if (GetKeyState(VK_SHIFT) < 0)
  6171. {
  6172. _fs.fFlags ^= FWF_HIDEFILENAMES; // toggle
  6173. }
  6174. _SetThumbview();
  6175. _DoThumbnailReadAhead();
  6176. RECT rc = {1, 3, 4, 4};
  6177. ListView_SetViewMargins(_hwndListview, &rc);
  6178. }
  6179. break;
  6180. case FVM_TILE:
  6181. if (!_IsTileMode(fvmOld))
  6182. {
  6183. _SetTileview();
  6184. RECT rc = {3, 4, 4, 1};
  6185. ListView_SetViewMargins(_hwndListview, &rc);
  6186. }
  6187. break;
  6188. default:
  6189. _SetSysImageList();
  6190. {
  6191. RECT rc = {1, 3, 4, 0};
  6192. ListView_SetViewMargins(_hwndListview, &rc);
  6193. }
  6194. break;
  6195. }
  6196. }
  6197. if (SWITCHTOVIEW_NOWEBVIEW != uiType)
  6198. {
  6199. // New to Whistler: a view mode transition may also entail a web view template change
  6200. if (_ShouldShowWebView())
  6201. {
  6202. _TryShowWebView(fvmNew, fvmOld);
  6203. _AutoAutoArrange(0);
  6204. hr = S_OK; // we don't care about failure since we still get icons
  6205. }
  6206. else
  6207. {
  6208. _TryHideWebView();
  6209. }
  6210. }
  6211. if (SWITCHTOVIEW_WEBVIEWONLY != uiType)
  6212. {
  6213. ShowHideListView();
  6214. _AutoAutoArrange(0);
  6215. if (bSetFocusRequired)
  6216. {
  6217. // _hwndListview is the current view window. Let's set focus to it.
  6218. CallCB(SFVM_SETFOCUS, 0, 0);
  6219. ViewWindowSetFocus();
  6220. // notify image preview control to update its image
  6221. if (fvmNew == FVM_THUMBSTRIP)
  6222. _ThumbstripSendImagePreviewFocusChangeEvent();
  6223. }
  6224. else
  6225. {
  6226. SetFocus(hwndCurrentFocus);
  6227. }
  6228. CheckToolbar();
  6229. // update menus, i.e. add Choose Columns to the view menu if Details view is selected
  6230. // or remove it otherwise
  6231. RecreateMenus();
  6232. _EnableDisableTBButtons();
  6233. }
  6234. return hr;
  6235. }
  6236. // Description:
  6237. // Notify image preview control to update its image. The image preview
  6238. // control uses focus change events to track when it should update the image
  6239. // it is displaying. When it receives a focus change event, it queries the
  6240. // listview to see which item has focus, then displays that item in the
  6241. // image preview window. When nothing in the listview has focus (such as
  6242. // when it has no items), the image preview window displays as empty.
  6243. //
  6244. // This method fires the "focus changed" event which is picked up by the
  6245. // image preview control, and causes it to update the image it's displaying.
  6246. //
  6247. void CDefView::_ThumbstripSendImagePreviewFocusChangeEvent()
  6248. {
  6249. ASSERT(_fs.ViewMode == FVM_THUMBSTRIP);
  6250. _FireEvent(DISPID_FOCUSCHANGED);
  6251. }
  6252. int CDefView::CheckCurrentViewMenuItem(HMENU hmenu)
  6253. {
  6254. int iCurViewMenuItem = _GetMenuIDFromViewMode(_fs.ViewMode);
  6255. CheckMenuRadioItem(hmenu, SFVIDM_VIEW_FIRSTVIEW, SFVIDM_VIEW_LASTVIEW,
  6256. iCurViewMenuItem, MF_BYCOMMAND | MF_CHECKED);
  6257. return iCurViewMenuItem;
  6258. }
  6259. const UINT c_aiNonCustomizableFolders[] = {
  6260. CSIDL_WINDOWS,
  6261. CSIDL_SYSTEM,
  6262. CSIDL_SYSTEMX86,
  6263. CSIDL_PROGRAM_FILES,
  6264. CSIDL_PROGRAM_FILESX86,
  6265. CSIDL_PERSONAL,
  6266. CSIDL_MYDOCUMENTS,
  6267. CSIDL_MYMUSIC,
  6268. CSIDL_MYPICTURES,
  6269. CSIDL_MYVIDEO,
  6270. CSIDL_COMMON_DOCUMENTS,
  6271. CSIDL_COMMON_MUSIC,
  6272. CSIDL_COMMON_PICTURES,
  6273. CSIDL_COMMON_VIDEO
  6274. };
  6275. // since we moved to the property bag this check is fast; we don't probe to see if we can create desktop.ini
  6276. // or anything.
  6277. BOOL IsCustomizable(LPCITEMIDLIST pidlFolder)
  6278. {
  6279. BOOL fCustomizable = FALSE;
  6280. if (!SHRestricted(REST_NOCUSTOMIZETHISFOLDER) && !SHRestricted(REST_CLASSICSHELL))
  6281. {
  6282. // Check if this is a file system folder.
  6283. // customization requires the folder being a regular file system
  6284. // folder. FILESYSTEMANCESTOR is the key bit here
  6285. #define SFGAO_CUST_BITS (SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR)
  6286. ULONG rgfFolderAttr = SFGAO_CUST_BITS;
  6287. TCHAR szPath[MAX_PATH];
  6288. if (SUCCEEDED(SHGetNameAndFlags(pidlFolder, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), &rgfFolderAttr)) &&
  6289. (SFGAO_CUST_BITS == (rgfFolderAttr & SFGAO_CUST_BITS)))
  6290. {
  6291. if (!PathIsOneOf(szPath, c_aiNonCustomizableFolders, ARRAYSIZE(c_aiNonCustomizableFolders)) &&
  6292. (!PathIsRoot(szPath) || PathIsUNCServerShare(szPath)) &&
  6293. !SHRestricted(REST_NOCUSTOMIZEWEBVIEW))
  6294. {
  6295. IPropertyBag *ppb;
  6296. if (SUCCEEDED(SHGetViewStatePropertyBag(pidlFolder, VS_BAGSTR_EXPLORER, SHGVSPB_PERUSER | SHGVSPB_PERFOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
  6297. {
  6298. fCustomizable = TRUE;
  6299. ppb->Release();
  6300. }
  6301. }
  6302. }
  6303. }
  6304. return fCustomizable;
  6305. }
  6306. // wrapper around IsCustomizable to save some state, plus some defview-specific logic.
  6307. BOOL CDefView::_CachedIsCustomizable()
  6308. {
  6309. if (_IsDesktop() || _IsViewDesktop() || _IsCommonDialog())
  6310. {
  6311. _iCustomizable = NOT_CUSTOMIZABLE;
  6312. }
  6313. if (_iCustomizable == DONTKNOW_IF_CUSTOMIZABLE)
  6314. {
  6315. LPITEMIDLIST pidl = _GetViewPidl();
  6316. if (pidl)
  6317. {
  6318. _iCustomizable = IsCustomizable(pidl) ? YES_CUSTOMIZABLE : NOT_CUSTOMIZABLE;
  6319. ILFree(pidl);
  6320. }
  6321. }
  6322. return (_iCustomizable != NOT_CUSTOMIZABLE);
  6323. }
  6324. BOOL CDefView::_InvokeCustomization()
  6325. {
  6326. BOOL fRet = FALSE;
  6327. if (!_CachedIsCustomizable())
  6328. {
  6329. //If not customizable, put up this error message!
  6330. ShellMessageBox(HINST_THISDLL, _hwndMain, MAKEINTRESOURCE(IDS_NOTCUSTOMIZABLE), NULL,
  6331. MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
  6332. return FALSE; // ...and bail out!
  6333. }
  6334. //Save the view state first.
  6335. SaveViewState();
  6336. LPITEMIDLIST pidl = _GetViewPidl();
  6337. if (pidl)
  6338. {
  6339. TCHAR szSheetName[25];
  6340. LoadString(HINST_THISDLL, IDS_CUSTOMIZE, szSheetName, ARRAYSIZE(szSheetName));
  6341. SHELLEXECUTEINFO sei =
  6342. {
  6343. SIZEOF(sei),
  6344. SEE_MASK_INVOKEIDLIST, // fMask
  6345. _hwndMain, // hwnd
  6346. c_szProperties, // lpVerb
  6347. NULL, // lpFile
  6348. szSheetName, // lpParameters
  6349. NULL, // lpDirectory
  6350. SW_SHOWNORMAL, // nShow
  6351. NULL, // hInstApp
  6352. pidl, // lpIDList
  6353. NULL, // lpClass
  6354. 0, // hkeyClass
  6355. 0, // dwHotKey
  6356. NULL // hIcon
  6357. };
  6358. // only invoking properties verb
  6359. fRet = ShellExecuteEx(&sei);
  6360. ILFree(pidl);
  6361. }
  6362. return fRet;
  6363. }
  6364. struct {
  6365. UINT uiSfvidm;
  6366. DWORD dwOlecmdid;
  6367. } const c_CmdTable[] = {
  6368. { SFVIDM_EDIT_CUT, OLECMDID_CUT },
  6369. { SFVIDM_EDIT_COPY, OLECMDID_COPY },
  6370. { SFVIDM_EDIT_PASTE, OLECMDID_PASTE },
  6371. { SFVIDM_FILE_DELETE, OLECMDID_DELETE },
  6372. { SFVIDM_FILE_PROPERTIES, OLECMDID_PROPERTIES },
  6373. };
  6374. DWORD OlecmdidFromSfvidm(UINT uiSfvidm)
  6375. {
  6376. DWORD dwOlecmdid = 0;
  6377. for (int i = 0; i < ARRAYSIZE(c_CmdTable); i++)
  6378. {
  6379. if (c_CmdTable[i].uiSfvidm == uiSfvidm)
  6380. {
  6381. dwOlecmdid = c_CmdTable[i].dwOlecmdid;
  6382. break;
  6383. }
  6384. }
  6385. return dwOlecmdid;
  6386. }
  6387. void HideIE4DesktopChannelBar()
  6388. {
  6389. HWND hwndChannelBar;
  6390. //Check if the channel bar is currently running. If so, turn it off!
  6391. if ((hwndChannelBar = FindWindowEx(GetShellWindow(), NULL, TEXT("BaseBar"), TEXT("ChanApp"))) ||
  6392. (hwndChannelBar = FindWindowEx(NULL, NULL, TEXT("BaseBar"), TEXT("ChanApp")))) // can be a toplevel window
  6393. {
  6394. //Close the channel bar.
  6395. PostMessage(hwndChannelBar, WM_CLOSE, 0, 0);
  6396. }
  6397. }
  6398. // Wrapper around _SwitchToWebView to do desktop-specific stuff
  6399. LRESULT CDefView::_SwitchDesktopHTML(BOOL fShow)
  6400. {
  6401. LRESULT lRes;
  6402. if (fShow)
  6403. {
  6404. // Do this early to give the desktop a chance to regenerate it's webview template
  6405. _CallRefresh(TRUE);
  6406. lRes = SUCCEEDED(_SwitchToWebView(TRUE));
  6407. if (lRes)
  6408. {
  6409. HideIE4DesktopChannelBar();
  6410. }
  6411. }
  6412. else
  6413. {
  6414. _SwitchToWebView(FALSE);
  6415. CoFreeUnusedLibraries();
  6416. lRes = TRUE;
  6417. }
  6418. return lRes;
  6419. }
  6420. void CDefView::_DoColumnsMenu(int x, int y) // X and Y are screen coordinates
  6421. {
  6422. HMENU hmenu = CreatePopupMenu();
  6423. if (hmenu)
  6424. {
  6425. AddColumnsToMenu(hmenu, SFVIDM_COLUMN_FIRST);
  6426. int item = TrackPopupMenu(hmenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD,
  6427. x, y, 0, _hwndListview, NULL);
  6428. DestroyMenu(hmenu);
  6429. // validate item first
  6430. if (item == SFVIDM_VIEW_COLSETTINGS)
  6431. {
  6432. CColumnDlg ccd(this);
  6433. AddColumns();
  6434. ccd.ShowDialog(_hwndMain);
  6435. }
  6436. else if (item > SFVIDM_COLUMN_FIRST)
  6437. {
  6438. _HandleColumnToggle(item - SFVIDM_COLUMN_FIRST, TRUE);
  6439. }
  6440. }
  6441. }
  6442. BOOL CDefView::_ArrangeBy(UINT idCmd)
  6443. {
  6444. int iColumn = idCmd - SFVIDM_GROUPSFIRST;
  6445. BOOL fAllowToggle = TRUE;
  6446. // We want to enter group by if We already have a group, or if this is an extended grouping
  6447. if ((_fGroupView || InRange(idCmd, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST)) &&
  6448. !(_fs.ViewMode == FVM_LIST))
  6449. {
  6450. _GroupBy(idCmd);
  6451. iColumn = 0; // Arrange by name, when grouping
  6452. fAllowToggle = FALSE; // Always arrange in ascending order
  6453. }
  6454. return S_OK == _OnRearrange(iColumn, fAllowToggle);
  6455. }
  6456. BOOL CDefView::_InitArrangeMenu(HMENU hmInit)
  6457. {
  6458. MENUITEMINFO mii = {0};
  6459. mii.cbSize = sizeof(mii);
  6460. mii.fMask = MIIM_SUBMENU;
  6461. GetMenuItemInfo(hmInit, SFVIDM_MENU_ARRANGE, MF_BYCOMMAND, &mii);
  6462. HMENU hmenuCtx = mii.hSubMenu;
  6463. if (hmenuCtx)
  6464. {
  6465. int idToCheck = -1;
  6466. AddColumns();
  6467. UINT cVisible = _RealToVisibleCol(-1) + 1; // count
  6468. ICategoryProvider* pcp = NULL;
  6469. _pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp));
  6470. while (1)
  6471. {
  6472. MENUITEMINFO miiSep = {0};
  6473. miiSep.cbSize = sizeof(mii);
  6474. miiSep.fMask = MIIM_ID | MIIM_TYPE;
  6475. miiSep.wID = -1;
  6476. if (!GetMenuItemInfo(hmenuCtx, 0, MF_BYPOSITION, &miiSep) ||
  6477. miiSep.wID == SFVIDM_GROUPSEP)
  6478. {
  6479. break;
  6480. }
  6481. DeleteMenu(hmenuCtx, 0, MF_BYPOSITION);
  6482. }
  6483. UINT iInsert = 0;
  6484. for (UINT i = 0; i < cVisible; i++)
  6485. {
  6486. BOOL fAddItem = TRUE;
  6487. UINT iReal = _VisibleToRealCol(i);
  6488. if (_IsDetailsColumn(iReal))
  6489. {
  6490. // See if the category Provider wants to exclude this column when groupview is enabled
  6491. if (pcp && _fGroupView)
  6492. {
  6493. SHCOLUMNID scid;
  6494. if (SUCCEEDED(_pshf2->MapColumnToSCID(iReal, &scid)))
  6495. {
  6496. // returns S_FALSE to remove.
  6497. fAddItem = (S_OK == pcp->CanCategorizeOnSCID(&scid));
  6498. }
  6499. }
  6500. if (fAddItem)
  6501. {
  6502. WCHAR wszName[MAX_COLUMN_NAME_LEN];
  6503. BOOL bpuiName = FALSE;
  6504. IPropertyUI *ppui;
  6505. // Attempt to retrieve mnemonic name from IPropertyUI interface.
  6506. if (_pshf2 && SUCCEEDED(_GetPropertyUI(&ppui)))
  6507. {
  6508. SHCOLUMNID scid;
  6509. if (SUCCEEDED(_pshf2->MapColumnToSCID(iReal, &scid)))
  6510. {
  6511. bpuiName = SUCCEEDED(ppui->GetDisplayName(scid.fmtid, scid.pid, PUIFNF_MNEMONIC, wszName, ARRAYSIZE(wszName)));
  6512. }
  6513. ppui->Release();
  6514. }
  6515. MENUITEMINFO miiItem = {0};
  6516. miiItem.cbSize = sizeof(mii);
  6517. miiItem.fMask = MIIM_ID | MIIM_TYPE;
  6518. miiItem.fType = MFT_STRING;
  6519. miiItem.wID = iReal + SFVIDM_GROUPSFIRST;
  6520. miiItem.dwTypeData = bpuiName ? wszName : _vs.GetColumnName(iReal);
  6521. InsertMenuItem(hmenuCtx, iInsert++, TRUE, &miiItem);
  6522. }
  6523. }
  6524. }
  6525. _InitExtendedGroups(pcp, hmenuCtx, iInsert, &idToCheck);
  6526. // Only do the Bullets if we're in auto arrange mode or if we are in details.
  6527. if (_IsAutoArrange() || _fGroupView || _fs.ViewMode == FVM_DETAILS)
  6528. {
  6529. if (idToCheck == -1)
  6530. {
  6531. // Since we're not going to have more than 4million columns, this case should suffice
  6532. idToCheck = (int)_vs._lParamSort + SFVIDM_GROUPSFIRST;
  6533. if (_fGroupView &&
  6534. !(_fs.ViewMode == FVM_LIST))
  6535. {
  6536. idToCheck = MapSCIDToColumn(_pshf2, &_vs._scidDetails) + SFVIDM_GROUPSFIRST;
  6537. }
  6538. }
  6539. CheckMenuRadioItem(hmenuCtx, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSEXTENDEDLAST, idToCheck, MF_BYCOMMAND | MF_CHECKED);
  6540. }
  6541. if (pcp)
  6542. pcp->Release();
  6543. }
  6544. DWORD dwGroupEnableFlags = MF_GRAYED;
  6545. if (_pshf2 && // Needs to implement IShellFolder2
  6546. !_IsViewDesktop() && // Doesn't work on the desktop
  6547. !(_fs.ViewMode == FVM_LIST) && // Doesn't work in 'List' View
  6548. !(_fs.ViewMode == FVM_THUMBSTRIP) &&// Doesn't work in 'ThumbStrip' View
  6549. !(_fs.fFlags & FWF_OWNERDATA)) // Doesn't work for ownerdata lists (search)
  6550. {
  6551. dwGroupEnableFlags = MF_ENABLED;
  6552. CheckMenuItem(hmenuCtx, SFVIDM_GROUPBY, MF_BYCOMMAND | (_fGroupView?MF_CHECKED:0));
  6553. }
  6554. EnableMenuItem(hmenuCtx, SFVIDM_GROUPBY, MF_BYCOMMAND | dwGroupEnableFlags);
  6555. _SHPrettyMenu(hmenuCtx);
  6556. return TRUE;
  6557. }
  6558. BOOL CDefView::_InitExtendedGroups(ICategoryProvider* pcp, HMENU hmenuCtx, int iIndex, int* piIdToCheck)
  6559. {
  6560. if (!pcp)
  6561. return FALSE;
  6562. *piIdToCheck = -1;
  6563. if (_hdaCategories == NULL)
  6564. {
  6565. _hdaCategories = DSA_Create(sizeof(GUID), 5);
  6566. if (_hdaCategories)
  6567. {
  6568. IEnumGUID* penum;
  6569. if (SUCCEEDED(pcp->EnumCategories(&penum)))
  6570. {
  6571. GUID guidCat;
  6572. while (S_OK == penum->Next(1, &guidCat, NULL))
  6573. {
  6574. DSA_AppendItem(_hdaCategories, (void*)&guidCat);
  6575. }
  6576. penum->Release();
  6577. }
  6578. }
  6579. }
  6580. if (_hdaCategories)
  6581. {
  6582. int id = SFVIDM_GROUPSEXTENDEDFIRST;
  6583. TCHAR szName[MAX_PATH];
  6584. TCHAR szCurrentName[MAX_PATH];
  6585. WCHAR wszName[MAX_PATH];
  6586. GUID* pguidCat;
  6587. szCurrentName[0] = 0;
  6588. if (_pcat)
  6589. {
  6590. _pcat->GetDescription(szCurrentName, ARRAYSIZE(szCurrentName));
  6591. }
  6592. MENUITEMINFO mii = {0};
  6593. mii.cbSize = sizeof(MENUITEMINFO);
  6594. mii.fMask = MIIM_ID | MIIM_TYPE;
  6595. mii.fType = MFT_SEPARATOR;
  6596. mii.wID = -1;
  6597. InsertMenuItem(hmenuCtx, iIndex, TRUE, &mii);
  6598. iIndex++;
  6599. int cCategories = DSA_GetItemCount(_hdaCategories);
  6600. for (int i = 0; i < cCategories; i++)
  6601. {
  6602. pguidCat = (GUID*)DSA_GetItemPtr(_hdaCategories, i);
  6603. if (SUCCEEDED(pcp->GetCategoryName(pguidCat, wszName, ARRAYSIZE(wszName))))
  6604. {
  6605. SHUnicodeToTChar(wszName, szName, ARRAYSIZE(szName));
  6606. MENUITEMINFO mii = {0};
  6607. mii.cbSize = sizeof(MENUITEMINFO);
  6608. mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
  6609. mii.fType = MFT_STRING;
  6610. mii.dwItemData = (DWORD_PTR)pguidCat;
  6611. mii.wID = id;
  6612. mii.dwTypeData = szName;
  6613. mii.cch = ARRAYSIZE(szName);
  6614. InsertMenuItem(hmenuCtx, iIndex, TRUE, &mii);
  6615. if (lstrcmpi(szCurrentName, szName) == 0)
  6616. {
  6617. *piIdToCheck = id;
  6618. }
  6619. id++;
  6620. iIndex++;
  6621. }
  6622. }
  6623. }
  6624. return TRUE;
  6625. }
  6626. BOOL CDefView::_CategorizeOnSCID(const SHCOLUMNID* pscid)
  6627. {
  6628. BOOL fRet = FALSE;
  6629. _fSlowGroup = FALSE;
  6630. if (IsEqualSCID(*pscid, SCID_NAME))
  6631. {
  6632. if (SUCCEEDED(CAlphaCategorizer_Create(_pshf2, IID_PPV_ARG(ICategorizer, &_pcat))))
  6633. {
  6634. _vs._guidGroupID = CLSID_AlphabeticalCategorizer;
  6635. fRet = TRUE;
  6636. }
  6637. }
  6638. else if (IsEqualSCID(*pscid, SCID_SIZE))
  6639. {
  6640. if (SUCCEEDED(CSizeCategorizer_Create(_pshf2, IID_PPV_ARG(ICategorizer, &_pcat))))
  6641. {
  6642. _vs._guidGroupID = CLSID_SizeCategorizer;
  6643. fRet = TRUE;
  6644. }
  6645. }
  6646. else if (IsEqualSCID(*pscid, SCID_WRITETIME) ||
  6647. IsEqualSCID(*pscid, SCID_CREATETIME) ||
  6648. IsEqualSCID(*pscid, SCID_ACCESSTIME) ||
  6649. IsEqualSCID(*pscid, SCID_DATEDELETED))
  6650. {
  6651. if (SUCCEEDED(CTimeCategorizer_Create(_pshf2, pscid, IID_PPV_ARG(ICategorizer, &_pcat))))
  6652. {
  6653. _vs._guidGroupID = CLSID_TimeCategorizer;
  6654. fRet = TRUE;
  6655. }
  6656. }
  6657. else
  6658. {
  6659. _fSlowGroup = TRUE;
  6660. if (SUCCEEDED(CDetailCategorizer_Create(*pscid, _pshf2, IID_PPV_ARG(ICategorizer, &_pcat))))
  6661. {
  6662. _vs._guidGroupID = CLSID_DetailCategorizer;
  6663. fRet = TRUE;
  6664. }
  6665. }
  6666. if (fRet)
  6667. {
  6668. _vs._scidDetails = *pscid;
  6669. }
  6670. return fRet;
  6671. }
  6672. // slow groups have an architecture problem, after 5000 items in the view
  6673. // the message queue overflows from groupdone messages and its all bad.
  6674. // this ends up hanging the static flashlight window around because of resulting
  6675. // refcount issues.
  6676. // the only view that both defaults to a slow group and could have 5000 items is the
  6677. // cd burning folder. lou says its too late to change the interface now to let the
  6678. // categorizer decide if its slow or not, so just special case it here.
  6679. // everything works if its a fast group (and its actually fast anyway).
  6680. BOOL CDefView::_IsSlowGroup(const GUID *pguid)
  6681. {
  6682. BOOL fSlow = TRUE;
  6683. if (IsEqualGUID(*pguid, CLSID_MergedCategorizer))
  6684. {
  6685. fSlow = FALSE;
  6686. }
  6687. // room to grow if we need to special case others
  6688. return fSlow;
  6689. }
  6690. BOOL CDefView::_CategorizeOnGUID(const GUID* pguid, const SHCOLUMNID* pscid)
  6691. {
  6692. BOOL fRet = FALSE;
  6693. if (_pshf2)
  6694. {
  6695. _fGroupView = FALSE; // Just in case the create fails
  6696. if (_pScheduler)
  6697. _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE);
  6698. ATOMICRELEASE(_pcat);
  6699. ListView_RemoveAllGroups(_hwndListview);
  6700. ICategoryProvider* pcp;
  6701. if (SUCCEEDED(_pshf->CreateViewObject(NULL, IID_PPV_ARG(ICategoryProvider, &pcp))))
  6702. {
  6703. GUID guidGroup = *pguid;
  6704. if (pscid && S_OK != pcp->GetCategoryForSCID(const_cast<SHCOLUMNID*>(pscid), &guidGroup))
  6705. {
  6706. fRet = _CategorizeOnSCID(pscid);
  6707. }
  6708. else
  6709. {
  6710. _fSlowGroup = _IsSlowGroup(&guidGroup);
  6711. if (SUCCEEDED(pcp->CreateCategory(&guidGroup, IID_PPV_ARG(ICategorizer, &_pcat))))
  6712. {
  6713. _vs._guidGroupID = guidGroup;
  6714. if (pscid)
  6715. {
  6716. _vs._scidDetails = *pscid;
  6717. }
  6718. else
  6719. {
  6720. ZeroMemory(&_vs._scidDetails, sizeof(_vs._scidDetails));
  6721. }
  6722. fRet = TRUE;
  6723. }
  6724. }
  6725. pcp->Release();
  6726. }
  6727. else
  6728. {
  6729. if (pscid)
  6730. fRet = _CategorizeOnSCID(pscid);
  6731. }
  6732. }
  6733. if (fRet)
  6734. {
  6735. _ClearItemPositions();
  6736. _fGroupView = TRUE;
  6737. SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, LVS_AUTOARRANGE);
  6738. // We're enabling groupview, so turn off the selected column
  6739. // (this will make it so tiles do not show the selected column as their first column)
  6740. ListView_SetSelectedColumn(_hwndListview, -1);
  6741. if (_fSlowGroup)
  6742. _fAllowSearchingWindow = TRUE;
  6743. ListView_EnableGroupView(_hwndListview, TRUE);
  6744. }
  6745. return fRet;
  6746. }
  6747. void CDefView::_GroupBy(int iColumn)
  6748. {
  6749. _fGroupView = FALSE; // Just in case the create fails
  6750. if (_pshf2)
  6751. {
  6752. if (InRange(iColumn, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST))
  6753. {
  6754. int iIndex = iColumn - SFVIDM_GROUPSEXTENDEDFIRST;
  6755. GUID* pguid = (GUID*)DSA_GetItemPtr(_hdaCategories, iIndex);
  6756. if (pguid)
  6757. {
  6758. _CategorizeOnGUID(pguid, NULL);
  6759. }
  6760. }
  6761. else
  6762. {
  6763. SHCOLUMNID scid;
  6764. iColumn -= SFVIDM_GROUPSFIRST;
  6765. if (SUCCEEDED(_pshf2->MapColumnToSCID(iColumn, &scid)))
  6766. {
  6767. _CategorizeOnGUID(&CLSID_DetailCategorizer, &scid);
  6768. }
  6769. }
  6770. // Make sure the arrows on details view look right...
  6771. _SetSortFeedback();
  6772. }
  6773. }
  6774. void CDefView::_ToggleGrouping()
  6775. {
  6776. if (_fGroupView)
  6777. {
  6778. _fGroupView = FALSE;
  6779. if (_pScheduler)
  6780. _pScheduler->RemoveTasks(TOID_DVBackgroundGroup, ITSAT_DEFAULT_LPARAM, TRUE);
  6781. ListView_EnableGroupView(_hwndListview, FALSE);
  6782. ListView_RemoveAllGroups(_hwndListview);
  6783. ListView_SetSelectedColumn(_hwndListview, _vs._lParamSort);
  6784. _SetSortFeedback();
  6785. _OnRearrange(_vs._lParamSort, FALSE);
  6786. }
  6787. else if (FVM_THUMBSTRIP != _fs.ViewMode) // Thumbstrip can never go into groupby mode
  6788. {
  6789. // If we have a categorizer, then we can just reenable grouping.
  6790. if (_pcat)
  6791. {
  6792. _fGroupView = TRUE;
  6793. ListView_EnableGroupView(_hwndListview, TRUE);
  6794. ListView_SetSelectedColumn(_hwndListview, -1);
  6795. _SetSortFeedback();
  6796. }
  6797. else
  6798. {
  6799. // If we don't, then we need to go get one.
  6800. _GroupBy((int)_vs._lParamSort + SFVIDM_GROUPSFIRST);
  6801. }
  6802. }
  6803. }
  6804. LRESULT CDefView::_OnDefviewEditCommand(UINT uID)
  6805. {
  6806. // if we are in label edit mode, don't allowany of the buttons......
  6807. if (_fInLabelEdit)
  6808. {
  6809. MessageBeep(0);
  6810. return 1;
  6811. }
  6812. if (_AllowCommand(uID))
  6813. {
  6814. HRESULT hr = _ExplorerCommand(uID);
  6815. if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_CANCELLED)))
  6816. {
  6817. MessageBeep(0);
  6818. }
  6819. return 1;
  6820. }
  6821. else
  6822. {
  6823. return 0;
  6824. }
  6825. }
  6826. HRESULT CDefView::_DoMoveOrCopyTo(REFCLSID clsid, IShellItemArray *psiItemArray)
  6827. {
  6828. IDataObject *pdo = NULL;
  6829. IContextMenu *pcm;
  6830. HRESULT hr = E_FAIL;
  6831. if (!psiItemArray)
  6832. {
  6833. psiItemArray = _GetFolderAsShellItemArray();
  6834. }
  6835. if (psiItemArray)
  6836. {
  6837. hr = psiItemArray->BindToHandler(NULL,BHID_DataObject,IID_PPV_ARG(IDataObject, &pdo));
  6838. }
  6839. if (SUCCEEDED(hr))
  6840. {
  6841. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
  6842. if (SUCCEEDED(hr))
  6843. {
  6844. IUnknown_SetSite(pcm, SAFECAST(this, IDropTarget *)); // Needed to go modal during UI
  6845. IShellExtInit* psei;
  6846. hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &psei));
  6847. if (SUCCEEDED(hr))
  6848. {
  6849. LPITEMIDLIST pidlFolder = _GetViewPidl();
  6850. if (pidlFolder)
  6851. {
  6852. psei->Initialize(pidlFolder, pdo, NULL);
  6853. ILFree(pidlFolder);
  6854. }
  6855. CMINVOKECOMMANDINFO ici = {0};
  6856. ici.hwnd = _hwndMain;
  6857. hr = pcm->InvokeCommand(&ici);
  6858. psei->Release();
  6859. }
  6860. IUnknown_SetSite(pcm, NULL);
  6861. pcm->Release();
  6862. }
  6863. pdo->Release();
  6864. }
  6865. return hr;
  6866. }
  6867. LRESULT CDefView::_OnCommand(IContextMenu *pcmToInvoke, WPARAM wParam, LPARAM lParam)
  6868. {
  6869. UINT uID = GET_WM_COMMAND_ID(wParam, lParam);
  6870. if (InRange(uID, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSEXTENDEDLAST))
  6871. {
  6872. _ArrangeBy(uID);
  6873. return 1;
  6874. }
  6875. else if (InRange(uID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
  6876. {
  6877. UINT uCMBias = SFVIDM_CONTEXT_FIRST;
  6878. if (_pcmFile)
  6879. {
  6880. IContextMenu* pcmToInvoke = _pcmFile;
  6881. pcmToInvoke->AddRef();
  6882. // We need to special case the rename command
  6883. TCHAR szCommandString[64];
  6884. ContextMenu_GetCommandStringVerb(pcmToInvoke, uID - SFVIDM_CONTEXT_FIRST, szCommandString, ARRAYSIZE(szCommandString));
  6885. if (lstrcmpi(szCommandString, c_szRename) == 0)
  6886. {
  6887. DoRename();
  6888. }
  6889. else
  6890. {
  6891. CMINVOKECOMMANDINFOEX ici = { 0 };
  6892. ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
  6893. ici.hwnd = _hwndMain;
  6894. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(uID - SFVIDM_CONTEXT_FIRST);
  6895. ici.nShow = SW_NORMAL;
  6896. ici.fMask = CMIC_MASK_FLAG_LOG_USAGE;
  6897. int iItemSelect = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
  6898. if (iItemSelect != -1)
  6899. {
  6900. RECT rcItem;
  6901. ListView_GetItemRect(_hwndListview, iItemSelect, &rcItem, LVIR_BOUNDS);
  6902. MapWindowPoints(_hwndListview, HWND_DESKTOP, (POINT *)&rcItem, 2);
  6903. ici.ptInvoke.x = (rcItem.left + rcItem.right) / 2;
  6904. ici.ptInvoke.y = (rcItem.top + rcItem.bottom) / 2;
  6905. ici.fMask |= CMIC_MASK_PTINVOKE;
  6906. }
  6907. // record if shift or control was being held down
  6908. SetICIKeyModifiers(&ici.fMask);
  6909. _InvokeContextMenu(pcmToInvoke, &ici);
  6910. }
  6911. //Since we are releaseing our only hold on the context menu, release the site.
  6912. IUnknown_SetSite(pcmToInvoke, NULL);
  6913. pcmToInvoke->Release(); // undo our gaurd ref
  6914. ATOMICRELEASE(_pcmFile); // once used, it can't be used again
  6915. }
  6916. return 0;
  6917. }
  6918. #ifdef DEBUG
  6919. else if (InRange(uID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST))
  6920. {
  6921. RIPMSG(FALSE, "_OnCommand should not get this context menu invoke...");
  6922. }
  6923. #endif
  6924. else if (InRange(uID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB())
  6925. {
  6926. // view callback range
  6927. CallCB(SFVM_INVOKECOMMAND, uID - SFVIDM_CLIENT_FIRST, 0);
  6928. return 0;
  6929. }
  6930. // First check for commands that always go to this defview
  6931. switch (uID)
  6932. {
  6933. case SFVIDM_GROUPBY:
  6934. _ToggleGrouping();
  6935. break;
  6936. case SFVIDM_EDIT_UNDO:
  6937. // if we are in label edit mode, don't allowany of the buttons......
  6938. if (_fInLabelEdit)
  6939. {
  6940. MessageBeep(0);
  6941. return 0;
  6942. }
  6943. Undo(_hwndMain);
  6944. break;
  6945. case SFVIDM_VIEW_COLSETTINGS:
  6946. {
  6947. CColumnDlg ccd(this);
  6948. AddColumns();
  6949. ccd.ShowDialog(_hwndMain);
  6950. break;
  6951. }
  6952. case SFVIDM_VIEW_VIEWMENU:
  6953. {
  6954. // if we are in label edit mode, don't allow any of the buttons......
  6955. if (_fInLabelEdit)
  6956. {
  6957. MessageBeep(0);
  6958. return 0;
  6959. }
  6960. LPCDFVCMDDATA pcd = (LPCDFVCMDDATA)lParam;
  6961. if (pcd && pcd->pva && pcd->pva->byref)
  6962. {
  6963. LPRECT prect = (LPRECT)pcd->pva->byref;
  6964. IContextMenu* pcm;
  6965. if (SUCCEEDED(_Create_BackgrndHMENU(TRUE, IID_PPV_ARG(IContextMenu, &pcm))))
  6966. {
  6967. POINT pt = { prect->left, prect->bottom};
  6968. DoContextMenuPopup(pcm, 0, pt);
  6969. pcm->Release();
  6970. }
  6971. }
  6972. }
  6973. break;
  6974. case SFVIDM_VIEW_TILE:
  6975. //
  6976. // AppCompat: Pre WinXP 0x702E used to be SFVIDM_VIEW_VIEWMENU, now it's SFVIDM_VIEW_TILE.
  6977. // Corel apps send 0x702E to get the ViewMenu on the SaveAs dialogs. Of course that no
  6978. // longer works since 0x702E switches them to TileMode. Luckily SFVIDM_VIEW_VIEWMENU has
  6979. // a non-NULL lParam while SFVIDM_VIEW_TILE always has a NULL lParam so we can tell the
  6980. // two apart. So when Corel sends a 0x702E with a non-NULL lParam they mean SFVIDM_VIEW_VIEWMENU
  6981. // and when they send a 0x702E with a NULL lParam they mean SFVIDM_VIEW_TILE.
  6982. //
  6983. COMPILETIME_ASSERT(SFVIDM_VIEW_TILE == 0x702E); //see above app compat comments.
  6984. if (lParam && (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW))
  6985. {
  6986. return _OnCommand(pcmToInvoke, SFVIDM_VIEW_VIEWMENU, lParam); // change this into a SFVIDM_VIEW_VIEWMENU
  6987. }
  6988. // Fall through ...
  6989. case SFVIDM_VIEW_ICON:
  6990. case SFVIDM_VIEW_SMALLICON:
  6991. case SFVIDM_VIEW_THUMBNAIL:
  6992. case SFVIDM_VIEW_THUMBSTRIP:
  6993. case SFVIDM_VIEW_LIST:
  6994. case SFVIDM_VIEW_DETAILS:
  6995. COMPILETIME_ASSERT(FVM_ICON == (SFVIDM_VIEW_ICON-SFVIDM_VIEW_FIRST));
  6996. COMPILETIME_ASSERT(FVM_SMALLICON == (SFVIDM_VIEW_SMALLICON-SFVIDM_VIEW_FIRST));
  6997. COMPILETIME_ASSERT(FVM_THUMBNAIL == (SFVIDM_VIEW_THUMBNAIL-SFVIDM_VIEW_FIRST));
  6998. COMPILETIME_ASSERT(FVM_THUMBSTRIP == (SFVIDM_VIEW_THUMBSTRIP-SFVIDM_VIEW_FIRST));
  6999. COMPILETIME_ASSERT(FVM_LIST == (SFVIDM_VIEW_LIST-SFVIDM_VIEW_FIRST));
  7000. COMPILETIME_ASSERT(FVM_TILE == (SFVIDM_VIEW_TILE-SFVIDM_VIEW_FIRST));
  7001. COMPILETIME_ASSERT(FVM_DETAILS == (SFVIDM_VIEW_DETAILS-SFVIDM_VIEW_FIRST));
  7002. SetCurrentViewMode(uID - SFVIDM_VIEW_FIRST);
  7003. break;
  7004. case SFVIDM_DESKTOPHTML_WEBCONTENT:
  7005. {
  7006. // we have removed this button, but we need to keep this for message for other things
  7007. BOOL bHasVisibleNonLocalPicture = FALSE;
  7008. SHELLSTATE ss;
  7009. SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE); // Get the setting
  7010. ss.fDesktopHTML = !ss.fDesktopHTML; // Toggle the state
  7011. if (ss.fDesktopHTML && !IsICWCompleted())
  7012. {
  7013. IActiveDesktop *pIAD;
  7014. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IActiveDesktop, &pIAD))))
  7015. {
  7016. bHasVisibleNonLocalPicture = (DisableUndisplayableComponents(pIAD) != 0);
  7017. pIAD->Release();
  7018. }
  7019. }
  7020. if (!bHasVisibleNonLocalPicture)
  7021. {
  7022. SHELLSTATE ss2;
  7023. SHGetSetSettings(&ss, SSF_DESKTOPHTML, TRUE); // Write back the new
  7024. // Now read back the current setting - only call _SwitchDesktopHTML if the current
  7025. // setting and the one we just set agree. If they don't that means someone changed
  7026. // the setting during the above call and we shouldn't do any more work or our state
  7027. // will get messed up.
  7028. SHGetSetSettings(&ss2, SSF_DESKTOPHTML, FALSE);
  7029. if (ss.fDesktopHTML == ss2.fDesktopHTML)
  7030. {
  7031. _SwitchDesktopHTML(BOOLIFY(ss.fDesktopHTML));
  7032. }
  7033. }
  7034. }
  7035. break;
  7036. case SFVIDM_DESKTOPHTML_ICONS:
  7037. case SFVIDM_ARRANGE_DISPLAYICONS: // (buzzr) I'm leaving SFVIDM_ARRANGE_DISPLAYICONS
  7038. { // for backwards compat. It used to be a
  7039. SHELLSTATE ss; // menu entry on POPUP_SFV_BACKGROUND.
  7040. DWORD dwValue;
  7041. // Toggle the cached state
  7042. _fs.fFlags ^= FWF_NOICONS;
  7043. ss.fHideIcons = ((_fs.fFlags & FWF_NOICONS) != 0);
  7044. dwValue = ss.fHideIcons ? 1 : 0;
  7045. // Since this value is currrently stored under the "advanced" reg tree we need
  7046. // to explicitly write to the registry or the value won't persist properly via
  7047. // SHGetSetSettings.
  7048. SHSetValue(HKEY_CURRENT_USER,
  7049. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  7050. TEXT("HideIcons"), REG_DWORD, &dwValue, sizeof(dwValue));
  7051. // Finally set the ShellState and perform the action!
  7052. SHGetSetSettings(&ss, SSF_HIDEICONS, TRUE);
  7053. // Since this SFVIDM_ comes from the menu, we better already be active (or
  7054. // this SW_SHOW could make us visible before we want to be seen).
  7055. ASSERT(_uState != SVUIA_DEACTIVATE);
  7056. ActiveDesktop_ApplyChanges();
  7057. ShowHideListView();
  7058. }
  7059. break;
  7060. case SFVIDM_DESKTOPHTML_LOCK:
  7061. {
  7062. DWORD dwFlags = GetDesktopFlags();
  7063. dwFlags ^= COMPONENTS_LOCKED;
  7064. SetDesktopFlags(COMPONENTS_LOCKED, dwFlags);
  7065. ActiveDesktop_ApplyChanges();
  7066. }
  7067. break;
  7068. case SFVIDM_DESKTOPHTML_WIZARD:
  7069. {
  7070. // launch desktop cleanup wizard
  7071. SHRunDLLThread(NULL, TEXT("fldrclnr.dll,Wizard_RunDLL all"), SW_SHOWNORMAL);
  7072. }
  7073. break;
  7074. case SFVIDM_EDIT_COPYTO:
  7075. case SFVIDM_EDIT_MOVETO:
  7076. {
  7077. // if we are in label edit mode, don't allowany of the buttons......
  7078. if (_fInLabelEdit)
  7079. {
  7080. MessageBeep(0);
  7081. return 0;
  7082. }
  7083. if (_pSelectionShellItemArray)
  7084. {
  7085. _DoMoveOrCopyTo(((uID == SFVIDM_EDIT_COPYTO) ? CLSID_CopyToMenu : CLSID_MoveToMenu), _pSelectionShellItemArray);
  7086. }
  7087. }
  7088. break;
  7089. case SFVIDM_FILE_PROPERTIES:
  7090. if (SHRestricted(REST_NOVIEWCONTEXTMENU))
  7091. break;
  7092. // else fall through...
  7093. case SFVIDM_EDIT_PASTE:
  7094. case SFVIDM_EDIT_PASTELINK:
  7095. case SFVIDM_EDIT_COPY:
  7096. case SFVIDM_EDIT_CUT:
  7097. case SFVIDM_FILE_LINK:
  7098. case SFVIDM_FILE_DELETE:
  7099. if (!_OnDefviewEditCommand(uID))
  7100. {
  7101. // REVIEW: this looks like a hack.
  7102. // there's got to be a cleaner way of doing this...
  7103. //
  7104. LPDFVCMDDATA pcd = (LPDFVCMDDATA)lParam;
  7105. // Try translating the SFVIDM value into a standard
  7106. // OLECMDID value, so that the caller can try applying
  7107. // it to a different object.
  7108. // doh
  7109. if (!IsBadWritePtr(pcd, sizeof(*pcd)))
  7110. {
  7111. pcd->nCmdIDTranslated = OlecmdidFromSfvidm(uID);
  7112. }
  7113. }
  7114. break;
  7115. case SFVIDM_TOOL_OPTIONS:
  7116. if (!SHRestricted(REST_NOFOLDEROPTIONS))
  7117. {
  7118. IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_OPTIONS, 0, NULL, NULL);
  7119. }
  7120. break;
  7121. #ifdef DEBUG
  7122. case SFVIDM_DEBUG_WEBVIEW:
  7123. _cFrame._ShowWebViewContent();
  7124. break;
  7125. #endif // DEBUG
  7126. case SFVIDM_HELP_TOPIC:
  7127. // Don't call WinHelp when we are in the common dialog.
  7128. if (!_IsCommonDialog())
  7129. {
  7130. // Use a callback to see if the namespace has requested a different help file name and/or topic
  7131. SFVM_HELPTOPIC_DATA htd;
  7132. HWND hwndDesktop = GetDesktopWindow();
  7133. SHTCharToUnicode(c_szHtmlWindowsHlp, htd.wszHelpFile, ARRAYSIZE(htd.wszHelpFile));
  7134. htd.wszHelpTopic[0] = 0;
  7135. if (SUCCEEDED(CallCB(SFVM_GETHELPTOPIC, 0, (LPARAM)&htd)))
  7136. {
  7137. if (URL_SCHEME_MSHELP == GetUrlSchemeW(htd.wszHelpTopic))
  7138. {
  7139. //
  7140. // Callback specified an HSS help URL.
  7141. //
  7142. SHELLEXECUTEINFOW sei = {0};
  7143. sei.cbSize = sizeof(sei);
  7144. sei.lpFile = htd.wszHelpTopic;
  7145. sei.hwnd = hwndDesktop;
  7146. sei.nShow = SW_NORMAL;
  7147. // executing help topic
  7148. ShellExecuteExW(&sei);
  7149. }
  7150. else
  7151. {
  7152. HtmlHelp(hwndDesktop, htd.wszHelpFile, HH_HELP_FINDER, htd.wszHelpTopic[0] ? (DWORD_PTR)htd.wszHelpTopic : 0);
  7153. }
  7154. }
  7155. else
  7156. {
  7157. // ask the shell dispatch object to display Help for us
  7158. IShellDispatch *psd;
  7159. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_Shell, NULL, IID_PPV_ARG(IShellDispatch, &psd))))
  7160. {
  7161. psd->Help();
  7162. psd->Release();
  7163. }
  7164. }
  7165. }
  7166. break;
  7167. case SFVIDM_VIEW_CUSTOMWIZARD:
  7168. _InvokeCustomization();
  7169. break;
  7170. case SFVIDM_MISC_HARDREFRESH:
  7171. _fAllowSearchingWindow = TRUE;
  7172. _FreeWebViewContentData();
  7173. _ReloadContent(TRUE); // have to enumerate before _GetDefaultViewMode() will be accurate
  7174. SetCurrentViewMode(_GetDefaultViewMode()); // even if fvm is the same, it will update webview if it changed
  7175. Refresh();
  7176. break;
  7177. case SFVIDM_MISC_SETWEBVIEW:
  7178. SetCurrentViewMode(_fs.ViewMode); // re-setting the fvm updates everything (turning web view off can switch from Thumbstrip to Thumbnail!)
  7179. Refresh(); // we want to refresh when we switch turn webview on/off, since some icons appear/disappear on the transition
  7180. break;
  7181. case SFVIDM_MISC_REFRESH:
  7182. _fAllowSearchingWindow = TRUE;
  7183. Refresh();
  7184. break;
  7185. default:
  7186. // check for commands that need to be sent to the active object
  7187. switch (uID)
  7188. {
  7189. case SFVIDM_ARRANGE_AUTO:
  7190. _fs.fFlags ^= FWF_AUTOARRANGE; // toggle
  7191. _ClearItemPositions();
  7192. SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, _IsAutoArrange() ? LVS_AUTOARRANGE : 0);
  7193. break;
  7194. case SFVIDM_ARRANGE_GRID:
  7195. ListView_Arrange(_hwndListview, LVA_SNAPTOGRID);
  7196. break;
  7197. case SFVIDM_ARRANGE_AUTOGRID:
  7198. {
  7199. _fs.fFlags ^= FWF_SNAPTOGRID;
  7200. DWORD dwLVFlags = ListView_GetExtendedListViewStyle(_hwndListview);
  7201. dwLVFlags ^= LVS_EX_SNAPTOGRID;
  7202. ListView_SetExtendedListViewStyle(_hwndListview, dwLVFlags);
  7203. //if this is desktop, we need to change the icon spacing.
  7204. UpdateGridSizes(_IsDesktop(), _hwndListview, 0, NULL, BOOLIFY(dwLVFlags & LVS_EX_SNAPTOGRID));
  7205. // if ActiveDesktop on, need to refresh, otherwise, can just arrange
  7206. SHELLSTATE ss = {0};
  7207. SHGetSetSettings( &ss, SSF_DESKTOPHTML, FALSE);
  7208. if (ss.fDesktopHTML)
  7209. {
  7210. Refresh();
  7211. }
  7212. else
  7213. {
  7214. if ((dwLVFlags & LVS_EX_SNAPTOGRID))
  7215. {
  7216. ListView_Arrange(_hwndListview, LVA_SNAPTOGRID);
  7217. }
  7218. }
  7219. }
  7220. break;
  7221. default:
  7222. // Normal view, we know what to do
  7223. switch (uID)
  7224. {
  7225. case SFVIDM_SELECT_ALL:
  7226. {
  7227. DECLAREWAITCURSOR;
  7228. if (CallCB(SFVM_SELECTALL, 0, 0) != S_FALSE)
  7229. {
  7230. SetWaitCursor();
  7231. SetFocus(_hwndListview);
  7232. ListView_SetItemState(_hwndListview, -1, LVIS_SELECTED, LVIS_SELECTED);
  7233. // make the first item in the view the focused guy
  7234. ListView_SetItemState(_hwndListview, 0, LVIS_FOCUSED, LVIS_FOCUSED);
  7235. ResetWaitCursor();
  7236. }
  7237. break;
  7238. }
  7239. case SFVIDM_DESELECT_ALL:
  7240. ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
  7241. break;
  7242. case SFVIDM_SELECT_INVERT:
  7243. {
  7244. DECLAREWAITCURSOR;
  7245. SetWaitCursor();
  7246. SetFocus(_hwndListview);
  7247. int iItem = -1;
  7248. while ((iItem = ListView_GetNextItem(_hwndListview, iItem, 0)) != -1)
  7249. {
  7250. // flip the selection bit on each item
  7251. UINT flag = ListView_GetItemState(_hwndListview, iItem, LVIS_SELECTED);
  7252. flag ^= LVNI_SELECTED;
  7253. ListView_SetItemState(_hwndListview, iItem, flag, LVIS_SELECTED);
  7254. }
  7255. ResetWaitCursor();
  7256. break;
  7257. }
  7258. case SFVIDM_FILE_RENAME:
  7259. DoRename();
  7260. break;
  7261. default:
  7262. return 1;
  7263. }
  7264. }
  7265. }
  7266. return 0;
  7267. }
  7268. LPITEMIDLIST CDefView::_ObjectExists(LPCITEMIDLIST pidl, BOOL fGlobal)
  7269. {
  7270. LPITEMIDLIST pidlReal = NULL;
  7271. // 365069 - global events also come through here - ZekeL - 16-APR-2001
  7272. // this means that that the pidl may not be one level. if its deeper
  7273. // then for us this item doesnt exist. this enforces our assert
  7274. if (pidl && !ILIsEmpty(pidl) && (!fGlobal || ILIsEmpty(_ILNext(pidl))))
  7275. {
  7276. ASSERTMSG(ILFindLastID(pidl) == pidl, "defview doesnt expect recursive notification");
  7277. SHGetRealIDL(_pshf, pidl, &pidlReal);
  7278. }
  7279. return pidlReal;
  7280. }
  7281. void CDefView::_OnRename(LPCITEMIDLIST* ppidl)
  7282. {
  7283. if (_pidlMonitor)
  7284. {
  7285. if (!ILIsParent(_pidlMonitor, ppidl[0], TRUE))
  7286. {
  7287. // move to this folder
  7288. _OnFSNotify(SHCNE_CREATE, &ppidl[1]);
  7289. }
  7290. else if (!ILIsParent(_pidlMonitor, ppidl[1], TRUE))
  7291. {
  7292. // move from this folder
  7293. _OnFSNotify(SHCNE_DELETE, &ppidl[0]);
  7294. }
  7295. else
  7296. {
  7297. // rename within this folder
  7298. // _pidlMonitor is guaranteed to be immediate parent of both pidls so ILFindLastID is okay.
  7299. LPCITEMIDLIST pidlOld = ILFindLastID(ppidl[0]);
  7300. LPITEMIDLIST pidlNew = _ObjectExists(ILFindLastID(ppidl[1]), FALSE);
  7301. if (pidlNew)
  7302. {
  7303. _UpdateObject(pidlOld, pidlNew);
  7304. ILFree(pidlNew);
  7305. }
  7306. }
  7307. }
  7308. }
  7309. //
  7310. // SFVM_UPDATESTATUSBAR return values:
  7311. //
  7312. // failure code = Callback did not do anything, we must do it all
  7313. //
  7314. // Otherwise, the GetScode(hr) is a bitmask describing what the app
  7315. // wants us to do.
  7316. //
  7317. // 0 - App wants us to do nothing (S_OK) - message handled completely
  7318. // 1 - App wants us to set the default text (but not initialize)
  7319. //
  7320. // <other bits reserved for future use>
  7321. void CDefView::_UpdateStatusBar(BOOL fInitialize)
  7322. {
  7323. HRESULT hr;
  7324. // We have to clear the contents here since some clients (like the ftp client) return S_OK from
  7325. // the callback but do not set the text of the bar
  7326. HWND hwndStatus;
  7327. if (_psb && SUCCEEDED(_psb->GetControlWindow(FCW_STATUS, &hwndStatus)) && hwndStatus)
  7328. {
  7329. _fBackgroundStatusTextValid = FALSE;
  7330. SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)_TEXT(""));
  7331. }
  7332. if (_bBkFilling || FAILED(hr = CallCB(SFVM_UPDATESTATUSBAR, fInitialize, 0)))
  7333. {
  7334. // Client wants us to do everything
  7335. _DoStatusBar(fInitialize);
  7336. }
  7337. else if (hr & SFVUSB_INITED)
  7338. {
  7339. // Client wants us to do text but not initialize
  7340. _DoStatusBar(FALSE);
  7341. }
  7342. }
  7343. // Returns TRUE iff we are supposed to show Web View content on this view.
  7344. // For the most part it follows SSF_WEBVIEW for normal folders and SSF_DESKTOPHTML for the desktop
  7345. //
  7346. BOOL CDefView::_ShouldShowWebView()
  7347. {
  7348. // No webview for common dialogs
  7349. if (_IsCommonDialog())
  7350. {
  7351. return FALSE;
  7352. }
  7353. // No webview in cleanboot mode
  7354. if (GetSystemMetrics(SM_CLEANBOOT))
  7355. return FALSE;
  7356. BOOL bForceWebViewOn;
  7357. if (SUCCEEDED(CallCB(SFVM_FORCEWEBVIEW, (WPARAM)&bForceWebViewOn, 0)))
  7358. {
  7359. return bForceWebViewOn;
  7360. }
  7361. // Quattro Pro (QPW) doesn't know how SHChangeNotify works,
  7362. // so when they want to refresh My Computer, they create an IShellView,
  7363. // invoke its CreateViewWindow(), invoke its Refresh(), then DestroyWindow
  7364. // the window and release the view. The IShellBrowser they pass
  7365. // to CreateViewWindow is allocated on the stack (!), and they expect
  7366. // that their Release() be the last one. Creating an async view keeps
  7367. // the object alive, so when the view is complete, we try to talk to the
  7368. // IShellBrowser and fault because it's already gone.
  7369. //
  7370. // The Zip Archives (from Aeco Systems) is another messed up App.
  7371. // They neither implement IPersistFolder2 (so we can't get their pidl) nor
  7372. // set the pidl to the shellfolderviewcb object. They don't implement
  7373. // IShellFolder2 either. Webview is practically useless for them.
  7374. //
  7375. // Adaptec Easy CD Creator 3.5 is in the same boat.
  7376. //
  7377. SHELLSTATE ss;
  7378. SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW, FALSE);
  7379. // If the "no web view" flag is set (potential WebOC case) then return false;
  7380. if (_fs.fFlags & FWF_NOWEBVIEW)
  7381. return FALSE;
  7382. if (_IsDesktop())
  7383. {
  7384. return ss.fDesktopHTML;
  7385. }
  7386. else
  7387. {
  7388. return ss.fWebView &&
  7389. !(SHGetAppCompatFlags(ACF_OLDCREATEVIEWWND) & ACF_OLDCREATEVIEWWND) &&
  7390. !(SHGetObjectCompatFlags(_pshf, NULL) & OBJCOMPATF_NO_WEBVIEW);
  7391. }
  7392. }
  7393. // takes ownership of pidlNew since _AddObject takes ownership.
  7394. void CDefView::_AddOrUpdateItem(LPCITEMIDLIST pidlOld, LPITEMIDLIST pidlNew)
  7395. {
  7396. if (_FindItem(pidlOld, NULL, FALSE) != -1)
  7397. {
  7398. _UpdateObject(pidlOld, pidlNew);
  7399. ILFree(pidlNew);
  7400. }
  7401. else
  7402. {
  7403. // check if the shellfolder says this new guy shouldn't be enumerated.
  7404. if (!_Attributes(pidlNew, SFGAO_NONENUMERATED))
  7405. {
  7406. _AddObject(pidlNew); // takes pidl ownership.
  7407. }
  7408. else
  7409. {
  7410. ILFree(pidlNew);
  7411. }
  7412. }
  7413. }
  7414. #define FSNDEBUG
  7415. // WM_DSV_FSNOTIFY message
  7416. LRESULT CDefView::_OnFSNotify(LONG lNotification, LPCITEMIDLIST* ppidl)
  7417. {
  7418. LPITEMIDLIST pidl;
  7419. LPCITEMIDLIST pidlItem;
  7420. //
  7421. // Note that renames between directories are changed to
  7422. // create/delete pairs by SHChangeNotify.
  7423. //
  7424. #ifdef DEBUG
  7425. #ifdef FSNDEBUG
  7426. TCHAR szPath[MAX_PATH];
  7427. TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify, hwnd = %d lEvent = %d", _hwndView, lNotification);
  7428. switch (lNotification)
  7429. {
  7430. case SHCNE_RENAMEITEM:
  7431. case SHCNE_RENAMEFOLDER:
  7432. // two pidls
  7433. SHGetPathFromIDList(ppidl[0], szPath);
  7434. TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath);
  7435. SHGetPathFromIDList(ppidl[1], szPath);
  7436. TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath);
  7437. break;
  7438. case SHCNE_CREATE:
  7439. case SHCNE_DELETE:
  7440. case SHCNE_MKDIR:
  7441. case SHCNE_RMDIR:
  7442. case SHCNE_MEDIAINSERTED:
  7443. case SHCNE_MEDIAREMOVED:
  7444. case SHCNE_DRIVEREMOVED:
  7445. case SHCNE_DRIVEADD:
  7446. case SHCNE_NETSHARE:
  7447. case SHCNE_NETUNSHARE:
  7448. case SHCNE_ATTRIBUTES:
  7449. case SHCNE_UPDATEDIR:
  7450. case SHCNE_UPDATEITEM:
  7451. case SHCNE_SERVERDISCONNECT:
  7452. case SHCNE_DRIVEADDGUI:
  7453. case SHCNE_EXTENDED_EVENT:
  7454. // one pidl
  7455. SHGetPathFromIDList(ppidl[0], szPath);
  7456. TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %s", _hwndView, szPath);
  7457. break;
  7458. case SHCNE_UPDATEIMAGE:
  7459. // DWORD wrapped inside a pidl
  7460. TraceMsg(TF_DEFVIEW, "CDefView::_OnFSNotify: hwnd %d, %08x", _hwndView,
  7461. ((LPSHChangeDWORDAsIDList)ppidl[0])->dwItem1);
  7462. break;
  7463. case SHCNE_ASSOCCHANGED:
  7464. // No parameters
  7465. break;
  7466. }
  7467. #endif
  7468. #endif
  7469. // we may be registered for notifications on pidls that are different from
  7470. // the one returned by _GetViewPidl (ftp folder).
  7471. switch (lNotification)
  7472. {
  7473. case SHCNE_DRIVEADD:
  7474. case SHCNE_CREATE:
  7475. case SHCNE_MKDIR:
  7476. pidlItem = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL;
  7477. pidl = _ObjectExists(pidlItem, FALSE);
  7478. if (pidl)
  7479. {
  7480. _AddOrUpdateItem(pidlItem, pidl);
  7481. }
  7482. break;
  7483. case SHCNE_DRIVEREMOVED:
  7484. case SHCNE_DELETE:
  7485. case SHCNE_RMDIR:
  7486. pidlItem = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL;
  7487. if (pidlItem)
  7488. {
  7489. ASSERTMSG(ILFindLastID(pidlItem) == pidlItem, "defview doesnt expect recursive notification");
  7490. _RemoveObject((LPITEMIDLIST)pidlItem, FALSE);
  7491. }
  7492. break;
  7493. case SHCNE_RENAMEITEM:
  7494. case SHCNE_RENAMEFOLDER:
  7495. _OnRename(ppidl);
  7496. break;
  7497. case SHCNE_UPDATEIMAGE:
  7498. // the system image cache is changing
  7499. // ppidl[0] is a IDLIST of image indexs that have changed
  7500. if (ppidl && ppidl[1])
  7501. {
  7502. // this event is generated instead of a normal UPDATEIMAGE so that we can handle the
  7503. // cross process case....
  7504. // handle the notification
  7505. int iImage = SHHandleUpdateImage(ppidl[1]);
  7506. if (iImage != -1)
  7507. {
  7508. _UpdateImage(iImage);
  7509. }
  7510. }
  7511. else if (ppidl && ppidl[0])
  7512. {
  7513. int iImage = *(int UNALIGNED *)((BYTE *)ppidl[0] + 2);
  7514. _UpdateImage(iImage);
  7515. }
  7516. break;
  7517. case SHCNE_ASSOCCHANGED:
  7518. // For this one we will call refresh as we may need to reextract
  7519. // the icons and the like. Later we can optimize this somewhat if
  7520. // we can detect which ones changed and only update those.
  7521. _ReloadContent();
  7522. break;
  7523. case SHCNE_ATTRIBUTES: // these all mean the same thing
  7524. case SHCNE_MEDIAINSERTED:
  7525. case SHCNE_MEDIAREMOVED:
  7526. case SHCNE_NETUNSHARE:
  7527. case SHCNE_NETSHARE:
  7528. case SHCNE_UPDATEITEM:
  7529. if (ppidl)
  7530. {
  7531. LPCITEMIDLIST pidlOld = _pidlMonitor ? ILFindChild(_pidlMonitor, ppidl[0]) : NULL;
  7532. LPITEMIDLIST pidlNew = _ObjectExists(pidlOld, SHCNE_GLOBALEVENTS & lNotification);
  7533. if (pidlNew)
  7534. {
  7535. _AddOrUpdateItem(pidlOld, pidlNew);
  7536. }
  7537. else
  7538. {
  7539. // If we do not have any subobjects and the passed in pidl is the same as
  7540. // this views pidl then refresh all the items.
  7541. LPITEMIDLIST pidlView = _GetViewPidl();
  7542. if (pidlView)
  7543. {
  7544. if (ILIsEqual(ppidl[0], pidlView))
  7545. {
  7546. _FullViewUpdate(SHCNE_UPDATEITEM == lNotification);
  7547. }
  7548. ILFree(pidlView);
  7549. }
  7550. }
  7551. }
  7552. else // ppidl == NULL means update all items (re-enum them)
  7553. {
  7554. _FullViewUpdate(SHCNE_UPDATEITEM == lNotification);
  7555. }
  7556. break;
  7557. case SHCNE_FREESPACE:
  7558. TCHAR szPath[MAX_PATH];
  7559. if (_GetPath(szPath))
  7560. {
  7561. int idDrive = PathGetDriveNumber(szPath);
  7562. if (idDrive != -1)
  7563. {
  7564. DWORD dwChangedDrives = *(DWORD UNALIGNED *)((BYTE *)ppidl[0] + 2);
  7565. if (((1 << idDrive) & dwChangedDrives))
  7566. {
  7567. _UpdateStatusBar(TRUE);
  7568. }
  7569. }
  7570. }
  7571. break;
  7572. default:
  7573. TraceMsg(TF_DEFVIEW, "DefView: unknown FSNotify %08lX, doing full update", lNotification);
  7574. _FullViewUpdate(FALSE);
  7575. break;
  7576. }
  7577. _UpdateStatusBar(FALSE);
  7578. return 0;
  7579. }
  7580. // called when some of our objects get put on the clipboard
  7581. LRESULT CDefView::_OnSetClipboard(BOOL bMove)
  7582. {
  7583. if (bMove) // move
  7584. {
  7585. // mark all selected items as being "cut"
  7586. int i = -1;
  7587. while ((i = ListView_GetNextItem(_hwndListview, i, LVIS_SELECTED)) != -1)
  7588. {
  7589. ListView_SetItemState(_hwndListview, i, LVIS_CUT, LVIS_CUT);
  7590. _bHaveCutStuff = TRUE;
  7591. }
  7592. // join the clipboard viewer chain so we will know when to
  7593. // "uncut" our selected items.
  7594. if (_bHaveCutStuff)
  7595. {
  7596. ASSERT(!_bClipViewer);
  7597. ASSERT(_hwndNextViewer == NULL);
  7598. _hwndNextViewer = SetClipboardViewer(_hwndView);
  7599. _bClipViewer = TRUE;
  7600. }
  7601. }
  7602. return 0;
  7603. }
  7604. // called when the clipboard get changed, clear any items in the "cut" state
  7605. //
  7606. LRESULT CDefView::_OnClipboardChange()
  7607. {
  7608. //
  7609. // if we dont have any cut stuff we dont care.
  7610. //
  7611. if (!_bHaveCutStuff)
  7612. return 0;
  7613. ASSERT(_bClipViewer);
  7614. _RestoreAllGhostedFileView();
  7615. _bHaveCutStuff = FALSE;
  7616. //
  7617. // unhook from the clipboard viewer chain.
  7618. //
  7619. ChangeClipboardChain(_hwndView, _hwndNextViewer);
  7620. _bClipViewer = FALSE;
  7621. _hwndNextViewer = NULL;
  7622. return 0;
  7623. }
  7624. //
  7625. // Note: this function returns the point in Listview Coordinate
  7626. // space. So any hit testing done with this needs to be converted
  7627. // back to Client coordinate space...
  7628. BOOL CDefView::_GetDropPoint(POINT *ppt)
  7629. {
  7630. // Check whether we already have gotten the drop anchor (before any
  7631. // menu processing)
  7632. if (_bDropAnchor)
  7633. {
  7634. // We'll use the insert mark rect (if available) to determine a drop point
  7635. if (!_GetInsertPoint(ppt))
  7636. {
  7637. *ppt = _ptDrop; // Otherwise use _ptDrop
  7638. LVUtil_ClientToLV(_hwndListview, ppt);
  7639. }
  7640. }
  7641. else if (_bMouseMenu)
  7642. {
  7643. *ppt = _ptDragAnchor;
  7644. return TRUE;
  7645. }
  7646. else
  7647. {
  7648. // We need the most up-to-date cursor information, since this
  7649. // may be called during a drop, and the last time the current
  7650. // thread called GetMessage was about 10 minutes ago
  7651. GetCursorPos(ppt);
  7652. LVUtil_ScreenToLV(_hwndListview, ppt);
  7653. }
  7654. return _bDropAnchor;
  7655. }
  7656. // This uses the listview's insertmark to determinie an insert point
  7657. // Returns FALSE if a point could not be determined, TRUE otherwise
  7658. // The coordinates returned are in listview coordinate space.
  7659. BOOL CDefView::_GetInsertPoint(POINT *ppt)
  7660. {
  7661. if (_IsAutoArrange() || (_fs.fFlags & FWF_SNAPTOGRID))
  7662. {
  7663. RECT rcInsert;
  7664. if (ListView_GetInsertMarkRect(_hwndListview, &rcInsert))
  7665. {
  7666. LONG dwStyle = GetWindowLong(_hwndListview, GWL_STYLE);
  7667. BOOL fHorizontal = (_fs.fFlags & FWF_ALIGNLEFT);
  7668. if (fHorizontal)
  7669. {
  7670. ppt->x = (rcInsert.right + rcInsert.left) / 2; // Drop in middle of insertmark rect
  7671. ppt->y = rcInsert.top;
  7672. }
  7673. else
  7674. {
  7675. ppt->x = rcInsert.left;
  7676. ppt->y = (rcInsert.bottom + rcInsert.top) / 2; // Drop in middle of insertmark rect
  7677. }
  7678. return TRUE;
  7679. }
  7680. }
  7681. return FALSE;
  7682. }
  7683. BOOL CDefView::_GetDragPoint(POINT *ppt)
  7684. {
  7685. BOOL fSource = _bDragSource || _bMouseMenu;
  7686. if (fSource)
  7687. {
  7688. // if anchor from mouse activity
  7689. *ppt = _ptDragAnchor;
  7690. }
  7691. else
  7692. {
  7693. // if anchor from keyboard activity... use the focused item
  7694. int i = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
  7695. if (i != -1)
  7696. {
  7697. ListView_GetItemPosition(_hwndListview, i, ppt);
  7698. }
  7699. else
  7700. {
  7701. ppt->x = ppt->y = 0;
  7702. }
  7703. }
  7704. return fSource;
  7705. }
  7706. void CDefView::_PaintErrMsg(HWND hWnd)
  7707. {
  7708. PAINTSTRUCT ps;
  7709. HDC hdc = BeginPaint(hWnd, &ps);
  7710. // if we're in an error state, make sure we're not in webview
  7711. if (_cFrame.IsWebView())
  7712. {
  7713. _SwitchToWebView(FALSE);
  7714. }
  7715. RECT rc;
  7716. GetClientRect(hWnd, &rc);
  7717. DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_SOFT | BF_ADJUST | BF_MIDDLE);
  7718. EndPaint(hWnd, &ps);
  7719. }
  7720. //
  7721. // The default status bar looks like this:
  7722. //
  7723. // No items selected: "nn object(s)" nn = total objects in folder
  7724. // One item selected: <InfoTip for selected item> if item supports InfoTip
  7725. // Else: "nn object(s) selected" nn = num selected objects
  7726. //
  7727. //
  7728. void CDefView::_DoStatusBar(BOOL fInitialize)
  7729. {
  7730. HWND hwndStatus;
  7731. if (_psb && SUCCEEDED(_psb->GetControlWindow(FCW_STATUS, &hwndStatus)) && hwndStatus)
  7732. {
  7733. // Some of the failure cases do not null hwnd...
  7734. UINT uMsg = IDS_FSSTATUSSELECTED;
  7735. if (fInitialize)
  7736. {
  7737. int ciParts[] = {-1};
  7738. SendMessage(hwndStatus, SB_SETPARTS, ARRAYSIZE(ciParts), (LPARAM)ciParts);
  7739. }
  7740. if (_bBkFilling && ListView_GetSelectedCount(_hwndListview) == 0)
  7741. {
  7742. _fBackgroundStatusTextValid = FALSE;
  7743. LPWSTR pszStatus = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_FSSTATUSSEARCHING));
  7744. // We are not checking if the alloc succeeded in ShellConstructMessageString since both
  7745. // SendMessage and LocalFree can take NULL as inputs.
  7746. SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatus);
  7747. LocalFree((void *)pszStatus);
  7748. }
  7749. else
  7750. {
  7751. LPCITEMIDLIST *apidl = NULL;
  7752. int nMsgParam = ListView_GetSelectedCount(_hwndListview);
  7753. switch (nMsgParam)
  7754. {
  7755. case 0:
  7756. // No objects selected; show total item count
  7757. nMsgParam = ListView_GetItemCount(_hwndListview);
  7758. uMsg = IDS_FSSTATUSBASE;
  7759. break;
  7760. case 1:
  7761. UINT cItems;
  7762. GetSelectedObjects(&apidl, &cItems);
  7763. break;
  7764. }
  7765. LPITEMIDLIST pidlFolder = _GetViewPidl();
  7766. if (pidlFolder)
  7767. {
  7768. CStatusBarAndInfoTipTask *pTask;
  7769. if (SUCCEEDED(CStatusBarAndInfoTipTask_CreateInstance(pidlFolder, apidl ? *apidl : NULL, uMsg, nMsgParam, NULL, _hwndView, _pScheduler, &pTask)))
  7770. {
  7771. if (_pScheduler)
  7772. {
  7773. // make sure there are no other status bar background tasks going on...
  7774. _pScheduler->RemoveTasks(TOID_DVBackgroundStatusBar, ITSAT_DEFAULT_LPARAM, FALSE);
  7775. }
  7776. _fBackgroundStatusTextValid = TRUE;
  7777. _AddTask(pTask, TOID_DVBackgroundStatusBar, 0, TASK_PRIORITY_INFOTIP, ADDTASK_ATEND);
  7778. pTask->Release();
  7779. }
  7780. ILFree(pidlFolder);
  7781. }
  7782. if (apidl)
  7783. LocalFree(apidl);
  7784. }
  7785. }
  7786. }
  7787. void CDefView::_OnWinIniChangeDesktop(WPARAM wParam, LPCTSTR pszSection)
  7788. {
  7789. if (pszSection)
  7790. {
  7791. if (!lstrcmpi(pszSection, TEXT("ToggleDesktop")))
  7792. {
  7793. _OnCommand(NULL, SFVIDM_DESKTOPHTML_WEBCONTENT, 0);
  7794. }
  7795. else if (!lstrcmpi(pszSection, TEXT("RefreshDesktop")))
  7796. {
  7797. if (FAILED(Refresh()))
  7798. {
  7799. SHELLSTATE ss;
  7800. //Refresh failed because the new template didn't exist
  7801. //Toggle the Registry settings back to Icons-only mode!
  7802. ss.fDesktopHTML = FALSE;
  7803. SHGetSetSettings(&ss, SSF_DESKTOPHTML, TRUE); // Write back the new
  7804. }
  7805. }
  7806. else if (!lstrcmpi(pszSection, TEXT("BufferedRefresh")))
  7807. {
  7808. //See if we have already started a timer to refresh
  7809. if (!_fRefreshBuffered)
  7810. {
  7811. TraceMsg(TF_DEFVIEW, "A Buffered refresh starts the timer");
  7812. SetTimer(_hwndView, DV_IDTIMER_BUFFERED_REFRESH, 5000, NULL); // 5 sec
  7813. _fRefreshBuffered = TRUE;
  7814. }
  7815. else //If refresh is already buffered, don't do anything!
  7816. {
  7817. TraceMsg(TF_DEFVIEW, "A buffered refresh occured while another is pending");
  7818. }
  7819. }
  7820. else
  7821. {
  7822. if (wParam == SPI_SETDESKWALLPAPER || wParam == SPI_SETDESKPATTERN)
  7823. {
  7824. _SetFolderColors();
  7825. }
  7826. }
  7827. }
  7828. else
  7829. {
  7830. switch(wParam)
  7831. {
  7832. case SPI_SETDESKWALLPAPER:
  7833. case SPI_SETDESKPATTERN:
  7834. _SetFolderColors();
  7835. break;
  7836. case SPI_ICONHORIZONTALSPACING:
  7837. case SPI_ICONVERTICALSPACING:
  7838. if (_IsDesktop())
  7839. {
  7840. DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(_hwndListview);
  7841. UpdateGridSizes(TRUE, _hwndListview, 0, NULL, BOOLIFY(dwLVExStyle & LVS_EX_SNAPTOGRID));
  7842. }
  7843. break;
  7844. }
  7845. }
  7846. }
  7847. void CDefView::_OnWinIniChange(WPARAM wParam, LPCTSTR pszSection)
  7848. {
  7849. if ((wParam == SPI_GETICONTITLELOGFONT) ||
  7850. ((wParam == 0) && pszSection && !lstrcmpi(pszSection, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IconUnderline"))))
  7851. {
  7852. _UpdateUnderlines();
  7853. }
  7854. if (pszSection && !lstrcmpi(pszSection, TEXT("VisualEffects")))
  7855. {
  7856. Refresh();
  7857. }
  7858. // Why all this code? It's a rare event -- just kick off a refresh...
  7859. if (!wParam || (pszSection && !lstrcmpi(pszSection, TEXT("intl"))))
  7860. {
  7861. // has the time format changed while we're in details mode?
  7862. if (ViewRequiresColumns(_fs.ViewMode) && !_IsOwnerData())
  7863. {
  7864. InvalidateRect(_hwndListview, NULL, TRUE);
  7865. // 99/04/13 #320903 vtan: If the date format has changed then iterate
  7866. // the entire list looking for extended columns of type date and
  7867. // resetting them to LPSTR_TEXTCALLBACK effectively dumping the cache.
  7868. // For performance improvement it's possible to collect an array of
  7869. // visible columns and reset that array. It will still involve TWO
  7870. // for loops.
  7871. int iItemCount = ListView_GetItemCount(_hwndListview);
  7872. for (int iItem = 0; iItem < iItemCount; ++iItem)
  7873. {
  7874. for (UINT uiRealColumn = 0; uiRealColumn < _vs.GetColumnCount(); ++uiRealColumn)
  7875. {
  7876. DWORD dwFlags = _vs.GetColumnState(uiRealColumn);
  7877. if (((dwFlags & SHCOLSTATE_EXTENDED) != 0) &&
  7878. ((dwFlags & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_DATE))
  7879. {
  7880. UINT uiVisibleColumn = _RealToVisibleCol(uiRealColumn);
  7881. ListView_SetItemText(_hwndListview, iItem, uiVisibleColumn, LPSTR_TEXTCALLBACK);
  7882. }
  7883. }
  7884. }
  7885. }
  7886. }
  7887. //
  7888. // we may need to rebuild the icon cache.
  7889. //
  7890. if (wParam == SPI_SETICONMETRICS ||
  7891. wParam == SPI_SETNONCLIENTMETRICS)
  7892. {
  7893. if (_IsImageMode())
  7894. {
  7895. _SetThumbview();
  7896. }
  7897. else if (_IsTileMode())
  7898. {
  7899. _SetTileview();
  7900. }
  7901. else
  7902. {
  7903. _SetSysImageList();
  7904. }
  7905. }
  7906. //
  7907. // we need to invalidate the cursor cache
  7908. //
  7909. if (wParam == SPI_SETCURSORS)
  7910. {
  7911. DAD_InvalidateCursors();
  7912. }
  7913. if ((wParam == SPI_SETMENUANIMATION) && _pDUIView)
  7914. {
  7915. _pDUIView->ManageAnimations(FALSE);
  7916. }
  7917. if (!wParam && !pszSection && _pDUIView)
  7918. {
  7919. if (_fBarrierDisplayed != _QueryBarricadeState())
  7920. {
  7921. _fBarrierDisplayed = !_fBarrierDisplayed;
  7922. _pDUIView->EnableBarrier (_fBarrierDisplayed);
  7923. }
  7924. }
  7925. if (_IsDesktop())
  7926. {
  7927. _OnWinIniChangeDesktop(wParam, pszSection);
  7928. }
  7929. }
  7930. void CDefView::_SetDefaultViewSettings()
  7931. {
  7932. // only do this if we've actually shown the view...
  7933. // (ie, there's no _hwndStatic)
  7934. // and we're not the desktop
  7935. // and we're not an exstended view
  7936. // and we are not in an explorer (tree pane on)
  7937. if (!_hwndStatic && !_IsDesktop() && !IsExplorerBrowser(_psb))
  7938. {
  7939. SHELLSTATE ss;
  7940. ss.lParamSort = (LONG)_vs._lParamSort;
  7941. ss.iSortDirection = _vs._iDirection;
  7942. SHGetSetSettings(&ss, SSF_SORTCOLUMNS, TRUE);
  7943. }
  7944. }
  7945. HWND CDefView::GetChildViewWindow()
  7946. {
  7947. if (_cFrame.IsWebView())
  7948. return _cFrame.GetExtendedViewWindow();
  7949. return _hwndListview;
  7950. }
  7951. void CDefView::_SetFocus()
  7952. {
  7953. // if it's a combined view then we need to give focus to listview
  7954. if (!_fCombinedView && _cFrame.IsWebView() && !_fActivateLV)
  7955. {
  7956. _OnViewWindowActive();
  7957. if (_cFrame._pOleObj)
  7958. {
  7959. MSG msg = {_hwndView, WM_KEYDOWN, VK_TAB, 0xf0001};
  7960. // HACKHACK!!! MUST set state here! idealy shbrowse should call
  7961. // UIActivate on the view but that breaks dochost stuff.
  7962. // if we did not set the state here, trident would call
  7963. // CSFVSite::ActivateMe that would not forward the call to obj::UIActivate
  7964. // and therefore nothing would get focus (actually trident would have it
  7965. // but it would not be visible). Note that this behavior happens only
  7966. // second time around, i.e. on init UIActivate is called and everything
  7967. // works fine, but if we tab from address bar onto the view, that's when
  7968. // the stuff gets broken.
  7969. OnActivate(SVUIA_ACTIVATE_FOCUS);
  7970. _cFrame._UIActivateIO(TRUE, &msg);
  7971. }
  7972. }
  7973. else
  7974. {
  7975. CallCB(SFVM_SETFOCUS, 0, 0);
  7976. if (_hwndListview)
  7977. SetFocus(_hwndListview);
  7978. if (!_IsDesktop())
  7979. {
  7980. _cFrame._uState = SVUIA_ACTIVATE_FOCUS;
  7981. }
  7982. }
  7983. }
  7984. LRESULT CALLBACK CDefView::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  7985. {
  7986. LRESULT lres = 0;
  7987. CDefView * pThis;
  7988. ULONG_PTR cookie = 0;
  7989. if (WM_NCCREATE == uMsg)
  7990. {
  7991. pThis = (CDefView*)((LPCREATESTRUCT)lParam)->lpCreateParams;
  7992. if (pThis)
  7993. {
  7994. pThis->AddRef();
  7995. SetWindowLongPtr(hWnd, 0, (LONG_PTR)pThis);
  7996. }
  7997. }
  7998. else
  7999. {
  8000. pThis = (CDefView*)GetWindowLongPtr(hWnd, 0);
  8001. }
  8002. // FUSION: When defview calls out to 3rd party code we want it to use
  8003. // the process default context. This means that the 3rd party code will get
  8004. // v5 in the explorer process. However, if shell32 is hosted in a v6 process,
  8005. // then the 3rd party code will still get v6.
  8006. // Future enhancements to this codepath may include using the fusion manifest
  8007. // tab <noinherit> which basically surplants the activat(null) in the following
  8008. // codepath. This disables the automatic activation from user32 for the duration
  8009. // of this wndproc, essentially doing this null push.
  8010. ActivateActCtx(NULL, &cookie);
  8011. // we need to use a __try{}__finally{} block here to make sure that we de-activate
  8012. // the activation context. Fusion pushes and pops these off a stack and if we leave one
  8013. // around we will basically infect this thread with the wrong context.
  8014. __try
  8015. {
  8016. if (pThis)
  8017. {
  8018. lres = pThis->WndProc(hWnd, uMsg, wParam, lParam);
  8019. }
  8020. else
  8021. {
  8022. lres = DefWindowProc(hWnd, uMsg, wParam, lParam);
  8023. }
  8024. }
  8025. __finally
  8026. {
  8027. if (cookie != 0)
  8028. {
  8029. DeactivateActCtx(0, cookie);
  8030. }
  8031. }
  8032. return lres;
  8033. }
  8034. BOOL CDefView::_OnAppCommand(UINT cmd, UINT uDevice, DWORD dwKeys)
  8035. {
  8036. BOOL bHandled = FALSE;
  8037. switch (cmd)
  8038. {
  8039. case APPCOMMAND_MEDIA_PLAY_PAUSE:
  8040. if (S_OK == _InvokeContextMenuVerbOnSelection("play", 0, 0))
  8041. bHandled = TRUE;
  8042. break;
  8043. }
  8044. return bHandled;
  8045. }
  8046. HRESULT CDefView::_ForwardMenuMessages(DWORD dwID, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult, BOOL* pfHandled)
  8047. {
  8048. if (InRange(dwID, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST))
  8049. {
  8050. if (pfHandled)
  8051. *pfHandled = TRUE;
  8052. return SHForwardContextMenuMsg(_pcmContextMenuPopup, uMsg, wParam, lParam, plResult, TRUE);
  8053. }
  8054. else if (InRange(dwID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
  8055. {
  8056. if (pfHandled)
  8057. *pfHandled = TRUE;
  8058. return SHForwardContextMenuMsg(_pcmFile, uMsg, wParam, lParam, plResult, TRUE);
  8059. }
  8060. if (pfHandled)
  8061. *pfHandled = FALSE;
  8062. return E_FAIL;
  8063. }
  8064. LRESULT CDefView::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  8065. {
  8066. LRESULT l;
  8067. DWORD dwID;
  8068. switch (uMsg)
  8069. {
  8070. // IShellBrowser forwards these to the IShellView.
  8071. // Dochost also forwards them down to the IOleObject, so we should do it too...
  8072. case WM_SYSCOLORCHANGE:
  8073. {
  8074. HDITEM hdi = {HDI_FORMAT, 0, NULL, NULL, 0, 0, 0, 0, 0};
  8075. HWND hwndHead = ListView_GetHeader(_hwndListview);
  8076. // We only want to update the sort arrows if they are already present.
  8077. if (hwndHead)
  8078. {
  8079. Header_GetItem(hwndHead, _vs._lParamSort, &hdi);
  8080. if (hdi.fmt & HDF_BITMAP)
  8081. _SetSortFeedback();
  8082. }
  8083. // fall through
  8084. }
  8085. case WM_WININICHANGE:
  8086. _sizeThumbnail.cx = -1;
  8087. // fall through
  8088. case WM_ENTERSIZEMOVE:
  8089. case WM_EXITSIZEMOVE:
  8090. case WM_FONTCHANGE:
  8091. if (_cFrame.IsWebView())
  8092. {
  8093. HWND hwndExt = _cFrame.GetExtendedViewWindow();
  8094. if (hwndExt)
  8095. {
  8096. SendMessage(hwndExt, uMsg, wParam, lParam);
  8097. }
  8098. }
  8099. break;
  8100. }
  8101. switch (uMsg)
  8102. {
  8103. case WM_DESTROY:
  8104. if (GetKeyState(VK_CONTROL) < 0)
  8105. _SetDefaultViewSettings();
  8106. // Dont need our web view data any more
  8107. _FreeWebViewContentData();
  8108. // We don't flush these on WM_EXITMENULOOP any more, so do it here
  8109. IUnknown_SetSite(_pcmFile, NULL);
  8110. ATOMICRELEASE(_pcmFile);
  8111. EmptyBkgrndThread(_pScheduler);
  8112. ATOMICRELEASE(_pScheduler);
  8113. // do this after our task scheduler is gone, since one of it's
  8114. // items may be on the background task scheduler (or DUI may be
  8115. // talking to it on the background) and it may need it's site chain.
  8116. IUnknown_SetSite(_cCallback.GetSFVCB(), NULL);
  8117. if (_pDiskCache)
  8118. {
  8119. // at this point we assume that we have no lock,
  8120. _pDiskCache->Close(NULL);
  8121. ATOMICRELEASE(_pDiskCache);
  8122. }
  8123. // Depending on when it is closed we may have an outstanding post
  8124. // to us about the rest of the fill data which we should try to
  8125. // process in order to keep from leaking stuff...
  8126. // logically hWnd == _hwndView, but we already zeroed
  8127. // _hwndView so use hWnd
  8128. _ClearPostedMsgs(hWnd);
  8129. //
  8130. // remove ourself as a clipboard viewer
  8131. //
  8132. if (_bClipViewer)
  8133. {
  8134. ChangeClipboardChain(hWnd, _hwndNextViewer);
  8135. _bClipViewer = FALSE;
  8136. _hwndNextViewer = NULL;
  8137. }
  8138. if (_uRegister)
  8139. {
  8140. ULONG uRegister = _uRegister;
  8141. _uRegister = 0;
  8142. SHChangeNotifyDeregister(uRegister);
  8143. }
  8144. ATOMICRELEASE(_psd);
  8145. ATOMICRELEASE(_pdtgtBack);
  8146. if (_hwndListview)
  8147. {
  8148. if (_IsDesktop()) // only the desktop can have a combined view (e.g. Active Desktop)
  8149. {
  8150. EnableCombinedView(this, FALSE);
  8151. }
  8152. if (_bRegisteredDragDrop)
  8153. RevokeDragDrop(_hwndListview);
  8154. }
  8155. SetAutomationObject(NULL); // cleanup refs we may be holding
  8156. if (IsWindow(_hwndInfotip))
  8157. {
  8158. DestroyWindow(_hwndInfotip);
  8159. _hwndInfotip = NULL;
  8160. }
  8161. break;
  8162. case WM_CREATE:
  8163. return _OnCreate(hWnd);
  8164. case WM_DSV_DELAYED_DESTROYWND:
  8165. DestroyWindow(hWnd);
  8166. break;
  8167. case WM_NCDESTROY:
  8168. _hwndView = NULL;
  8169. SetWindowLongPtr(hWnd, 0, 0);
  8170. // get rid of extra junk in the icon cache
  8171. IconCacheFlush(FALSE);
  8172. if (_pDUIView)
  8173. {
  8174. //
  8175. // We must uninitialize DUser prior to releasing
  8176. // _pDUIView so that all DUser gadgets are properly destroyed.
  8177. // We used to call DirectUI::UnInitThread() in the CDUIView dtor.
  8178. // However, since both CDefView and the various 'task' DUI
  8179. // elements maintain a ref to CDUIView, we got into scenarios where
  8180. // one of the 'task' elements held the final ref to CDUIView. That
  8181. // resulted in the destruction of that 'task' element causing
  8182. // uninitialization of DUser in the middle of a DUser call stack.
  8183. // That's bad.
  8184. // Uninitializing DUser here causes DUser to handle all pending
  8185. // messages and destroy all it's gadgets on it's own terms.
  8186. //
  8187. _pDUIView->UnInitializeDirectUI();
  8188. _pDUIView->Release();
  8189. _pDUIView = NULL;
  8190. }
  8191. // release our reference generated during WM_NCCREATE in static wndproc
  8192. Release();
  8193. break;
  8194. case WM_ENABLE:
  8195. _fDisabled = !wParam;
  8196. break;
  8197. case WM_ERASEBKGND:
  8198. {
  8199. COLORREF cr = ListView_GetBkColor(_hwndListview);
  8200. if (cr == CLR_NONE)
  8201. return SendMessage(_hwndMain, uMsg, wParam, lParam);
  8202. //Turning On EraseBkgnd. This is required so as to avoid the
  8203. //painting issue - when the listview is not visible and
  8204. //invalidation occurs.
  8205. HBRUSH hbr = CreateSolidBrush(cr);
  8206. RECT rc;
  8207. GetClientRect(hWnd, &rc);
  8208. FillRect((HDC)wParam, &rc, hbr);
  8209. DeleteObject(hbr);
  8210. }
  8211. // We want to reduce flash
  8212. return 1;
  8213. case WM_PAINT:
  8214. if (_fEnumFailed)
  8215. _PaintErrMsg(hWnd);
  8216. else
  8217. goto DoDefWndProc;
  8218. break;
  8219. case WM_LBUTTONUP:
  8220. if (_fEnumFailed)
  8221. PostMessage(hWnd, WM_KEYDOWN, (WPARAM)VK_F5, 0);
  8222. else
  8223. goto DoDefWndProc;
  8224. break;
  8225. case WM_SETFOCUS:
  8226. if (!_fDestroying) // Ignore if we are destroying _hwndView.
  8227. {
  8228. _SetFocus();
  8229. }
  8230. break;
  8231. case WM_MOUSEACTIVATE:
  8232. //
  8233. // this keeps our window from coming to the front on button down
  8234. // instead, we activate the window on the up click
  8235. //
  8236. if (LOWORD(lParam) != HTCLIENT)
  8237. goto DoDefWndProc;
  8238. LV_HITTESTINFO lvhti;
  8239. GetCursorPos(&lvhti.pt);
  8240. ScreenToClient(_hwndListview, &lvhti.pt);
  8241. ListView_HitTest(_hwndListview, &lvhti);
  8242. if (lvhti.iItem != -1 && lvhti.flags & LVHT_ONITEM)
  8243. return MA_NOACTIVATE;
  8244. else
  8245. return MA_ACTIVATE;
  8246. case WM_ACTIVATE:
  8247. // force update on inactive to not ruin save bits
  8248. if (wParam == WA_INACTIVE)
  8249. UpdateWindow(_hwndListview);
  8250. // if active view created, call active object to allow it to visualize activation.
  8251. if (_cFrame._pActive)
  8252. _cFrame._pActive->OnFrameWindowActivate((BOOL)wParam);
  8253. break;
  8254. case WM_SIZE:
  8255. return WndSize(hWnd);
  8256. case WM_NOTIFY:
  8257. {
  8258. #ifdef DEBUG
  8259. // DefView_OnNotify sometimes destroys the pnm, so we need to save
  8260. // the code while we can. (E.g., common dialog single-click activate.
  8261. // LVN_ITEMACTIVATE causes us to dismiss the common dialog, which
  8262. // does a DestroyViewWindow, which destroys the ListView
  8263. // which destroys the NMHDR!)
  8264. UINT code = ((NMHDR *)lParam)->code;
  8265. #endif
  8266. AddRef(); // just in case
  8267. l = _OnNotify((NMHDR *)lParam);
  8268. Release(); // release
  8269. return l;
  8270. }
  8271. case WM_CONTEXTMENU:
  8272. if (!_fDisabled)
  8273. {
  8274. if (lParam != (LPARAM) -1)
  8275. {
  8276. _bMouseMenu = TRUE;
  8277. _ptDragAnchor.x = GET_X_LPARAM(lParam);
  8278. _ptDragAnchor.y = GET_Y_LPARAM(lParam);
  8279. LVUtil_ScreenToLV(_hwndListview, &_ptDragAnchor);
  8280. }
  8281. // Note: in deview inside a defview we can have problems of the
  8282. // parent destroying us when we change views, so we better addref/release
  8283. // around this...
  8284. AddRef();
  8285. _bContextMenuMode = TRUE;
  8286. ContextMenu((DWORD) lParam);
  8287. _bContextMenuMode = FALSE;
  8288. _bMouseMenu = FALSE;
  8289. Release();
  8290. }
  8291. break;
  8292. case WM_COMMAND:
  8293. return _OnCommand(NULL, wParam, lParam);
  8294. case WM_APPCOMMAND:
  8295. if (!_OnAppCommand(GET_APPCOMMAND_LPARAM(lParam), GET_DEVICE_LPARAM(lParam), GET_KEYSTATE_LPARAM(lParam)))
  8296. goto DoDefWndProc;
  8297. break;
  8298. case WM_DSV_DISABLEACTIVEDESKTOP:
  8299. DisableActiveDesktop();
  8300. break;
  8301. case WM_DSV_DELAYWINDOWCREATE:
  8302. CallCB(SFVM_DELAYWINDOWCREATE, (WPARAM)_hwndView, 0);
  8303. break;
  8304. case WM_DSV_BACKGROUNDENUMDONE:
  8305. // Make sure this notify is from our enumeration task (it could be from a previous one)
  8306. if (lParam == _dwEnumId)
  8307. _OnBackgroundEnumDone();
  8308. break;
  8309. case WM_DSV_GROUPINGDONE:
  8310. _OnCategoryTaskDone();
  8311. break;
  8312. case WM_DSV_FILELISTENUMDONE:
  8313. _OnEnumDoneMessage();
  8314. break;
  8315. case WM_DSV_FILELISTFILLDONE:
  8316. _ShowSearchUI(FALSE);
  8317. break;
  8318. case WM_DSV_UPDATETHUMBNAIL:
  8319. {
  8320. DSV_UPDATETHUMBNAIL* putn = (DSV_UPDATETHUMBNAIL*)lParam;
  8321. if (_IsImageMode()) // some messages may come in after the view mode is changed.
  8322. {
  8323. _UpdateThumbnail(putn->iItem, putn->iImage, putn->pidl);
  8324. }
  8325. _CleanupUpdateThumbnail(putn);
  8326. }
  8327. break;
  8328. case WM_DSV_POSTCREATEINFOTIP:
  8329. _OnPostCreateInfotip((TOOLINFO *)wParam, lParam);
  8330. break;
  8331. case WM_DSV_FSNOTIFY:
  8332. {
  8333. LPITEMIDLIST *ppidl;
  8334. LONG lEvent;
  8335. LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  8336. if (pshcnl)
  8337. {
  8338. if (_fDisabled ||
  8339. (CallCB(SFVM_FSNOTIFY, (WPARAM)ppidl, (LPARAM)lEvent) == S_FALSE))
  8340. {
  8341. lParam = 0;
  8342. }
  8343. else
  8344. {
  8345. lParam = _OnFSNotify(lEvent, (LPCITEMIDLIST*)ppidl);
  8346. }
  8347. SHChangeNotification_Unlock(pshcnl);
  8348. }
  8349. }
  8350. return lParam;
  8351. // the background thread's callback will post this message to us
  8352. // when it has finished extracting a icon in the background.
  8353. //
  8354. // wParam is PIDL
  8355. // lParam is iIconIndex
  8356. case WM_DSV_UPDATEICON:
  8357. _UpdateIcon((LPITEMIDLIST)wParam, (UINT)lParam);
  8358. break;
  8359. case WM_DSV_UPDATECOLDATA:
  8360. _UpdateColData((CBackgroundColInfo*)lParam);
  8361. break;
  8362. case WM_DSV_UPDATEOVERLAY:
  8363. _UpdateOverlay((int)wParam, (int)lParam);
  8364. break;
  8365. case WM_DSV_SETIMPORTANTCOLUMNS:
  8366. _SetImportantColumns((CBackgroundTileInfo*)lParam);
  8367. break;
  8368. case WM_DSV_SHOWDRAGIMAGE:
  8369. return DAD_ShowDragImage((BOOL)lParam);
  8370. case WM_DSV_DELAYSTATUSBARUPDATE:
  8371. {
  8372. HWND hwndStatus;
  8373. LPWSTR pszStatus = (LPWSTR)lParam;
  8374. if (_fBackgroundStatusTextValid)
  8375. {
  8376. _fBackgroundStatusTextValid = FALSE;
  8377. // Now prepare the text and post it to the status bar window.
  8378. _psb->GetControlWindow(FCW_STATUS, &hwndStatus);
  8379. if (hwndStatus)
  8380. {
  8381. SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatus);
  8382. }
  8383. }
  8384. LocalFree((void *)pszStatus);
  8385. }
  8386. break;
  8387. case WM_DSV_DELAYINFOTIP:
  8388. if ((CBackgroundInfoTip *)wParam == _pBackgroundInfoTip && _pBackgroundInfoTip->_fReady)
  8389. {
  8390. LRESULT lRet = SendMessage(_hwndListview, LVM_SETINFOTIP, NULL, (LPARAM)&_pBackgroundInfoTip->_lvSetInfoTip);
  8391. ATOMICRELEASE(_pBackgroundInfoTip);
  8392. return lRet;
  8393. }
  8394. break;
  8395. case WM_DSV_ENSURE_COLUMNS_LOADED:
  8396. if (!_fDestroying)
  8397. {
  8398. AddColumns();
  8399. return 1;
  8400. }
  8401. break;
  8402. case GET_WM_CTLCOLOR_MSG(CTLCOLOR_STATIC):
  8403. SetBkColor(GET_WM_CTLCOLOR_HDC(wParam, lParam, uMsg),
  8404. GetSysColor(COLOR_WINDOW));
  8405. return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
  8406. case WM_DRAWCLIPBOARD:
  8407. if (_hwndNextViewer != NULL)
  8408. SendMessage(_hwndNextViewer, uMsg, wParam, lParam);
  8409. if (_bClipViewer)
  8410. return _OnClipboardChange();
  8411. break;
  8412. case WM_CHANGECBCHAIN:
  8413. if ((HWND)wParam == _hwndNextViewer)
  8414. {
  8415. _hwndNextViewer = (HWND)lParam;
  8416. return TRUE;
  8417. }
  8418. if (_hwndNextViewer != NULL)
  8419. return SendMessage(_hwndNextViewer, uMsg, wParam, lParam);
  8420. break;
  8421. case WM_WININICHANGE:
  8422. _OnWinIniChange(wParam, (LPCTSTR)lParam);
  8423. SendMessage(_hwndListview, uMsg, wParam, lParam);
  8424. break;
  8425. case WM_THEMECHANGED:
  8426. PostMessage(_hwndView, WM_COMMAND, (WPARAM)SFVIDM_MISC_REFRESH, 0);
  8427. break;
  8428. case WM_SHELLNOTIFY:
  8429. #define SHELLNOTIFY_SETDESKWALLPAPER 0x0004
  8430. if (wParam == SHELLNOTIFY_SETDESKWALLPAPER)
  8431. {
  8432. if (_IsDesktop())
  8433. {
  8434. _fHasDeskWallPaper = (lParam != 0);
  8435. _SetFolderColors();
  8436. }
  8437. }
  8438. break;
  8439. // What we would like out of these menu messages:
  8440. // WM_ENTERMENULOOP
  8441. // WM_INITMENUPOPUP
  8442. // for File.Edit.View...: handle ourselves (merge in _pcmFile etc) and forward to IShellFolderViewCB for init
  8443. // for submenus or context menus: forward to whatever IContextMenu owns the popup
  8444. // WM_INITMENUPOPUP for next menu, etc
  8445. // WM_EXITMENULOOP
  8446. // PostMessage(WM_DSV_MENUTERM)
  8447. // WM_COMMAND comes in, if a menu item was selected
  8448. // Forward to the correct object to handle
  8449. // WM_DSV_MENUTERM
  8450. // clean up File.Edit.View... (release _pcmFile etc), and forward to IShellFolderViewCB for cleanup
  8451. //
  8452. // From previous comments here, it sounds like we don't get proper WM_ENTERMENULOOP / WM_EXITMENULOOP.
  8453. // I suspect this is a behavior change since Win95. (This probably happened when we changed
  8454. // the browser's HMENU to our own custom menu bar implementation way back in IE4...)
  8455. //
  8456. // Previous code also posted WM_DSV_MENUTERM *twice* -- another relic from the Edit menu days...
  8457. //
  8458. // If we try to clean up on WM_EXITMENULOOP, then we'll free _pcmFile etc when
  8459. // the File menu closes. This caused us problems when we tried to merge _pcmFile
  8460. // into the Edit menu. (We should have used _pcmEdit and cleaned up on WM_UNINITMENUPOPUP.)
  8461. // This is no longer a problem for defview, but it is a problem for the IShellFolderViewCB
  8462. // which can merge into any of File.Edit.View... menus. (In fact, no code in the source tree
  8463. // does anything on SFVM_EXITMENULOOP.)
  8464. //
  8465. // We could free up _pcmFile early (when the File menu goes away) if we want,
  8466. // but there doesn't seem to be any harm in letting it sit around.
  8467. // So rip out this unused WM_EXITMENULOOP/WM_DSVMENUTERM/_OnMenuTermination code.
  8468. //
  8469. case WM_INITMENU:
  8470. _OnInitMenu();
  8471. break;
  8472. case WM_INITMENUPOPUP:
  8473. _OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
  8474. break;
  8475. case WM_TIMER:
  8476. KillTimer(hWnd, (UINT) wParam);
  8477. // Ignore if we're in the middle of destroying the window
  8478. if (_fDestroying)
  8479. break;
  8480. if (DV_IDTIMER_START_ANI == wParam)
  8481. {
  8482. if (_hwndStatic)
  8483. {
  8484. WCHAR szName[128];
  8485. HINSTANCE hinst;
  8486. // used only by camera namespace; they have a const string < 128 ch
  8487. if (S_OK != CallCB(SFVM_GETANIMATION, (WPARAM)&hinst, (LPARAM)szName))
  8488. {
  8489. hinst = g_hinst;
  8490. StrCpyNW(szName, L"#150", ARRAYSIZE(szName));
  8491. }
  8492. HWND hAnimate = ::GetWindow (_hwndStatic, GW_CHILD);
  8493. if (hAnimate)
  8494. {
  8495. // Animate_OpenEx() except we want the W version always
  8496. SendMessage(hAnimate, ACM_OPENW, (WPARAM)hinst, (LPARAM)szName);
  8497. }
  8498. }
  8499. }
  8500. else if (DV_IDTIMER_BUFFERED_REFRESH == wParam)
  8501. {
  8502. if (_fRefreshBuffered)
  8503. {
  8504. _fRefreshBuffered = FALSE;
  8505. PostMessage(_hwndView, WM_KEYDOWN, (WPARAM)VK_F5, 0);
  8506. TraceMsg(TF_DEFVIEW, "Buffered Refresh timer causes actual refresh");
  8507. }
  8508. }
  8509. else if (DV_IDTIMER_NOTIFY_AUTOMATION_SELCHANGE == wParam)
  8510. {
  8511. _OnDelayedSelectionChange();
  8512. }
  8513. else if (DV_IDTIMER_NOTIFY_AUTOMATION_CONTENTSCHANGED == wParam)
  8514. {
  8515. _OnDelayedContentsChanged();
  8516. }
  8517. else if (DV_IDTIMER_DISKCACHE == wParam)
  8518. {
  8519. DWORD dwMode;
  8520. if (_pDiskCache->GetMode(&dwMode) == S_OK && _pDiskCache->IsLocked() == S_FALSE)
  8521. {
  8522. // two seconds since last access, close the cache.
  8523. _pDiskCache->Close(NULL);
  8524. }
  8525. if (_GetBackgroundTaskCount(TOID_NULL) == 0)
  8526. {
  8527. // there is nothing in the queue pending, so quit listening...
  8528. KillTimer(hWnd, DV_IDTIMER_DISKCACHE);
  8529. }
  8530. break;
  8531. }
  8532. else if (DV_IDTIMER_SCROLL_TIMEOUT == wParam)
  8533. {
  8534. // Scroll timer expired.
  8535. TraceMsg(TF_DEFVIEW, "SCROLL TIMEOUT");
  8536. _fScrolling = FALSE;
  8537. // Now we send a paint to listview, so it will send us more requests for tileinformation
  8538. // that we ignored during scrolling.
  8539. if (_fRequestedTileDuringScroll)
  8540. {
  8541. InvalidateRect(_hwndListview, NULL, FALSE);
  8542. }
  8543. }
  8544. else
  8545. {
  8546. ASSERT(FALSE); // nobody is handling this timer id!
  8547. }
  8548. break;
  8549. case WM_SETCURSOR:
  8550. if (_hwndStatic)
  8551. {
  8552. SetCursor(LoadCursor(NULL, IDC_WAIT));
  8553. return TRUE;
  8554. }
  8555. goto DoDefWndProc;
  8556. case WM_DRAWITEM:
  8557. #define lpdis ((LPDRAWITEMSTRUCT)lParam)
  8558. dwID = lpdis->itemID;
  8559. if (lpdis->CtlType != ODT_MENU)
  8560. return 0;
  8561. if (InRange(lpdis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB())
  8562. {
  8563. CallCB(SFVM_DRAWITEM, SFVIDM_CLIENT_FIRST, lParam);
  8564. return 1;
  8565. }
  8566. else
  8567. {
  8568. LRESULT lResult = 0;
  8569. _ForwardMenuMessages(dwID, uMsg, wParam, lParam, &lResult, NULL);
  8570. return lResult;
  8571. }
  8572. #undef lpdis
  8573. case WM_MEASUREITEM:
  8574. #define lpmis ((LPMEASUREITEMSTRUCT)lParam)
  8575. dwID = lpmis->itemID;
  8576. if (lpmis->CtlType != ODT_MENU)
  8577. return 0;
  8578. if (InRange(lpmis->itemID, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB())
  8579. {
  8580. CallCB(SFVM_MEASUREITEM, SFVIDM_CLIENT_FIRST, lParam);
  8581. return 1;
  8582. }
  8583. else
  8584. {
  8585. LRESULT lResult = 0;
  8586. _ForwardMenuMessages(dwID, uMsg, wParam, lParam, &lResult, NULL);
  8587. return lResult;
  8588. }
  8589. case WM_MENUCHAR:
  8590. if (_pcmFile)
  8591. {
  8592. LRESULT lResult;
  8593. HRESULT hr = SHForwardContextMenuMsg(_pcmFile, uMsg, wParam, lParam, &lResult, FALSE);
  8594. if (hr == S_OK)
  8595. return lResult;
  8596. }
  8597. if (_pcmContextMenuPopup)
  8598. {
  8599. LRESULT lResult;
  8600. HRESULT hr = SHForwardContextMenuMsg(_pcmContextMenuPopup, uMsg, wParam, lParam, &lResult, FALSE);
  8601. if (hr == S_OK)
  8602. return lResult;
  8603. }
  8604. return MAKELONG(0, MNC_IGNORE);
  8605. // there are two possible ways to put help texts in the
  8606. // status bar, (1) processing WM_MENUSELECT or (2) handling MenuHelp
  8607. // messages. (1) is compatible with OLE, but (2) is required anyway
  8608. // for tooltips.
  8609. //
  8610. case WM_MENUSELECT:
  8611. _OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam), GET_WM_MENUSELECT_FLAGS(wParam, lParam), GET_WM_MENUSELECT_HMENU(wParam, lParam));
  8612. break;
  8613. case WM_SYSCOLORCHANGE:
  8614. _SetFolderColors();
  8615. SendMessage(_hwndListview, uMsg, wParam, lParam);
  8616. _rgbBackColor = CLR_INVALID;
  8617. break;
  8618. case SVM_SELECTITEM:
  8619. SelectItem((LPCITEMIDLIST)lParam, (int) wParam);
  8620. break;
  8621. case SVM_SELECTANDPOSITIONITEM:
  8622. {
  8623. SFM_SAP * psap = (SFM_SAP*)lParam;
  8624. for (UINT i = 0; i < wParam; psap++, i++)
  8625. SelectAndPositionItem(psap->pidl, psap->uSelectFlags, psap->fMove ? &psap->pt : NULL);
  8626. break;
  8627. }
  8628. case WM_PALETTECHANGED:
  8629. if (_IsImageMode())
  8630. {
  8631. InvalidateRect(_hwndListview, NULL, FALSE);
  8632. return TRUE;
  8633. }
  8634. // else Fall Through
  8635. case WM_QUERYNEWPALETTE:
  8636. if (_IsImageMode())
  8637. {
  8638. return FALSE; // Let Browser handle palette management
  8639. }
  8640. else
  8641. {
  8642. HWND hwndT = GetChildViewWindow();
  8643. if (!hwndT)
  8644. goto DoDefWndProc;
  8645. return SendMessage(hwndT, uMsg, wParam, lParam);
  8646. }
  8647. case WM_DSV_REARRANGELISTVIEW:
  8648. _ShowAndActivate();
  8649. break;
  8650. case WM_DSV_SENDSELECTIONCHANGED:
  8651. _OnSelectionChanged();
  8652. break;
  8653. case WM_DSV_SENDNOITEMSTATECHANGED:
  8654. _OnNoItemStateChanged();
  8655. break;
  8656. case WM_DSV_DESKHTML_CHANGES:
  8657. if (_IsDesktop())
  8658. {
  8659. IADesktopP2 *piadp2;
  8660. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IADesktopP2, &piadp2))))
  8661. {
  8662. IActiveDesktopP *piadpp;
  8663. // 98/11/23 #254482 vtan: When making changes using dynamic
  8664. // HTML don't forget to update the "desktop.htt" file so
  8665. // that it's in sync with the registry BEFORE using DHTML.
  8666. if (SUCCEEDED(piadp2->QueryInterface(IID_PPV_ARG(IActiveDesktopP, &piadpp))))
  8667. {
  8668. piadpp->EnsureUpdateHTML(); // ignore result
  8669. piadpp->Release();
  8670. }
  8671. piadp2->MakeDynamicChanges(_cFrame._pOleObj);
  8672. piadp2->Release();
  8673. }
  8674. }
  8675. break;
  8676. // Toggling the New Start Menu on/off causes My Computer, etc.
  8677. // desktop icons to dynamically hide/show themselves.
  8678. case WM_DSV_STARTPAGE_TURNONOFF:
  8679. _ReloadContent(FALSE);
  8680. break;
  8681. case WM_DSV_ADJUSTRECYCLEBINPOSITION:
  8682. {
  8683. // We need to move the recycle bin to it's default position.
  8684. POINT ptRecycleBin;
  8685. int iIndexRecycleBin = _FreezeRecycleBin(&ptRecycleBin);
  8686. if (iIndexRecycleBin != LV_NOFROZENITEM)
  8687. _SetRecycleBinInDefaultPosition(&ptRecycleBin);
  8688. }
  8689. break;
  8690. default:
  8691. DoDefWndProc:
  8692. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  8693. }
  8694. return 0;
  8695. }
  8696. // don't test the result as this will fail on the second call
  8697. void CDefView::_RegisterWindow(void)
  8698. {
  8699. WNDCLASS wc = {0};
  8700. // don't want vredraw and hredraw because that causes horrible
  8701. // flicker expecially with full drag
  8702. wc.style = CS_PARENTDC;
  8703. wc.lpfnWndProc = CDefView::s_WndProc;
  8704. wc.cbWndExtra = sizeof(CDefView *);
  8705. wc.hInstance = HINST_THISDLL;
  8706. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  8707. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  8708. wc.lpszClassName = TEXT("SHELLDLL_DefView");
  8709. RegisterClass(&wc);
  8710. }
  8711. CDefView::~CDefView()
  8712. {
  8713. _uState = SVUIA_DEACTIVATE;
  8714. // Sanity check.
  8715. ASSERT(_tlistPendingInfotips.GetHeadPosition() == NULL);
  8716. DebugMsg(TF_LIFE, TEXT("dtor CDefView %x"), this);
  8717. //
  8718. // Just in case, there is a left over.
  8719. //
  8720. _dvdt.LeaveAndReleaseData();
  8721. //
  8722. // We need to give it a chance to clean up.
  8723. //
  8724. CallCB(SFVM_PRERELEASE, 0, 0);
  8725. DestroyViewWindow();
  8726. ATOMICRELEASE(_pSelectionShellItemArray);
  8727. ATOMICRELEASE(_pFolderShellItemArray);
  8728. ATOMICRELEASE(_pScheduler);
  8729. //
  8730. // We should release _psb after _pshf (for docfindx)
  8731. //
  8732. ATOMICRELEASE(_pshf);
  8733. ATOMICRELEASE(_pshf2);
  8734. ATOMICRELEASE(_pshfParent);
  8735. ATOMICRELEASE(_pshf2Parent);
  8736. ILFree(_pidlRelative);
  8737. ATOMICRELEASE(_psi);
  8738. ATOMICRELEASE(_psio);
  8739. ATOMICRELEASE(_pcdb);
  8740. ATOMICRELEASE(_psb);
  8741. ATOMICRELEASE(_psd);
  8742. IUnknown_SetSite(_pcmFile, NULL);
  8743. ATOMICRELEASE(_pcmFile);
  8744. ATOMICRELEASE(_pcat);
  8745. ATOMICRELEASE(_pImageCache);
  8746. ATOMICRELEASE(_pDiskCache);
  8747. DSA_Destroy(_hdaCategories);
  8748. DSA_Destroy(_hdsaSCIDCache);
  8749. // NOTE we dont release psvOuter
  8750. // it has a ref on us
  8751. if (_pbtn)
  8752. LocalFree(_pbtn);
  8753. //
  8754. // Cleanup _dvdt
  8755. //
  8756. _dvdt.ReleaseDataObject();
  8757. _dvdt.ReleaseCurrentDropTarget();
  8758. _ClearPendingSelectedItems();
  8759. ATOMICRELEASE(_pauto);
  8760. ATOMICRELEASE(_padvise);
  8761. if (_hmenuCur)
  8762. {
  8763. DestroyMenu(_hmenuCur);
  8764. }
  8765. ATOMICRELEASE(_pBackgroundInfoTip);
  8766. ATOMICRELEASE(_ppui);
  8767. if (_pidlSelectAndPosition)
  8768. ILFree(_pidlSelectAndPosition);
  8769. Str_SetPtr(&_pszLegacyWatermark, NULL);
  8770. if (_hdpaGroupingListActive)
  8771. DPA_Destroy(_hdpaGroupingListActive);
  8772. if (_hdpaGroupingListBackup)
  8773. DPA_Destroy(_hdpaGroupingListBackup);
  8774. }
  8775. HRESULT CDefView::_AddTask(IRunnableTask *pTask, REFTASKOWNERID rTID, DWORD_PTR lParam, DWORD dwPriority, DWORD grfFlags)
  8776. {
  8777. HRESULT hr = E_FAIL;
  8778. if (_pScheduler)
  8779. {
  8780. if (grfFlags & ADDTASK_ONLYONCE)
  8781. {
  8782. hr = _pScheduler->MoveTask(rTID, lParam, dwPriority, (grfFlags & ADDTASK_ATFRONT ? ITSSFLAG_TASK_PLACEINFRONT : ITSSFLAG_TASK_PLACEINBACK));
  8783. }
  8784. if (hr != S_OK) // If we didn't move it, add it
  8785. {
  8786. hr = _pScheduler->AddTask2(pTask, rTID, lParam, dwPriority, (grfFlags & ADDTASK_ATFRONT ? ITSSFLAG_TASK_PLACEINFRONT : ITSSFLAG_TASK_PLACEINBACK));
  8787. }
  8788. }
  8789. return hr;
  8790. }
  8791. // Get the number of running tasks of the indicated task ID.
  8792. UINT CDefView::_GetBackgroundTaskCount(REFTASKOWNERID rtid)
  8793. {
  8794. return _pScheduler ? _pScheduler->CountTasks(rtid) : 0;
  8795. }
  8796. const TBBUTTON c_tbDefView[] = {
  8797. { VIEW_MOVETO | IN_VIEW_BMP, SFVIDM_EDIT_MOVETO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8798. { VIEW_COPYTO | IN_VIEW_BMP, SFVIDM_EDIT_COPYTO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8799. { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8800. { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8801. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
  8802. { VIEW_VIEWMENU | IN_VIEW_BMP, SFVIDM_VIEW_VIEWMENU, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, -1},
  8803. // hidden buttons (off by default, available only via customize dialog)
  8804. { STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8805. { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8806. { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8807. { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8808. { VIEW_OPTIONS | IN_VIEW_BMP, SFVIDM_TOOL_OPTIONS, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8809. };
  8810. const TBBUTTON c_tbDefViewWebView[] = {
  8811. //{ 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
  8812. { VIEW_VIEWMENU | IN_VIEW_BMP, SFVIDM_VIEW_VIEWMENU, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, -1},
  8813. // hidden buttons (off by default, available only via customize dialog)
  8814. { VIEW_MOVETO | IN_VIEW_BMP, SFVIDM_EDIT_MOVETO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8815. { VIEW_COPYTO | IN_VIEW_BMP, SFVIDM_EDIT_COPYTO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8816. { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8817. { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8818. { STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8819. { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8820. { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8821. { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8822. { VIEW_OPTIONS | IN_VIEW_BMP, SFVIDM_TOOL_OPTIONS, TBSTATE_HIDDEN | TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8823. };
  8824. // win95 defview toolbar, used for corel apphack
  8825. const TBBUTTON c_tbDefView95[] = {
  8826. { STD_CUT | IN_STD_BMP, SFVIDM_EDIT_CUT, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8827. { STD_COPY | IN_STD_BMP, SFVIDM_EDIT_COPY, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8828. { STD_PASTE | IN_STD_BMP, SFVIDM_EDIT_PASTE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8829. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
  8830. { STD_UNDO | IN_STD_BMP, SFVIDM_EDIT_UNDO, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8831. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
  8832. { STD_DELETE | IN_STD_BMP, SFVIDM_FILE_DELETE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8833. { STD_PROPERTIES | IN_STD_BMP, SFVIDM_FILE_PROPERTIES, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1},
  8834. { 0, 0, TBSTATE_ENABLED, BTNS_SEP, {0,0}, 0, -1 },
  8835. // the bitmap indexes here are relative to the view bitmap
  8836. { VIEW_LARGEICONS | IN_VIEW_BMP, SFVIDM_VIEW_ICON, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 },
  8837. { VIEW_SMALLICONS | IN_VIEW_BMP, SFVIDM_VIEW_SMALLICON, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 },
  8838. { VIEW_LIST | IN_VIEW_BMP, SFVIDM_VIEW_LIST, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 },
  8839. { VIEW_DETAILS | IN_VIEW_BMP, SFVIDM_VIEW_DETAILS, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, -1 },
  8840. };
  8841. LRESULT CDefView::_TBNotify(NMHDR *pnm)
  8842. {
  8843. LPTBNOTIFY ptbn = (LPTBNOTIFY)pnm;
  8844. switch (pnm->code)
  8845. {
  8846. case TBN_BEGINDRAG:
  8847. _OnMenuSelect(ptbn->iItem, 0, 0);
  8848. break;
  8849. }
  8850. return 0;
  8851. }
  8852. BOOL CDefView::_MergeIExplorerToolbar(UINT cExtButtons)
  8853. {
  8854. BOOL fRet = FALSE;
  8855. IExplorerToolbar *piet;
  8856. if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SExplorerToolbar, IID_PPV_ARG(IExplorerToolbar, &piet))))
  8857. {
  8858. BOOL fGotClsid = TRUE;
  8859. DWORD dwFlags = 0;
  8860. if (cExtButtons == 0)
  8861. {
  8862. // This shf has no buttons to merge in; use the standard defview
  8863. // clsid so that the shf shares standard toolbar customization.
  8864. _clsid = CGID_DefViewFrame;
  8865. }
  8866. else if (SUCCEEDED(IUnknown_GetClassID(_pshf, &_clsid)))
  8867. {
  8868. // This shf has buttons to merge in; use its clsid
  8869. // so that this shf gets separate customization persistence.
  8870. // The shf might expect us to provide room for two lines of
  8871. // text (since that was the default in IE4).
  8872. dwFlags |= VBF_TWOLINESTEXT;
  8873. }
  8874. else
  8875. {
  8876. // This shf has buttons to merge in but doesn't implement
  8877. // IPersist::GetClassID; so we can't use IExplorerToolbar mechanism.
  8878. fGotClsid = FALSE;
  8879. }
  8880. if (fGotClsid)
  8881. {
  8882. HRESULT hr = piet->SetCommandTarget((IUnknown *)SAFECAST(this, IOleCommandTarget *), &_clsid, dwFlags);
  8883. if (SUCCEEDED(hr))
  8884. {
  8885. // If hr == S_FALSE, another defview merged in its buttons under the
  8886. // same clsid, and they're still there. So no need to call AddButtons.
  8887. if (hr != S_FALSE)
  8888. hr = piet->AddButtons(&_clsid, _cButtons, _pbtn);
  8889. if (SUCCEEDED(hr))
  8890. {
  8891. fRet = TRUE;
  8892. }
  8893. }
  8894. }
  8895. piet->Release();
  8896. }
  8897. return fRet;
  8898. }
  8899. int _FirstHiddenButton(TBBUTTON* ptbn, int cButtons)
  8900. {
  8901. for (int i = 0; i < cButtons; i++)
  8902. {
  8903. if (ptbn[i].fsState & TBSTATE_HIDDEN)
  8904. break;
  8905. }
  8906. return i;
  8907. }
  8908. void CDefView::_CopyDefViewButton(PTBBUTTON ptbbDest, PTBBUTTON ptbbSrc)
  8909. {
  8910. *ptbbDest = *ptbbSrc;
  8911. if (!(ptbbDest->fsStyle & BTNS_SEP))
  8912. {
  8913. // Fix up bitmap offset depending on whether this is a "view" bitmap or a "standard" bitmap
  8914. if (ptbbDest->iBitmap & IN_VIEW_BMP)
  8915. ptbbDest->iBitmap = (int)((ptbbDest->iBitmap & ~PRIVATE_TB_FLAGS) + _iViewBMOffset);
  8916. else
  8917. ptbbDest->iBitmap = (int)(ptbbDest->iBitmap + _iStdBMOffset);
  8918. }
  8919. }
  8920. //
  8921. // Here's the deal with _GetButtons
  8922. //
  8923. // DefView has some buttons, and its callback client may have some buttons.
  8924. //
  8925. // Some of defview's buttons are visible on the toolbar by default, and some only show
  8926. // up if you customize the toolbar.
  8927. //
  8928. // We specify which buttons are hidden by default by marking them with TBSTATE_HIDDEN in
  8929. // the declaration of c_tbDefView. We assume all such buttons are in a continuous block at
  8930. // the end of c_tbDefView.
  8931. //
  8932. // We return in ppbtn a pointer to an array of all the buttons, including those not shown
  8933. // by default. We put the buttons not shown by default at the end of this array. We pass
  8934. // back in pcButtons the count of visible buttons, and in pcTotalButtons the count of visible
  8935. // and hidden buttons.
  8936. //
  8937. // The int return value is the number of client buttons in the array.
  8938. //
  8939. int CDefView::_GetButtons(PTBBUTTON* ppbtn, LPINT pcButtons, LPINT pcTotalButtons)
  8940. {
  8941. int cVisibleBtns = 0; // count of visible defview + client buttons
  8942. TBINFO tbinfo;
  8943. tbinfo.uFlags = TBIF_APPEND;
  8944. tbinfo.cbuttons = 0;
  8945. // Does the client want to prepend/append a toolbar?
  8946. CallCB(SFVM_GETBUTTONINFO, 0, (LPARAM)&tbinfo);
  8947. _uDefToolbar = HIWORD(tbinfo.uFlags);
  8948. tbinfo.uFlags &= 0xffff;
  8949. // tbDefView needs to be big enough to hold either c_tbDefView or c_tbDefView95
  8950. COMPILETIME_ASSERT(ARRAYSIZE(c_tbDefView95) >= ARRAYSIZE(c_tbDefView));
  8951. COMPILETIME_ASSERT(ARRAYSIZE(c_tbDefView95) >= ARRAYSIZE(c_tbDefViewWebView));
  8952. TBBUTTON tbDefView[ARRAYSIZE(c_tbDefView95)];
  8953. int cDefViewBtns; // total count of defview buttons
  8954. if (SHGetAppCompatFlags(ACF_WIN95DEFVIEW) & ACF_WIN95DEFVIEW)
  8955. {
  8956. memcpy(tbDefView, c_tbDefView95, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefView95));
  8957. cDefViewBtns = ARRAYSIZE(c_tbDefView95);
  8958. }
  8959. else if (_cFrame.IsWebView() || _pDUIView)
  8960. {
  8961. memcpy(tbDefView, c_tbDefViewWebView, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefViewWebView));
  8962. cDefViewBtns = ARRAYSIZE(c_tbDefViewWebView);
  8963. }
  8964. else
  8965. {
  8966. memcpy(tbDefView, c_tbDefView, sizeof(TBBUTTON) * ARRAYSIZE(c_tbDefView));
  8967. cDefViewBtns = ARRAYSIZE(c_tbDefView);
  8968. }
  8969. int cVisibleDefViewBtns = _FirstHiddenButton(tbDefView, cDefViewBtns); // count of visible defview buttons
  8970. TBBUTTON *pbtn = (TBBUTTON *)LocalAlloc(LPTR, (cDefViewBtns + tbinfo.cbuttons) * sizeof(*pbtn));
  8971. if (pbtn)
  8972. {
  8973. int iStart = 0;
  8974. cVisibleBtns = tbinfo.cbuttons + cVisibleDefViewBtns;
  8975. // Have the client fill in its buttons
  8976. switch (tbinfo.uFlags)
  8977. {
  8978. case TBIF_PREPEND:
  8979. CallCB(SFVM_GETBUTTONS,
  8980. MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
  8981. (LPARAM)pbtn);
  8982. iStart = tbinfo.cbuttons;
  8983. break;
  8984. case TBIF_APPEND:
  8985. CallCB(SFVM_GETBUTTONS,
  8986. MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
  8987. (LPARAM)&pbtn[cVisibleDefViewBtns]);
  8988. iStart = 0;
  8989. break;
  8990. case TBIF_REPLACE:
  8991. CallCB(SFVM_GETBUTTONS,
  8992. MAKEWPARAM(SFVIDM_CLIENT_FIRST, tbinfo.cbuttons),
  8993. (LPARAM)pbtn);
  8994. cVisibleBtns = tbinfo.cbuttons;
  8995. cVisibleDefViewBtns = 0;
  8996. break;
  8997. default:
  8998. RIPMSG(0, "View callback passed an invalid TBINFO flag");
  8999. break;
  9000. }
  9001. // Fill in visible defview buttons
  9002. for (int i = 0; i < cVisibleDefViewBtns; i++)
  9003. {
  9004. // Visible defview button block gets added at iStart
  9005. _CopyDefViewButton(&pbtn[i + iStart], &tbDefView[i]);
  9006. }
  9007. // Fill in hidden defview buttons
  9008. for (i = cVisibleDefViewBtns; i < cDefViewBtns; i++)
  9009. {
  9010. // Hidden defview button block gets added after visible & client buttons
  9011. _CopyDefViewButton(&pbtn[i + tbinfo.cbuttons], &tbDefView[i]);
  9012. // If this rips a visible button got mixed in with the hidden block
  9013. ASSERT(pbtn[i + tbinfo.cbuttons].fsState & TBSTATE_HIDDEN);
  9014. // Rip off the hidden bit
  9015. pbtn[i + tbinfo.cbuttons].fsState &= ~TBSTATE_HIDDEN;
  9016. }
  9017. }
  9018. ASSERT(ppbtn);
  9019. ASSERT(pcButtons);
  9020. ASSERT(pcTotalButtons);
  9021. *ppbtn = pbtn;
  9022. *pcButtons = cVisibleBtns;
  9023. *pcTotalButtons = tbinfo.cbuttons + cDefViewBtns;
  9024. return tbinfo.cbuttons;
  9025. }
  9026. void CDefView::MergeToolBar(BOOL bCanRestore)
  9027. {
  9028. TBADDBITMAP ab;
  9029. ab.hInst = HINST_COMMCTRL; // hinstCommctrl
  9030. ab.nID = IDB_STD_SMALL_COLOR; // std bitmaps
  9031. _psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &_iStdBMOffset);
  9032. ab.nID = IDB_VIEW_SMALL_COLOR; // std view bitmaps
  9033. _psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 8, (LPARAM)&ab, &_iViewBMOffset);
  9034. if (_pbtn)
  9035. LocalFree(_pbtn);
  9036. int cExtButtons = _GetButtons(&_pbtn, &_cButtons, &_cTotalButtons);
  9037. if (_pbtn && !_MergeIExplorerToolbar(cExtButtons))
  9038. {
  9039. // if we're able to do the new IExplorerToolbar merge method, great...
  9040. // if not, we use the old style
  9041. _psb->SetToolbarItems(_pbtn, _cButtons, FCT_MERGE);
  9042. CDefView::CheckToolbar();
  9043. }
  9044. }
  9045. STDMETHODIMP CDefView::GetWindow(HWND *phwnd)
  9046. {
  9047. *phwnd = _hwndView;
  9048. return S_OK;
  9049. }
  9050. STDMETHODIMP CDefView::ContextSensitiveHelp(BOOL fEnterMode)
  9051. {
  9052. return E_NOTIMPL;
  9053. }
  9054. STDMETHODIMP CDefView::EnableModeless(BOOL fEnable)
  9055. {
  9056. // We have no modeless window to be enabled/disabled
  9057. return S_OK;
  9058. }
  9059. HRESULT CDefView::_ReloadListviewContent()
  9060. {
  9061. // HACK: We always call IsShared with fUpdateCache=FALSE for performance.
  9062. // However, we need to update the cache when the user explicitly tell
  9063. // us to "Refresh". This is not the ideal place to put this code, but
  9064. // we have no other choice.
  9065. TCHAR szPathAny[MAX_PATH];
  9066. _UpdateSelectionMode();
  9067. // finish any pending edits
  9068. SendMessage(_hwndListview, LVM_EDITLABEL, (WPARAM)-1, 0);
  9069. GetWindowsDirectory(szPathAny, ARRAYSIZE(szPathAny));
  9070. IsShared(szPathAny, TRUE);
  9071. // HACK: strange way to notify folder that we're refreshing
  9072. ULONG rgf = SFGAO_VALIDATE;
  9073. _pshf->GetAttributesOf(0, NULL, &rgf);
  9074. //
  9075. // if a item is selected, make sure it gets nuked from the icon
  9076. // cache, this is a last resort type thing, select a item and
  9077. // hit F5 to fix all your problems.
  9078. //
  9079. int iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
  9080. if (iItem != -1)
  9081. CFSFolder_UpdateIcon(_pshf, _GetPIDL(iItem));
  9082. // We should not save the selection if doing refresh.
  9083. _ClearPendingSelectedItems();
  9084. // 01/05/21 #399284: Don't save/restore the state and nuke objects if there's a background process using them
  9085. if(!_bBkFilling)
  9086. {
  9087. // First we have to save all the icon positions, so they will be restored
  9088. // properly during the FillObjectsShowHide
  9089. SaveViewState();
  9090. // 99/04/07 #309965 vtan: Persist the view state (above). Make sure
  9091. // our internal representation is the same as the one on the disk
  9092. // by dumping our cache and reloading the information.
  9093. GetViewState();
  9094. // To make it look like the refesh is doing something, clear
  9095. // all the icons from the view before we start enumerating.
  9096. _RemoveObject(NULL, FALSE);
  9097. _fSyncOnFillDone = TRUE; // apply the just-saved view state when we finish enumeration
  9098. }
  9099. return FillObjectsShowHide(TRUE);
  9100. }
  9101. HRESULT CDefView::_ReloadContent(BOOL fForce)
  9102. {
  9103. if (_bReEntrantReload)
  9104. {
  9105. return S_FALSE;
  9106. }
  9107. _bReEntrantReload = TRUE;
  9108. HRESULT hrExtView = S_OK;
  9109. HRESULT hrNormalView = S_OK;
  9110. SHELLSTATE ss;
  9111. // Tell the defview client that this window is about to be refreshed
  9112. _CallRefresh(TRUE);
  9113. // make sure that the CommandIds and the Uids match by recreating the menus
  9114. RecreateMenus();
  9115. // If the global SSF_WIN95CLASSIC state changed, we need to muck with the UI.
  9116. SHGetSetSettings(&ss, SSF_WIN95CLASSIC, FALSE);
  9117. // Show webview and pane again if we are forced OR the view has changed.
  9118. if (fForce || (BOOLIFY(ss.fWin95Classic) != BOOLIFY(_fClassic)))
  9119. {
  9120. _fClassic = ss.fWin95Classic;
  9121. _UpdateListviewColors();
  9122. }
  9123. if (_ShouldShowWebView())
  9124. {
  9125. // We need to save the icon positions before we refresh the view.
  9126. SaveViewState();
  9127. if (_pDUIView)
  9128. {
  9129. hrExtView = _pDUIView->Refresh();
  9130. }
  9131. else
  9132. {
  9133. _TryShowWebView(_fs.ViewMode, _fs.ViewMode);
  9134. }
  9135. }
  9136. else
  9137. {
  9138. _TryHideWebView(); // make sure it's off
  9139. }
  9140. // We want to preserve the earlier error if any
  9141. hrNormalView = _ReloadListviewContent();
  9142. _bReEntrantReload = FALSE;
  9143. return FAILED(hrExtView) ? hrExtView : hrNormalView;
  9144. }
  9145. STDMETHODIMP CDefView::Refresh()
  9146. {
  9147. // See if some refreshes were buffered
  9148. if (_fRefreshBuffered)
  9149. {
  9150. //Since we are refreshing it right now. Kill the timer.
  9151. TraceMsg(TF_DEFVIEW, "Buffered Refresh Timer Killed by regular Refresh");
  9152. KillTimer(_hwndView, DV_IDTIMER_BUFFERED_REFRESH);
  9153. _fRefreshBuffered = FALSE;
  9154. }
  9155. // If desktop is in modal state, do not attempt to refresh.
  9156. // If we do, we endup destroying Trident object when it is in modal state.
  9157. if (_IsDesktop() && _fDesktopModal)
  9158. {
  9159. // Remember that we could not refresh the desktop because it was in
  9160. // a modal state.
  9161. _fDesktopRefreshPending = TRUE;
  9162. return S_OK;
  9163. }
  9164. // make sure we have the latest
  9165. SHRefreshSettings();
  9166. _UpdateRegFlags();
  9167. if (_IsDesktop())
  9168. {
  9169. SHELLSTATE ss = {0};
  9170. SHGetSetSettings(&ss, SSF_DESKTOPHTML, FALSE);
  9171. // The following code is not needed because _ReloadContent() takes care of switching to
  9172. // web-view.
  9173. // _SwitchDesktopHTML(BOOLIFY(ss.fDesktopHTML));
  9174. if (ss.fDesktopHTML)
  9175. {
  9176. // For backward compatibility, hide the desktop channel bar.
  9177. HideIE4DesktopChannelBar();
  9178. // ActiveDesktop is not part of shdocvw's browser session count
  9179. // so when we refresh, we must tell wininet to reset the session
  9180. // count otherwise we will not hit the net.
  9181. MyInternetSetOption(NULL, INTERNET_OPTION_RESET_URLCACHE_SESSION, NULL, 0);
  9182. }
  9183. }
  9184. return _ReloadContent(TRUE);
  9185. }
  9186. STDMETHODIMP CDefView::CreateViewWindow(IShellView *psvPrevious,
  9187. LPCFOLDERSETTINGS pfs, IShellBrowser *psb, RECT *prc, HWND *phWnd)
  9188. {
  9189. SV2CVW2_PARAMS cParams = {0};
  9190. cParams.cbSize = sizeof(SV2CVW2_PARAMS);
  9191. cParams.psvPrev = psvPrevious;
  9192. cParams.pfs = pfs;
  9193. cParams.psbOwner = psb;
  9194. cParams.prcView = prc;
  9195. HRESULT hr = CreateViewWindow2(&cParams);
  9196. *phWnd = cParams.hwndView;
  9197. if (SUCCEEDED(hr) &&
  9198. (SHGetAppCompatFlags(ACF_OLDCREATEVIEWWND) & ACF_OLDCREATEVIEWWND))
  9199. {
  9200. //
  9201. // CreateViewWindow was documented as returning S_OK on success,
  9202. // but IE4 changed the function to return S_FALSE if the defview
  9203. // was created async.
  9204. //
  9205. // PowerDesk relies on the old behavior.
  9206. // So does Quattro Pro.
  9207. //
  9208. hr = S_OK;
  9209. }
  9210. return hr;
  9211. }
  9212. STDMETHODIMP CDefView::HandleRename(LPCITEMIDLIST pidl)
  9213. {
  9214. HRESULT hr = E_FAIL;
  9215. // Gross, but if no PIDL passed in use the GetObject(-2) hack to get the selected object...
  9216. // Don't need to free as it wsa not cloned...
  9217. if (!pidl)
  9218. {
  9219. GetObject((LPITEMIDLIST*)&pidl, (UINT)-2);
  9220. }
  9221. else
  9222. {
  9223. RIP(ILFindLastID(pidl) == pidl);
  9224. if (ILFindLastID(pidl) != pidl)
  9225. {
  9226. return E_INVALIDARG;
  9227. }
  9228. }
  9229. hr = SelectAndPositionItem(pidl, SVSI_SELECT, NULL);
  9230. if (SUCCEEDED(hr))
  9231. hr = SelectAndPositionItem(pidl, SVSI_EDIT, NULL);
  9232. return hr;
  9233. }
  9234. // IViewObject
  9235. HRESULT CDefView::GetColorSet(DWORD dwAspect, LONG lindex, void *pvAspect,
  9236. DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet)
  9237. {
  9238. if (_cFrame.IsWebView() && _cFrame._pvoActive)
  9239. {
  9240. return _cFrame._pvoActive->GetColorSet(dwAspect, lindex, pvAspect,
  9241. ptd, hicTargetDev, ppColorSet);
  9242. }
  9243. if (ppColorSet)
  9244. *ppColorSet = NULL;
  9245. return E_FAIL;
  9246. }
  9247. HRESULT CDefView::Freeze(DWORD, LONG, void *, DWORD *pdwFreeze)
  9248. {
  9249. return E_NOTIMPL;
  9250. }
  9251. HRESULT CDefView::Unfreeze(DWORD)
  9252. {
  9253. return E_NOTIMPL;
  9254. }
  9255. HRESULT CDefView::SetAdvise(DWORD dwAspect, DWORD advf, IAdviseSink *pSink)
  9256. {
  9257. if (dwAspect != DVASPECT_CONTENT)
  9258. return DV_E_DVASPECT;
  9259. if (advf & ~(ADVF_PRIMEFIRST | ADVF_ONLYONCE))
  9260. return E_INVALIDARG;
  9261. if (pSink != _padvise)
  9262. {
  9263. ATOMICRELEASE(_padvise);
  9264. _padvise = pSink;
  9265. if (_padvise)
  9266. _padvise->AddRef();
  9267. }
  9268. if (_padvise)
  9269. {
  9270. _advise_aspect = dwAspect;
  9271. _advise_advf = advf;
  9272. if (advf & ADVF_PRIMEFIRST)
  9273. PropagateOnViewChange(dwAspect, -1);
  9274. }
  9275. else
  9276. _advise_aspect = _advise_advf = 0;
  9277. return S_OK;
  9278. }
  9279. HRESULT CDefView::GetAdvise(DWORD *pdwAspect, DWORD *padvf,
  9280. IAdviseSink **ppSink)
  9281. {
  9282. if (pdwAspect)
  9283. *pdwAspect = _advise_aspect;
  9284. if (padvf)
  9285. *padvf = _advise_advf;
  9286. if (ppSink)
  9287. {
  9288. if (_padvise)
  9289. _padvise->AddRef();
  9290. *ppSink = _padvise;
  9291. }
  9292. return S_OK;
  9293. }
  9294. HRESULT CDefView::Draw(DWORD, LONG, void *, DVTARGETDEVICE *, HDC, HDC,
  9295. const RECTL *, const RECTL *, BOOL (*)(ULONG_PTR), ULONG_PTR)
  9296. {
  9297. return E_NOTIMPL;
  9298. }
  9299. void CDefView::PropagateOnViewChange(DWORD dwAspect, LONG lindex)
  9300. {
  9301. dwAspect &= _advise_aspect;
  9302. if (dwAspect && _padvise)
  9303. {
  9304. IAdviseSink *pSink = _padvise;
  9305. IUnknown *punkRelease;
  9306. if (_advise_advf & ADVF_ONLYONCE)
  9307. {
  9308. punkRelease = pSink;
  9309. _padvise = NULL;
  9310. _advise_aspect = _advise_advf = 0;
  9311. }
  9312. else
  9313. punkRelease = NULL;
  9314. pSink->OnViewChange(dwAspect, lindex);
  9315. ATOMICRELEASE(punkRelease);
  9316. }
  9317. }
  9318. void CDefView::PropagateOnClose()
  9319. {
  9320. //
  9321. // we aren't closing ourselves, just somebody under us...
  9322. // ...reflect this up the chain as a view change.
  9323. //
  9324. if (_padvise)
  9325. PropagateOnViewChange(_advise_aspect, -1);
  9326. }
  9327. UINT CDefView::_ValidateViewMode(UINT uViewMode)
  9328. {
  9329. UINT uViewModeDefault = FVM_ICON;
  9330. if (uViewMode >= FVM_FIRST && uViewMode <= FVM_LAST)
  9331. {
  9332. uViewModeDefault = uViewMode;
  9333. #ifdef DEBUG
  9334. if (!_ViewSupported(uViewMode))
  9335. {
  9336. // Whoa! the default is excluded? Ignore it.
  9337. TraceMsg(TF_WARNING, "Bug in IShellFolderViewCB client: returned a default viewmode that is excluded");
  9338. }
  9339. #endif
  9340. }
  9341. else
  9342. {
  9343. TraceMsg(TF_WARNING, "Bug in IShellFolderViewCB client: returned invalid viewmode");
  9344. }
  9345. return uViewModeDefault;
  9346. }
  9347. UINT CDefView::_GetDefaultViewMode()
  9348. {
  9349. UINT uViewMode = (_IsDesktop() || !IsOS(OS_SERVERADMINUI)) ? FVM_ICON : FVM_DETAILS;
  9350. CallCB(SFVM_DEFVIEWMODE, 0, (LPARAM)&uViewMode);
  9351. return _ValidateViewMode(uViewMode);
  9352. }
  9353. void CDefView::_GetDeferredViewSettings(UINT* puViewMode)
  9354. {
  9355. SFVM_DEFERRED_VIEW_SETTINGS sdvsSettings;
  9356. ZeroMemory(&sdvsSettings, sizeof(sdvsSettings));
  9357. if (SUCCEEDED(CallCB(SFVM_GETDEFERREDVIEWSETTINGS, 0, (LPARAM)&sdvsSettings)))
  9358. {
  9359. _vs._lParamSort = sdvsSettings.uSortCol;
  9360. _vs._iDirection = sdvsSettings.iSortDirection >= 0 ? 1 : -1;
  9361. *puViewMode = _ValidateViewMode(sdvsSettings.fvm);
  9362. _fs.fFlags = (_fs.fFlags & ~FWF_AUTOARRANGE) | (sdvsSettings.fFlags & FWF_AUTOARRANGE);
  9363. SHSetWindowBits(_hwndListview, GWL_STYLE, LVS_AUTOARRANGE, _IsAutoArrange() ? LVS_AUTOARRANGE : 0);
  9364. if (sdvsSettings.fGroupView && (*puViewMode != FVM_THUMBSTRIP))
  9365. {
  9366. SHCOLUMNID scid;
  9367. if SUCCEEDED(_pshf2->MapColumnToSCID(sdvsSettings.uSortCol, &scid))
  9368. {
  9369. _CategorizeOnGUID(&CLSID_DetailCategorizer, &scid);
  9370. }
  9371. }
  9372. }
  9373. else
  9374. {
  9375. *puViewMode = _GetDefaultViewMode();
  9376. }
  9377. }
  9378. BOOL CDefView::_ViewSupported(UINT uView)
  9379. {
  9380. SFVM_VIEW_DATA vi;
  9381. _GetSFVMViewState(uView, &vi);
  9382. BOOL fIncludeView;
  9383. if (vi.dwOptions == SFVMQVI_INCLUDE)
  9384. fIncludeView = TRUE;
  9385. else if (vi.dwOptions == SFVMQVI_EXCLUDE)
  9386. fIncludeView = FALSE;
  9387. else
  9388. fIncludeView = uView != FVM_THUMBSTRIP; // by default, everything is included except FVM_THUMBSTRIP
  9389. return fIncludeView;
  9390. }
  9391. STDMETHODIMP CDefView::GetView(SHELLVIEWID* pvid, ULONG uView)
  9392. {
  9393. HRESULT hr;
  9394. if ((int)uView >= 0)
  9395. {
  9396. // start with the first supported view
  9397. UINT fvm = FVM_FIRST;
  9398. while (fvm <= FVM_LAST && !_ViewSupported(fvm))
  9399. fvm++;
  9400. // find fvm associated with index uView
  9401. for (ULONG i = 0; fvm <= FVM_LAST && i < uView; fvm++, i++)
  9402. {
  9403. // skip unsupported views
  9404. while (fvm <= FVM_LAST && !_ViewSupported(fvm))
  9405. fvm++;
  9406. }
  9407. if (fvm <= FVM_LAST)
  9408. {
  9409. hr = SVIDFromViewMode((FOLDERVIEWMODE)fvm, pvid);
  9410. }
  9411. else if (i == uView)
  9412. {
  9413. // enumerate the "default view" so the browser doesn't throw it out later
  9414. *pvid = VID_DefaultView;
  9415. hr = S_OK;
  9416. }
  9417. else
  9418. {
  9419. hr = E_INVALIDARG;
  9420. }
  9421. }
  9422. else
  9423. {
  9424. // We're being asked about specific view info:
  9425. switch (uView)
  9426. {
  9427. case SV2GV_CURRENTVIEW:
  9428. hr = SVIDFromViewMode((FOLDERVIEWMODE)_fs.ViewMode, pvid);
  9429. break;
  9430. case SV2GV_DEFAULTVIEW:
  9431. // tell the browser "default" so we can pick the right one later on
  9432. *pvid = VID_DefaultView;
  9433. hr = S_OK;
  9434. break;
  9435. default:
  9436. hr = E_INVALIDARG;
  9437. break;
  9438. }
  9439. }
  9440. return hr;
  9441. }
  9442. // For Folder Advanced Options flags that we check often, it's better
  9443. // to cache the values as flags. Update them here.
  9444. void CDefView::_UpdateRegFlags()
  9445. {
  9446. DWORD dwValue, cbSize = sizeof(dwValue);
  9447. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER,
  9448. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  9449. TEXT("ClassicViewState"), NULL, &dwValue, &cbSize)
  9450. && dwValue)
  9451. {
  9452. _fWin95ViewState = TRUE;
  9453. }
  9454. else
  9455. {
  9456. _fWin95ViewState = FALSE;
  9457. }
  9458. }
  9459. BOOL CDefView::_SetupNotifyData()
  9460. {
  9461. if (!_pidlMonitor && !_lFSEvents)
  9462. {
  9463. LPCITEMIDLIST pidl = NULL;
  9464. LONG lEvents = 0;
  9465. if (SUCCEEDED(CallCB(SFVM_GETNOTIFY, (WPARAM)&pidl, (LPARAM)&lEvents)))
  9466. {
  9467. _pidlMonitor = pidl;
  9468. _lFSEvents = lEvents;
  9469. }
  9470. }
  9471. return _pidlMonitor || _lFSEvents;
  9472. }
  9473. void CDefView::_ShowViewEarly()
  9474. {
  9475. // Show the window early (what old code did)
  9476. SetWindowPos(_hwndView, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
  9477. _OnMoveWindowToTop(_hwndView);
  9478. UpdateWindow(_hwndView);
  9479. }
  9480. BOOL LV_FindWorkArea(RECT rcWorkAreas[], int nWorkAreas, POINT *ppt, int *piWorkArea)
  9481. {
  9482. for (int iWork = 0; iWork < nWorkAreas; iWork++)
  9483. {
  9484. if (PtInRect(&rcWorkAreas[iWork], *ppt))
  9485. {
  9486. *piWorkArea = iWork;
  9487. return TRUE;
  9488. }
  9489. }
  9490. *piWorkArea = 0; // default case is the primary work area
  9491. return FALSE;
  9492. }
  9493. void CDefView::_ClearItemPositions()
  9494. {
  9495. _fUserPositionedItems = FALSE;
  9496. _vs.ClearPositionData();
  9497. }
  9498. //
  9499. // This function finds the Recycle bin icon and freezes it. It also freezes the bottom right corner
  9500. // slot sothat no icon can occupy it.
  9501. //
  9502. int CDefView::_FreezeRecycleBin(POINT *ppt)
  9503. {
  9504. int iIndexRecycleBin = -1;
  9505. if (_IsDesktop())
  9506. {
  9507. LPITEMIDLIST pidlRecycleBin;
  9508. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlRecycleBin)))
  9509. {
  9510. //Find the index of the recycle bin in the listview.
  9511. iIndexRecycleBin = _FindItem(pidlRecycleBin, NULL, FALSE);
  9512. if (iIndexRecycleBin >= 0) //If we don't find recycle bin, we don't have anything to do!
  9513. {
  9514. //Freeze the recycle item (prevent it from moving)
  9515. ListView_SetFrozenItem(_hwndListview, TRUE, iIndexRecycleBin);
  9516. RECT rcItem;
  9517. ListView_GetItemRect(_hwndListview, iIndexRecycleBin, &rcItem, LVIR_SELECTBOUNDS);
  9518. //Get the ViewRect.
  9519. RECT rcViewRect;
  9520. int nWorkAreas = 0;
  9521. //Get the number of work-areas
  9522. ListView_GetNumberOfWorkAreas(_hwndListview, &nWorkAreas);
  9523. if (nWorkAreas > 1)
  9524. {
  9525. ASSERT(nWorkAreas <= LV_MAX_WORKAREAS);
  9526. if (nWorkAreas <= LV_MAX_WORKAREAS) // just make sure in retail
  9527. {
  9528. RECT rcWorkAreas[LV_MAX_WORKAREAS];
  9529. int iCurWorkArea = 0;
  9530. //Get all the work areas!
  9531. ListView_GetWorkAreas(_hwndListview, nWorkAreas, rcWorkAreas);
  9532. //Find which work area the Recycle-bin currently lies.
  9533. LV_FindWorkArea(rcWorkAreas, nWorkAreas, (LPPOINT)(&rcItem.left), &iCurWorkArea);
  9534. CopyRect(&rcViewRect, &rcWorkAreas[iCurWorkArea]);
  9535. }
  9536. }
  9537. else
  9538. {
  9539. ListView_GetViewRect(_hwndListview, &rcViewRect);
  9540. }
  9541. //Calculate the bottom-right corner of this slot
  9542. POINT ptRecycleBin;
  9543. ptRecycleBin.x = rcViewRect.right;
  9544. ptRecycleBin.y = rcViewRect.bottom;
  9545. //Freeze this slot sothat no other icon can occupy this.
  9546. ListView_SetFrozenSlot(_hwndListview, TRUE, &ptRecycleBin);
  9547. RECT rcIcon;
  9548. ListView_GetItemRect(_hwndListview, iIndexRecycleBin, &rcIcon, LVIR_ICON);
  9549. ppt->x = rcViewRect.right - RECTWIDTH(rcIcon) - (RECTWIDTH(rcItem) - RECTWIDTH(rcIcon))/2;
  9550. ppt->y = rcViewRect.bottom - RECTHEIGHT(rcItem);
  9551. }
  9552. ILFree(pidlRecycleBin);
  9553. }
  9554. }
  9555. return iIndexRecycleBin;
  9556. }
  9557. //
  9558. // This function moves the RecycleBin item to the given location and then unfreezes the item and
  9559. // the frozen slot.
  9560. //
  9561. void CDefView::_SetRecycleBinInDefaultPosition(POINT *ppt)
  9562. {
  9563. // If a sorting has happened since an item was frozen, the index of that item would have changed.
  9564. // So, get the index of the recycle bin here.
  9565. int iIndexRecycleBin = ListView_GetFrozenItem(_hwndListview);
  9566. if (iIndexRecycleBin != LV_NOFROZENITEM)
  9567. {
  9568. //Move the recycle-bin icon to it's default position
  9569. _SetItemPosition(iIndexRecycleBin, ppt->x, ppt->y);
  9570. //Unfreeze the slot
  9571. ListView_SetFrozenSlot(_hwndListview, FALSE, NULL); //FALSE ==> Unfreeze!
  9572. //Unfreeze the recycle bin
  9573. ListView_SetFrozenItem(_hwndListview, FALSE, 0); //FALSE ==> Unfreeze!
  9574. //Since we repositioned recyclebin earlier, we need to save it in the registry.
  9575. //Do we need this?
  9576. // SaveViewState();
  9577. }
  9578. }
  9579. STDMETHODIMP CDefView::CreateViewWindow2(LPSV2CVW2_PARAMS pParams)
  9580. {
  9581. if (g_dwProfileCAP & 0x00000001)
  9582. StopCAP();
  9583. if (pParams->cbSize < sizeof(SV2CVW2_PARAMS))
  9584. return E_INVALIDARG;
  9585. pParams->hwndView = NULL;
  9586. _RegisterWindow();
  9587. if (_hwndView || !pParams->psbOwner)
  9588. return E_UNEXPECTED;
  9589. DECLAREWAITCURSOR;
  9590. SetWaitCursor();
  9591. // Need to leave this code as is. Previously, we had changed it to
  9592. // pParams->psbOwner->QueryInterface(IID_PPV_ARG(IShellBrowser, &_psb));
  9593. // However, this breaks Corel Quattro Pro 8 in their filesave dialog.
  9594. // They pass in some sort of dummy "stub" IShellBrowser. QI'ing it for IShellBrowser
  9595. // will do nothing, and thus _psb will remain null, and we crash. Restoring it to
  9596. // the old way, _psb will be their "stub", but still valid, IShellBrowser.
  9597. // Look for other comments for "Quattro Pro" in this file to see why they pass
  9598. // in this stub.
  9599. // (do this before doing the GetWindowRect)
  9600. _psb = pParams->psbOwner;
  9601. _psb->AddRef();
  9602. ASSERT(_psb); // much of our code assumes this to be valid w/o checking
  9603. #ifdef _X86_
  9604. // Verify that the CHijaakObjectWithSite is properly laid out
  9605. COMPILETIME_ASSERT(FIELD_OFFSET(CDefView, _psfHijaak) + sizeof(_psfHijaak) ==
  9606. FIELD_OFFSET(CDefView, _psb));
  9607. #endif
  9608. _fGlobeCanSpin = TRUE;
  9609. _GlobeAnimation(TRUE);
  9610. HRESULT hr;
  9611. SHELLSTATE ss; // we will need these bits later on
  9612. SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_DESKTOPHTML | SSF_WEBVIEW | SSF_STARTPANELON, FALSE);
  9613. _pshf->QueryInterface(IID_PPV_ARG(IShellIcon, &_psi));
  9614. _pshf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &_psio));
  9615. pParams->psbOwner->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &_pcdb));
  9616. // listview starts out in large icon mode, we will switch to the proper view shortly
  9617. _fs.ViewMode = FVM_ICON;
  9618. // refetch FWF_ after browser supplied versions stomped our copy
  9619. _fs.fFlags = pParams->pfs->fFlags & ~FWF_OWNERDATA;
  9620. CallCB(SFVM_FOLDERSETTINGSFLAGS, 0, (LPARAM)&_fs.fFlags);
  9621. // pvid takes precedence over pfs->ViewMode
  9622. UINT fvm = pParams->pfs->ViewMode;
  9623. if (pParams->pvid)
  9624. {
  9625. if (IsEqualIID(*pParams->pvid, VID_DefaultView))
  9626. fvm = FVM_LAST + 1; // not a real view -- we will pick after enumeration
  9627. else
  9628. ViewModeFromSVID(pParams->pvid, (FOLDERVIEWMODE *)&fvm);
  9629. }
  9630. // This should never fail
  9631. _psb->GetWindow(&_hwndMain);
  9632. ASSERT(IsWindow(_hwndMain));
  9633. CallCB(SFVM_HWNDMAIN, 0, (LPARAM)_hwndMain);
  9634. // We need to restore the column widths and icon positions before showing the window
  9635. if (!GetViewState())
  9636. {
  9637. // Icon positions are not available; Therefore, it is a clean install
  9638. // and we need to position recycle bin if this is Desktop.
  9639. _fPositionRecycleBin = BOOLIFY(_IsDesktop());
  9640. }
  9641. _fSyncOnFillDone = TRUE; // apply the just-loaded view state when we finish enumeration
  9642. // if there was a previous view that we know about, update our column state
  9643. if (_fWin95ViewState && pParams->psvPrev)
  9644. {
  9645. _vs.InitFromPreviousView(pParams->psvPrev);
  9646. }
  9647. _pEnumTask = new CDefviewEnumTask(this, ++_dwEnumId);
  9648. if (_pEnumTask &&
  9649. CreateWindowEx(IS_WINDOW_RTL_MIRRORED(_hwndMain) ? dwExStyleRTLMirrorWnd : 0,
  9650. TEXT("SHELLDLL_DefView"), NULL,
  9651. WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP,
  9652. pParams->prcView->left, pParams->prcView->top,
  9653. pParams->prcView->right - pParams->prcView->left,
  9654. pParams->prcView->bottom - pParams->prcView->top,
  9655. _hwndMain, NULL, HINST_THISDLL, this))
  9656. {
  9657. // See if they want to overwrite the selection object
  9658. if (_fs.fFlags & FWF_OWNERDATA)
  9659. {
  9660. // Only used in owner data.
  9661. ILVRange *plvr = NULL;
  9662. CallCB(SFVM_GETODRANGEOBJECT, LVSR_SELECTION, (LPARAM)&plvr);
  9663. if (plvr)
  9664. {
  9665. ListView_SetLVRangeObject(_hwndListview, LVSR_SELECTION, plvr);
  9666. plvr->Release(); // We assume the lv will hold onto it...
  9667. }
  9668. plvr = NULL;
  9669. CallCB(SFVM_GETODRANGEOBJECT, LVSR_CUT, (LPARAM)&plvr);
  9670. if (plvr)
  9671. {
  9672. ListView_SetLVRangeObject(_hwndListview, LVSR_CUT, plvr);
  9673. plvr->Release(); // We assume the lv will hold onto it...
  9674. }
  9675. }
  9676. // This needs to be done before calling _BestFit (used to be in _FillObjects)
  9677. // so that the parent can handle size changes effectively.
  9678. pParams->hwndView = _hwndView;
  9679. // Since ::FillObjects can take a while we force a paint now
  9680. // before any items are added so we don't see the gray background of
  9681. // the explorer window for a long time.
  9682. //
  9683. // We used to do this after determining "async-ness" of the view, which
  9684. // required us to pick the webview template. We want to postpone that
  9685. // decision so force the repaint in the same scenarios that we otherwise
  9686. // would have (non-webview or desktop).
  9687. //
  9688. // Make an educated guess here, if we get it wrong, we fix it up below.
  9689. //
  9690. if (!_ShouldShowWebView() || _IsDesktop())
  9691. {
  9692. _ShowViewEarly();
  9693. }
  9694. // Try and fill the listview synchronously with view creation.
  9695. //
  9696. _fAllowSearchingWindow = TRUE;
  9697. hr = _pEnumTask->FillObjectsToDPA(TRUE);
  9698. if (SUCCEEDED(hr))
  9699. {
  9700. // Setting the view mode has to happen after SFVM_ENUMERATEDITEMS
  9701. // NOTE: this also AddColumns() if the new view requires them
  9702. if (FVM_LAST + 1 == fvm)
  9703. _GetDeferredViewSettings(&fvm);
  9704. // Don't call SetCurrentViewMode since it clears position data and we may have read in
  9705. // position data via GetViewState but haven't used it yet. Call _SwitchToViewFVM directly.
  9706. hr = _SwitchToViewFVM(fvm, SWITCHTOVIEW_NOWEBVIEW);
  9707. // The following bits depend on the result of _SwitchToViewFVM.
  9708. // It returns the value from turning on web view,
  9709. // this is used to determine async defview behavior (so we have
  9710. // an answer to the SHDVID_CANACTIVATENOW question the browser
  9711. // will soon ask us).
  9712. //
  9713. // Note: Desktop synchronous, even for web view
  9714. //
  9715. if (SUCCEEDED(hr) && _IsDesktop())
  9716. hr = S_OK;
  9717. _fCanActivateNow = (S_OK == hr); // S_FALSE implies async waiting for ReadyStateInteractive
  9718. _fIsAsyncDefView = !BOOLIFY(_fCanActivateNow); // needed in a separate bit since _fCanActivateNow changes
  9719. // This has to happen after _SwitchToViewFVM so it can calculate
  9720. // the correct size of the window
  9721. _BestFit();
  9722. // Tell the defview client that this windows has been initialized
  9723. // Note that this must come before _pEnumTask->FillObjectsDPAToDone() so that the status bar displays
  9724. // (Disk Free space xxGB) correctly in explorer view.
  9725. CallCB(SFVM_WINDOWCREATED, (WPARAM)_hwndView, 0);
  9726. //
  9727. // If this is desktop, we need to calc and upgrade the grid sizes.
  9728. // (This is needed because the SnapToGrid may be ON and the default grid size
  9729. // will result in large gutter space on the edges).
  9730. //
  9731. if (_IsDesktop())
  9732. {
  9733. DWORD dwLVExStyle = ListView_GetExtendedListViewStyle(_hwndListview);
  9734. //
  9735. //Since the work areas are NOT yet set for the desktop's listview (because this is too early
  9736. //in it's creation, we pass just one work area and the view rect as work area here.)
  9737. //
  9738. UpdateGridSizes(TRUE, _hwndListview, 1, pParams->prcView, BOOLIFY(dwLVExStyle & LVS_EX_SNAPTOGRID));
  9739. }
  9740. // Doing this after _BestFit means we dont need to auto-auto arrange
  9741. _pEnumTask->FillObjectsDPAToDone();
  9742. // splitting this function call in half means that we won't call WebView with contents changed for initial population
  9743. _SwitchToViewFVM(fvm, SWITCHTOVIEW_WEBVIEWONLY);
  9744. // If we're activating now, make sure we did the synchronous thing up above...
  9745. // (If not, do it now -- otherwise defview may never be shown)
  9746. if (_fCanActivateNow && !(!_ShouldShowWebView() || _IsDesktop()))
  9747. {
  9748. _ShowViewEarly();
  9749. }
  9750. if (_IsDesktop())
  9751. {
  9752. HideIE4DesktopChannelBar();
  9753. }
  9754. // turn on proper background and colors
  9755. _fClassic = ss.fWin95Classic;
  9756. _UpdateListviewColors();
  9757. // this needs to be done after the enumeration
  9758. if (_SetupNotifyData())
  9759. {
  9760. SHChangeNotifyEntry fsne = {0};
  9761. if (FAILED(CallCB(SFVM_QUERYFSNOTIFY, 0, (LPARAM)&fsne)))
  9762. {
  9763. // Reset entry
  9764. fsne.pidl = _pidlMonitor;
  9765. fsne.fRecursive = FALSE;
  9766. }
  9767. int iSources = (_lFSEvents & SHCNE_DISKEVENTS) ? SHCNRF_ShellLevel | SHCNRF_InterruptLevel : SHCNRF_ShellLevel;
  9768. LONG lEvents = _lFSEvents | SHCNE_UPDATEIMAGE | SHCNE_UPDATEDIR;
  9769. _uRegister = SHChangeNotifyRegister(_hwndView, SHCNRF_NewDelivery | iSources,
  9770. lEvents, WM_DSV_FSNOTIFY, 1, &fsne);
  9771. }
  9772. // We do the toolbar before the menu bar to avoid flash
  9773. if (!_IsDesktop())
  9774. MergeToolBar(TRUE);
  9775. // Note: it's okay for the CreateViewObject(&_pdtgtBack) to fail
  9776. ASSERT(_pdtgtBack == NULL);
  9777. _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IDropTarget, &_pdtgtBack));
  9778. // we don't really need to register drag drop when in the shell because
  9779. // our frame does it for us. we still need it here for comdlg and other
  9780. // hosts.. but for the desktop, let the desktpo frame take care of this
  9781. // so that they can do webbar d/d creation
  9782. if (!_IsDesktop())
  9783. {
  9784. THR(RegisterDragDrop(_hwndListview, SAFECAST(this, IDropTarget*)));
  9785. _bRegisteredDragDrop = TRUE;
  9786. }
  9787. ASSERT(SUCCEEDED(hr))
  9788. PostMessage(_hwndView, WM_DSV_DELAYWINDOWCREATE, 0, 0);
  9789. if (SUCCEEDED(CallCB(SFVM_QUERYCOPYHOOK, 0, 0)))
  9790. AddCopyHook();
  9791. if (SUCCEEDED(_GetIPersistHistoryObject(NULL)))
  9792. {
  9793. IBrowserService *pbs;
  9794. if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs))))
  9795. {
  9796. IOleObject *pole;
  9797. IStream *pstm;
  9798. IBindCtx *pbc;
  9799. pbs->GetHistoryObject(&pole, &pstm, &pbc);
  9800. if (pole)
  9801. {
  9802. IUnknown_SetSite(pole, SAFECAST(this, IShellView2*)); // Set the back pointer.
  9803. if (pstm)
  9804. {
  9805. IPersistHistory *pph;
  9806. if (SUCCEEDED(pole->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph))))
  9807. {
  9808. pph->LoadHistory(pstm, pbc);
  9809. pph->Release();
  9810. }
  9811. pstm->Release();
  9812. }
  9813. IUnknown_SetSite(pole, NULL); // just to be safe...
  9814. if (pbc)
  9815. pbc->Release();
  9816. pole->Release();
  9817. }
  9818. pbs->Release();
  9819. }
  9820. }
  9821. if (_psb && !_dwProffered)
  9822. {
  9823. // Proffer DVGetEnum service: this connects CDefView with the tree control for
  9824. // optimized navigation.
  9825. IUnknown_ProfferService(_psb, SID_SFolderView, SAFECAST(this, IServiceProvider *), &_dwProffered);
  9826. // Failure here does not require special handling
  9827. }
  9828. }
  9829. else
  9830. {
  9831. // Cleanup - enum failed.
  9832. DestroyViewWindow();
  9833. }
  9834. }
  9835. else
  9836. {
  9837. hr = E_OUTOFMEMORY;
  9838. }
  9839. _GlobeAnimation(FALSE);
  9840. ResetWaitCursor();
  9841. return hr;
  9842. }
  9843. struct SCHEDULER_AND_HWND {
  9844. IShellTaskScheduler *pScheduler;
  9845. HWND hwnd;
  9846. };
  9847. STDMETHODIMP CDefView::DestroyViewWindow()
  9848. {
  9849. if (_fDestroying)
  9850. return S_OK;
  9851. if (_psb && _dwProffered)
  9852. {
  9853. // Revoke DVGetEnum service
  9854. IUnknown_ProfferService(_psb, SID_SFolderView, NULL, &_dwProffered);
  9855. // Failure here does not require special handling
  9856. }
  9857. // Make sure that we stop the spinning globe before going away.
  9858. _GlobeAnimation(FALSE, TRUE);
  9859. _fDestroying = TRUE;
  9860. // 99/04/16 #326158 vtan: Loop thru the headers looking for
  9861. // stray HBITMAPs which need to be DeleteObject'd. Don't bother
  9862. // setting it back the header is about to be dumped.
  9863. // NOTE: Make sure this gets executed BEFORE the view gets
  9864. // dumped below in DestoryViewWindow().
  9865. if (IsWindow(_hwndListview))
  9866. {
  9867. HWND hwndHeader = ListView_GetHeader(_hwndListview);
  9868. if (IsWindow(hwndHeader))
  9869. {
  9870. int iHeaderCount = Header_GetItemCount(hwndHeader);
  9871. for (int i = 0; i < iHeaderCount; ++i)
  9872. {
  9873. HDITEM hdi = {0};
  9874. hdi.mask = HDI_BITMAP;
  9875. Header_GetItem(hwndHeader, i, &hdi);
  9876. if (hdi.hbm != NULL)
  9877. TBOOL(DeleteObject(hdi.hbm));
  9878. }
  9879. }
  9880. }
  9881. _cFrame.HideWebView();
  9882. //
  9883. // Just in case...
  9884. //
  9885. OnDeactivate();
  9886. if (IsWindow(_hwndView))
  9887. {
  9888. //
  9889. // This is a bit lazy implementation, but minimum code.
  9890. //
  9891. RemoveCopyHook();
  9892. // Tell the defview client that this window will be destroyed
  9893. CallCB(SFVM_WINDOWDESTROY, (WPARAM)_hwndView, 0);
  9894. }
  9895. if (IsWindow(_hwndView))
  9896. {
  9897. if (_pScheduler)
  9898. {
  9899. // empty the queue but do NOT wait until it is empty.....
  9900. _pScheduler->RemoveTasks(TOID_NULL, ITSAT_DEFAULT_LPARAM, FALSE);
  9901. // If there is still a task going, then kill our window later, as to not
  9902. // block the UI thread.
  9903. #ifdef DEBUG
  9904. // Stress the feature in debug mode
  9905. if (1)
  9906. #else
  9907. if (_GetBackgroundTaskCount(TOID_NULL) > 0)
  9908. #endif
  9909. {
  9910. ShowWindow(_hwndView, SW_HIDE);
  9911. // We are NOT passing 'this' defview pointer to the background thread
  9912. // because we do not want the destructor of defview to be called on any
  9913. // thread other than the one it was created on.
  9914. SCHEDULER_AND_HWND *pData = (SCHEDULER_AND_HWND *)LocalAlloc(LPTR, sizeof(*pData));
  9915. if (pData)
  9916. {
  9917. _pScheduler->AddRef();
  9918. pData->pScheduler = _pScheduler;
  9919. pData->hwnd = _hwndView;
  9920. // We need to keep Browseui loaded because we depend on the CShellTaskScheduler
  9921. // to be still around when our background task executes. Browseui can be unloaded by COM when
  9922. // we CoUninit from this thread.
  9923. if (SHQueueUserWorkItem(CDefView::BackgroundDestroyWindow, pData, 0, NULL, NULL, "browseui.dll", 0))
  9924. goto exit;
  9925. else
  9926. {
  9927. LocalFree(pData);
  9928. _pScheduler->Release();
  9929. }
  9930. }
  9931. }
  9932. }
  9933. DestroyWindow(_hwndView);
  9934. }
  9935. exit:
  9936. return S_OK;
  9937. }
  9938. DWORD CDefView::BackgroundDestroyWindow(void *pvData)
  9939. {
  9940. SCHEDULER_AND_HWND *pData = (SCHEDULER_AND_HWND *)pvData;
  9941. // Note: the window coud have been already destroyed before we get here
  9942. // in the case where the frame gets closed down.
  9943. if (IsWindow(pData->hwnd))
  9944. {
  9945. // Remove all tasks
  9946. EmptyBkgrndThread(pData->pScheduler);
  9947. // We need to release before we post to ensure that browseui doesn't get unloaded from under us (pScheduler is
  9948. // in browseui.dll). Browseui can get unloaded when we uninitialize OLE's MTA, even if there are still refs on the DLL.
  9949. pData->pScheduler->Release();
  9950. PostMessage(pData->hwnd, WM_DSV_DELAYED_DESTROYWND, 0, 0);
  9951. }
  9952. else
  9953. {
  9954. pData->pScheduler->Release();
  9955. }
  9956. LocalFree(pData);
  9957. return 0;
  9958. }
  9959. void CDefView::_MergeViewMenu(HMENU hmenuViewParent, HMENU hmenuMerge)
  9960. {
  9961. HMENU hmenuView = _GetMenuFromID(hmenuViewParent, FCIDM_MENU_VIEW);
  9962. if (hmenuView)
  9963. {
  9964. #ifdef DEBUG
  9965. DWORD dwValue;
  9966. DWORD cbSize = sizeof(dwValue);
  9967. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER,
  9968. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
  9969. TEXT("DebugWebView"), NULL, &dwValue, &cbSize)
  9970. && dwValue)
  9971. {
  9972. MENUITEMINFO mi = {0};
  9973. mi.cbSize = sizeof(mi);
  9974. mi.fMask = MIIM_TYPE|MIIM_ID;
  9975. mi.fType = MFT_STRING;
  9976. mi.dwTypeData = TEXT("Show WebView Content");
  9977. mi.wID = SFVIDM_DEBUG_WEBVIEW;
  9978. InsertMenuItem(hmenuMerge, -1, MF_BYPOSITION, &mi);
  9979. }
  9980. #endif
  9981. //
  9982. // Find the "options" separator in the view menu.
  9983. //
  9984. int index = MenuIndexFromID(hmenuView, FCIDM_MENU_VIEW_SEP_OPTIONS);
  9985. //
  9986. // Here, index is the index of he "optoins" separator if it has;
  9987. // otherwise, it is -1.
  9988. //
  9989. // Add the separator above (in addition to existing one if any).
  9990. InsertMenu(hmenuView, index, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  9991. // Then merge our menu between two separators (or right below if only one).
  9992. if (index != -1)
  9993. {
  9994. index++;
  9995. }
  9996. Shell_MergeMenus(hmenuView, hmenuMerge, (UINT)index, 0, (UINT)-1, MM_SUBMENUSHAVEIDS);
  9997. }
  9998. }
  9999. void CDefView::_SetUpMenus(UINT uState)
  10000. {
  10001. //
  10002. // If this is desktop, don't bother creating menu
  10003. //
  10004. if (!_IsDesktop())
  10005. {
  10006. OnDeactivate();
  10007. ASSERT(_hmenuCur == NULL);
  10008. HMENU hMenu = CreateMenu();
  10009. if (hMenu)
  10010. {
  10011. HMENU hMergeMenu;
  10012. OLEMENUGROUPWIDTHS mwidth = { { 0, 0, 0, 0, 0, 0 } };
  10013. _hmenuCur = hMenu;
  10014. _psb->InsertMenusSB(hMenu, &mwidth);
  10015. if (uState == SVUIA_ACTIVATE_FOCUS)
  10016. {
  10017. hMergeMenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_SFV_MAINMERGE));
  10018. if (hMergeMenu)
  10019. {
  10020. // NOTE: hard coded references to offsets in this menu
  10021. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_FILE),
  10022. GetSubMenu(hMergeMenu, 0), (UINT)0, 0, (UINT)-1,
  10023. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS);
  10024. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
  10025. GetSubMenu(hMergeMenu, 1), (UINT)-1, 0, (UINT)-1,
  10026. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS);
  10027. _MergeViewMenu(hMenu, GetSubMenu(hMergeMenu, 2));
  10028. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_HELP),
  10029. GetSubMenu(hMergeMenu, 3), (UINT)0, 0, (UINT)-1,
  10030. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  10031. DestroyMenu(hMergeMenu);
  10032. }
  10033. }
  10034. else
  10035. {
  10036. hMergeMenu = LoadMenu(HINST_THISDLL, MAKEINTRESOURCE(POPUP_SFV_MAINMERGENF));
  10037. if (hMergeMenu)
  10038. {
  10039. // NOTE: hard coded references to offsets in this menu
  10040. // top half of edit menu
  10041. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
  10042. GetSubMenu(hMergeMenu, 0), (UINT)0, 0, (UINT)-1,
  10043. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  10044. // bottom half of edit menu
  10045. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_EDIT),
  10046. GetSubMenu(hMergeMenu, 1), (UINT)-1, 0, (UINT)-1,
  10047. MM_SUBMENUSHAVEIDS);
  10048. // view menu
  10049. _MergeViewMenu(hMenu, GetSubMenu(hMergeMenu, 2));
  10050. Shell_MergeMenus(_GetMenuFromID(hMenu, FCIDM_MENU_HELP),
  10051. GetSubMenu(hMergeMenu, 3), (UINT)0, 0, (UINT)-1,
  10052. MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
  10053. DestroyMenu(hMergeMenu);
  10054. }
  10055. }
  10056. // Allow the client to merge its own menus
  10057. UINT indexClient = GetMenuItemCount(hMenu)-1;
  10058. QCMINFO info = { hMenu, indexClient, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST };
  10059. CallCB(SFVM_MERGEMENU, 0, (LPARAM)&info);
  10060. _psb->SetMenuSB(hMenu, NULL, _hwndView);
  10061. }
  10062. }
  10063. }
  10064. // set up the menus based on our activation state
  10065. //
  10066. BOOL CDefView::OnActivate(UINT uState)
  10067. {
  10068. if (_uState != uState)
  10069. {
  10070. _SetUpMenus(uState);
  10071. _uState = uState;
  10072. }
  10073. return TRUE;
  10074. }
  10075. BOOL CDefView::OnDeactivate()
  10076. {
  10077. if (_hmenuCur || (_uState != SVUIA_DEACTIVATE))
  10078. {
  10079. if (!_IsDesktop())
  10080. {
  10081. ASSERT(_hmenuCur);
  10082. CallCB(SFVM_UNMERGEMENU, 0, (LPARAM)_hmenuCur);
  10083. _psb->SetMenuSB(NULL, NULL, NULL);
  10084. _psb->RemoveMenusSB(_hmenuCur);
  10085. DestroyMenu(_hmenuCur);
  10086. _hmenuCur = NULL;
  10087. }
  10088. _uState = SVUIA_DEACTIVATE;
  10089. }
  10090. return TRUE;
  10091. }
  10092. void CDefView::_OnMoveWindowToTop(HWND hwnd)
  10093. {
  10094. //
  10095. // Let the browser know that this has happened
  10096. //
  10097. VARIANT var;
  10098. var.vt = VT_INT_PTR;
  10099. var.byref = hwnd;
  10100. IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_ONVIEWMOVETOTOP, 0, &var, NULL);
  10101. }
  10102. //
  10103. // This function activates the view window. Note that activating it
  10104. // will not change the focus (while setting the focus will activate it).
  10105. //
  10106. STDMETHODIMP CDefView::UIActivate(UINT uState)
  10107. {
  10108. if (SVUIA_DEACTIVATE == uState)
  10109. {
  10110. OnDeactivate();
  10111. ASSERT(_hmenuCur==NULL);
  10112. }
  10113. else
  10114. {
  10115. if (_fIsAsyncDefView)
  10116. {
  10117. // Need to show the defview window for the Async Case only. Showing
  10118. // it earlier causes repaint problems(Bug 275266). Showing the window
  10119. // here for the Sync case also causes problems - when the client
  10120. // creates a Synchronous Defview and then hides it later which gets
  10121. // lost with this SetWindowPos (Bug 355392).
  10122. SetWindowPos(_hwndView, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
  10123. UpdateWindow(_hwndView);
  10124. _OnMoveWindowToTop(_hwndView);
  10125. }
  10126. if (uState == SVUIA_ACTIVATE_NOFOCUS)
  10127. {
  10128. // we lost focus
  10129. // if in web view and we have valid ole obj (just being paranoid)
  10130. if (!_fCombinedView && _cFrame.IsWebView() && _cFrame._pOleObj)
  10131. {
  10132. _cFrame._UIActivateIO(FALSE, NULL);
  10133. }
  10134. }
  10135. // We may be waiting for ReadyState_Interactive. If requested,
  10136. // we should activate before then...
  10137. //
  10138. // When we boot, the desktop paints ugly white screen for several
  10139. // seconds before it shows the HTML content. This is because: the
  10140. // following code switches the oleobj even before it reaches readystate
  10141. // interactive. For desktop, we skip this here. When the new object
  10142. // reaches readystate interactive, we will show it!
  10143. if (!_IsDesktop())
  10144. {
  10145. _cFrame._SwitchToNewOleObj();
  10146. // NOTE: The browser IP/UI-activates us when we become the
  10147. // current active view! We want to resize and show windows
  10148. // at that time. But if we're still waiting for _fCanActivateNow
  10149. // (ie, ReadyStateInteractive), then we need to cache this request
  10150. // and do it later. NOTE: if Trident caches the focus (done w/ TAB)
  10151. // then we don't need to do anything here...
  10152. //
  10153. if (uState == SVUIA_ACTIVATE_FOCUS)
  10154. {
  10155. _SetFocus();
  10156. // _SetFocus can set _uState without causing our menu to
  10157. // get created and merged. Clear it here so that OnActivate does the
  10158. // right thing.
  10159. if (!_hmenuCur)
  10160. _uState = SVUIA_DEACTIVATE;
  10161. }
  10162. }
  10163. // else we are the desktop; do we also need to steal focus?
  10164. else if (uState == SVUIA_ACTIVATE_FOCUS)
  10165. {
  10166. HWND hwnd = GetFocus();
  10167. if (SHIsChildOrSelf(_hwndView, hwnd) != S_OK)
  10168. _SetFocus();
  10169. }
  10170. // OnActivate must follow _SetFocus
  10171. OnActivate(uState);
  10172. ShowHideListView();
  10173. ASSERT(_IsDesktop() || _hmenuCur);
  10174. _cFrame._UpdateZonesStatusPane(NULL);
  10175. }
  10176. return S_OK;
  10177. }
  10178. STDMETHODIMP CDefView::GetCurrentInfo(LPFOLDERSETTINGS pfs)
  10179. {
  10180. *pfs = _fs;
  10181. return S_OK;
  10182. }
  10183. BOOL IsBackSpace(const MSG *pMsg)
  10184. {
  10185. return pMsg && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_BACK);
  10186. }
  10187. extern int IsVK_TABCycler(MSG *pMsg);
  10188. //***
  10189. // NOTES
  10190. // try ListView->TA first
  10191. // then if that fails try WebView->TA iff it has focus.
  10192. // then if that fails and it's a TAB we do WebView->UIAct
  10193. STDMETHODIMP CDefView::TranslateAccelerator(LPMSG pmsg)
  10194. {
  10195. // 1st, try ListView
  10196. if (_fInLabelEdit)
  10197. {
  10198. // the second clause stops us passing mouse key clicks to the toolbar if we are in label edit mode...
  10199. if (WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message)
  10200. {
  10201. // process this msg so the exploer does not get to translate
  10202. TranslateMessage(pmsg);
  10203. DispatchMessage(pmsg);
  10204. return S_OK; // we handled it
  10205. }
  10206. else
  10207. return S_FALSE;
  10208. }
  10209. // If we are in classic mode and if it's a tab and the listview doesn't have focus already, receive the tab.
  10210. else if (IsVK_TABCycler(pmsg) && !(_cFrame.IsWebView() || _pDUIView) && (GetFocus() != _hwndListview))
  10211. {
  10212. _SetFocus();
  10213. return S_OK;
  10214. }
  10215. if (GetFocus() == _hwndListview)
  10216. {
  10217. if (::TranslateAccelerator(_hwndView, _hAccel, pmsg))
  10218. {
  10219. // we know we have a normal view, therefore this is
  10220. // the right translate accelerator to use, otherwise the
  10221. // common dialogs will fail to get any accelerated keys.
  10222. return S_OK;
  10223. }
  10224. else if (WM_KEYDOWN == pmsg->message || WM_SYSKEYDOWN == pmsg->message)
  10225. {
  10226. // MSHTML eats these keys for frameset scrolling, but we
  10227. // want to get them to our wndproc . . . translate 'em ourself
  10228. //
  10229. switch (pmsg->wParam)
  10230. {
  10231. case VK_LEFT:
  10232. case VK_RIGHT:
  10233. // only go through here if alt is not down.
  10234. // don't intercept all alt combinations because
  10235. // alt-enter means something
  10236. // this is for alt-left/right compat with IE
  10237. if (GetAsyncKeyState(VK_MENU) < 0)
  10238. break;
  10239. // fall through
  10240. case VK_UP:
  10241. case VK_DOWN:
  10242. case VK_HOME:
  10243. case VK_END:
  10244. case VK_PRIOR:
  10245. case VK_NEXT:
  10246. case VK_RETURN:
  10247. case VK_F10:
  10248. TranslateMessage(pmsg);
  10249. DispatchMessage(pmsg);
  10250. return S_OK;
  10251. }
  10252. }
  10253. }
  10254. // 1.5th, before we pass it down, see whether shell browser handles it.
  10255. // we do this to make sure that webview has the same accelerator semantics
  10256. // no matter what view(s) are active.
  10257. // note that this is arguably inconsistent w/ the 'pass it to whoever has
  10258. // focus'.
  10259. //
  10260. // however *don't* do this if:
  10261. // - we're in a dialog (in which case the buttons should come 1st)
  10262. // (comdlg's shellbrowser xxx::TA impl is broken it always does S_OK)
  10263. // - it's a TAB (which is always checked last)
  10264. // - it's a BACKSPACE (we should give the currently active object the first chance).
  10265. // However, in this case, we should call TranslateAcceleratorSB() AFTER we've tried
  10266. // calling TranslateAccelerator() on the currently active control (_pActive) in
  10267. // _cFrame->OnTranslateAccelerator().
  10268. //
  10269. // note: if you muck w/ this code careful not to regress the following:
  10270. // - ie41:62140: mnemonics broken after folder selected in organize favs
  10271. // - ie41:62419: TAB activates addr and menu if folder selected in explorer
  10272. if (!_IsCommonDialog() && !IsVK_TABCycler(pmsg) && !IsBackSpace(pmsg))
  10273. if (S_OK == _psb->TranslateAcceleratorSB(pmsg, 0))
  10274. return S_OK;
  10275. BOOL bTabOffLastTridentStop = FALSE;
  10276. BOOL bHadIOFocus = (_cFrame._HasFocusIO() == S_OK); // Cache this here before the _cFrame.OnTA() call below
  10277. // 2nd, try WebView if it's active
  10278. if (IsVK_TABCycler(pmsg) && _pDUIView)
  10279. {
  10280. if (_pDUIView->Navigate(GetAsyncKeyState(VK_SHIFT) >= 0))
  10281. return S_OK;
  10282. }
  10283. if (_cFrame.IsWebView() && (S_OK == _cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop)))
  10284. {
  10285. return S_OK;
  10286. }
  10287. // We've given _pActive->TranslateAccelerator() the first shot in
  10288. // _cFrame.OnTranslateAccelerator, but it failed. Let's try the shell browser.
  10289. if (IsBackSpace(pmsg) && (S_OK == _psb->TranslateAcceleratorSB(pmsg, 0)))
  10290. return S_OK;
  10291. // 3rd, ???
  10292. if (::TranslateAccelerator(_hwndView, _hAccel, pmsg))
  10293. return S_OK;
  10294. // 4th, if it's a TAB, cycle to next guy
  10295. // hack: we fake a bunch of the TAB-activation handshaking
  10296. if (IsVK_TABCycler(pmsg) && _cFrame.IsWebView())
  10297. {
  10298. HRESULT hr;
  10299. BOOL fBack = (GetAsyncKeyState(VK_SHIFT) < 0);
  10300. if (!bHadIOFocus && bTabOffLastTridentStop)
  10301. {
  10302. // We were at the last tab stop in trident when the browser called defview->TA().
  10303. // When we called TA() on trident above, it must've told us that we are tabbing
  10304. // off the last tab stop (bTabOffLastTridentStop). This will leave us not setting focus
  10305. // on anything. But, we have to set focus to something. We can do this by calling TA()
  10306. // on trident again, which will set focus on the first tab stop again.
  10307. return _cFrame.OnTranslateAccelerator(pmsg, &bTabOffLastTridentStop);
  10308. }
  10309. else if (_cFrame._HasFocusIO() == S_OK)
  10310. {
  10311. // ExtView has focus, and doesn't want the TAB.
  10312. // this means we're TABing off of it.
  10313. // no matter what, deactivate it (since we're TABing off).
  10314. // if the view is next in the TAB order, (pseudo-)activate it,
  10315. // and return S_OK since we've handled it.
  10316. // o.w. return S_OK so our parent will activate whoever's next
  10317. // in the TAB order.
  10318. hr = _cFrame._UIActivateIO(FALSE, NULL);
  10319. ASSERT(hr == S_OK);
  10320. // in web view listview already has focus so don't give it again
  10321. // that's not the case with desktop
  10322. if (fBack && _IsDesktop())
  10323. {
  10324. SetFocus(_hwndListview);
  10325. return S_OK;
  10326. }
  10327. return S_FALSE;
  10328. }
  10329. else
  10330. {
  10331. if (!fBack)
  10332. {
  10333. hr = _cFrame._UIActivateIO(TRUE, pmsg);
  10334. ASSERT(hr == S_OK || hr == S_FALSE);
  10335. return hr;
  10336. }
  10337. }
  10338. }
  10339. return S_FALSE;
  10340. }
  10341. // Description:
  10342. // Regenerates the CDefView's menus. Used for regaining any menu items
  10343. // which may have been stripped via DeleteMenu(), as occurs for various
  10344. // particular view states.
  10345. //
  10346. // Example: Transitioning to a barricaded view automatically strips out
  10347. // a number of commands from the "View" menu which are not appropriate
  10348. // for the barricaded view. Thus, on the transition back out of the
  10349. // barricaded view, the menus must be recreated in order to regain
  10350. // any/all the menu items stripped (this is not to say a number of
  10351. // them may not be stripped again if we're just transitioning to
  10352. // another view which doesn't want them in there!).
  10353. //
  10354. void CDefView::RecreateMenus()
  10355. {
  10356. UINT uState = _uState;
  10357. _SetUpMenus(uState); // Note _SetupMenus() calls OnDeactivate()
  10358. _uState = uState; // which sets _uState to SVUIA_DEACTIVATE.
  10359. }
  10360. void CDefView::InitViewMenu(HMENU hmInit)
  10361. {
  10362. // Initialize view menu accordingly...
  10363. if (_fBarrierDisplayed)
  10364. _InitViewMenuWhenBarrierDisplayed(hmInit);
  10365. else
  10366. _InitViewMenuWhenBarrierNotDisplayed(hmInit);
  10367. // Remove any extraneous menu separators arising from initialization.
  10368. _SHPrettyMenu(hmInit);
  10369. }
  10370. // Description:
  10371. // Used to initialize the entries of the "View" menu and its associated
  10372. // submenus whenever a soft barrier is being displayed.
  10373. //
  10374. // Note:
  10375. // This method is also employed when "Category View" is being used in
  10376. // browsing the Control Panel.
  10377. //
  10378. void CDefView::_InitViewMenuWhenBarrierDisplayed(HMENU hmenuView)
  10379. {
  10380. // If "list view" is not visible (i.e. we're in Category View in the
  10381. // Control Panel, or we're looking at a barricaded folder), we strip
  10382. // out the following stuff from the View menu:
  10383. //
  10384. // Filmstrip
  10385. // Thumbnails
  10386. // Tiles
  10387. // Icons
  10388. // List
  10389. // Details
  10390. // -------------------
  10391. // Arrange Icons By ->
  10392. // -------------------
  10393. // Choose Details...
  10394. // Customize This Folder...
  10395. // Remove menu entries.
  10396. DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBSTRIP, MF_BYCOMMAND);
  10397. DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBNAIL, MF_BYCOMMAND);
  10398. DeleteMenu(hmenuView, SFVIDM_VIEW_TILE, MF_BYCOMMAND);
  10399. DeleteMenu(hmenuView, SFVIDM_VIEW_ICON, MF_BYCOMMAND);
  10400. DeleteMenu(hmenuView, SFVIDM_VIEW_LIST, MF_BYCOMMAND);
  10401. DeleteMenu(hmenuView, SFVIDM_VIEW_DETAILS, MF_BYCOMMAND);
  10402. DeleteMenu(hmenuView, SFVIDM_MENU_ARRANGE, MF_BYCOMMAND);
  10403. DeleteMenu(hmenuView, SFVIDM_VIEW_COLSETTINGS, MF_BYCOMMAND);
  10404. DeleteMenu(hmenuView, SFVIDM_VIEW_CUSTOMWIZARD, MF_BYCOMMAND);
  10405. }
  10406. // Description:
  10407. // Used to initialize the entries of the "View" menu and its associated
  10408. // submenus whenever a soft barrier is not being displayed.
  10409. //
  10410. void CDefView::_InitViewMenuWhenBarrierNotDisplayed(HMENU hmenuView)
  10411. {
  10412. DWORD dwListViewFlags = ListView_GetExtendedListViewStyle(_hwndListview);
  10413. UINT uEnabled = (MF_ENABLED | MF_BYCOMMAND);
  10414. UINT uDisabled = (MF_GRAYED | MF_BYCOMMAND);
  10415. UINT uChecked = (MF_CHECKED | MF_BYCOMMAND);
  10416. UINT uUnchecked = (MF_UNCHECKED | MF_BYCOMMAND);
  10417. UINT uAAEnable; // Auto Arrange
  10418. UINT uAACheck;
  10419. UINT uAGrEnable; // Align to Grid
  10420. UINT uAGrCheck;
  10421. // Initialize "view" menu entries.
  10422. _InitViewMenuViewsWhenBarrierNotDisplayed(hmenuView);
  10423. // Initialize "Arrange Icons By ->" submenu.
  10424. _InitArrangeMenu(hmenuView);
  10425. // Determine and set appropriate enable state for "Auto Arrange" and "Align to Grid".
  10426. if (_IsPositionedView() && _IsListviewVisible() && !(_fs.ViewMode == FVM_THUMBSTRIP))
  10427. uAAEnable = uAGrEnable = uEnabled;
  10428. else
  10429. uAAEnable = uAGrEnable = uDisabled;
  10430. EnableMenuItem(hmenuView, SFVIDM_ARRANGE_AUTO, uAAEnable);
  10431. EnableMenuItem(hmenuView, SFVIDM_ARRANGE_AUTOGRID, uAGrEnable);
  10432. // Determine and set appropriate check state for "Auto Arrange" and "Align to Grid".
  10433. uAACheck = (((uAAEnable == uEnabled) || _fGroupView || (_fs.ViewMode == FVM_THUMBSTRIP)) && _IsAutoArrange())
  10434. ? uChecked
  10435. : uUnchecked;
  10436. uAGrCheck = (((uAGrEnable == uEnabled) || _fGroupView) && (dwListViewFlags & LVS_EX_SNAPTOGRID))
  10437. ? uChecked
  10438. : uUnchecked;
  10439. CheckMenuItem(hmenuView, SFVIDM_ARRANGE_AUTO, uAACheck);
  10440. CheckMenuItem(hmenuView, SFVIDM_ARRANGE_AUTOGRID, uAGrCheck);
  10441. // If icons are not being shown (such as can be set on the
  10442. // desktop), disable ALL icon-arrangement related commands.
  10443. if (!_IsListviewVisible())
  10444. {
  10445. HMENU hArrangeSubMenu;
  10446. UINT uID;
  10447. int i = 0;
  10448. // Retrieve "Arrange Icons By ->" submenu.
  10449. hArrangeSubMenu = GetSubMenu(hmenuView, 2);
  10450. // Iterate and disable until we get to "Show Icons".
  10451. while (1)
  10452. {
  10453. uID = GetMenuItemID(hArrangeSubMenu, i);
  10454. if ((uID == SFVIDM_DESKTOPHTML_ICONS) || (uID == (UINT)-1))
  10455. break;
  10456. else
  10457. EnableMenuItem(hArrangeSubMenu, i, MF_GRAYED | MF_BYPOSITION);
  10458. i++;
  10459. }
  10460. }
  10461. else if (!_ShouldShowWebView())
  10462. {
  10463. // If Web View is off, then thumbstrip will never work...
  10464. DeleteMenu(hmenuView, SFVIDM_VIEW_THUMBSTRIP, MF_BYCOMMAND);
  10465. }
  10466. // Remove "Customize This Folder..." if folder is not customizable.
  10467. if (!_CachedIsCustomizable())
  10468. {
  10469. // The Folder Option "Classic style" and the shell restriction WIN95CLASSIC
  10470. // should be the same. (Per ChristoB, otherwise admin's never understand what
  10471. // the restriction means.) Since we want this to change DEFAULTs, and still
  10472. // allow the user to turn on Web View, we don't remove the customize wizard here.
  10473. int iIndex = MenuIndexFromID(hmenuView, SFVIDM_VIEW_CUSTOMWIZARD);
  10474. if (iIndex != -1)
  10475. {
  10476. DeleteMenu(hmenuView, iIndex + 1, MF_BYPOSITION); // Remove Menu seperator
  10477. DeleteMenu(hmenuView, iIndex, MF_BYPOSITION); // Remove Customize
  10478. }
  10479. }
  10480. }
  10481. // Description:
  10482. // Initializes the "view" entries on a view menu. This involves stripping
  10483. // out any "view" entries for unsupported views, and additionally checking
  10484. // of the appropriate "view" entry for the current view.
  10485. //
  10486. // Note:
  10487. // This method should not be called if a soft barrier is being displayed.
  10488. // Remember that in this case there is no concept of a view, so why
  10489. // would someone be attempting to initialize "view" menu entries.
  10490. //
  10491. void CDefView::_InitViewMenuViewsWhenBarrierNotDisplayed(HMENU hmenuView)
  10492. {
  10493. ASSERT(!_fBarrierDisplayed);
  10494. // Remove menu entries for unsupported views.
  10495. for (UINT fvm = FVM_FIRST; fvm <= FVM_LAST; fvm++)
  10496. if (!_ViewSupported(fvm))
  10497. DeleteMenu(hmenuView, SFVIDM_VIEW_FIRSTVIEW + fvm - FVM_FIRST, MF_BYCOMMAND);
  10498. // "Check" menu entry for current view.
  10499. CheckCurrentViewMenuItem(hmenuView);
  10500. }
  10501. void CDefView::_GetCBText(UINT_PTR id, UINT uMsgT, UINT uMsgA, UINT uMsgW, LPTSTR psz, UINT cch)
  10502. {
  10503. *psz = 0;
  10504. WCHAR szW[MAX_PATH];
  10505. if (SUCCEEDED(CallCB(uMsgW, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szW)), (LPARAM)szW)))
  10506. SHUnicodeToTChar(szW, psz, cch);
  10507. else
  10508. {
  10509. char szA[MAX_PATH];
  10510. if (SUCCEEDED(CallCB(uMsgA, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, ARRAYSIZE(szA)), (LPARAM)szA)))
  10511. SHAnsiToTChar(szA, psz, cch);
  10512. else
  10513. CallCB(uMsgT, MAKEWPARAM(id - SFVIDM_CLIENT_FIRST, cch), (LPARAM)psz);
  10514. }
  10515. }
  10516. void CDefView::_GetMenuHelpText(UINT_PTR id, LPTSTR pszText, UINT cchText)
  10517. {
  10518. *pszText = 0;
  10519. if ((InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST) && _pcmFile) ||
  10520. (InRange(id, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST) && _pcmContextMenuPopup))
  10521. {
  10522. UINT uCMBias = SFVIDM_CONTEXT_FIRST;
  10523. IContextMenu *pcmSel = NULL;
  10524. if (InRange(id, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
  10525. {
  10526. pcmSel = _pcmFile;
  10527. uCMBias = SFVIDM_CONTEXT_FIRST;
  10528. }
  10529. else if (InRange(id, SFVIDM_BACK_CONTEXT_FIRST, SFVIDM_BACK_CONTEXT_LAST))
  10530. {
  10531. pcmSel = _pcmContextMenuPopup;
  10532. uCMBias = SFVIDM_BACK_CONTEXT_FIRST;
  10533. }
  10534. // First try to get the stardard help string
  10535. pcmSel->GetCommandString(id - uCMBias, GCS_HELPTEXT, NULL,
  10536. (LPSTR)pszText, cchText);
  10537. if (*pszText == 0)
  10538. {
  10539. // If we didn't get anything, try to grab the ansi version
  10540. CHAR szText[MAX_PATH];
  10541. szText[0] = 0; // Don't start with garbage in case of failure...
  10542. pcmSel->GetCommandString(id - uCMBias, GCS_HELPTEXTA, NULL,
  10543. szText, ARRAYSIZE(szText));
  10544. SHAnsiToUnicode(szText, pszText, cchText);
  10545. }
  10546. }
  10547. else if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB())
  10548. {
  10549. _GetCBText(id, SFVM_GETHELPTEXT, SFVM_GETHELPTEXTA, SFVM_GETHELPTEXTW, pszText, cchText);
  10550. }
  10551. else if (InRange(id, SFVIDM_GROUPSFIRST, SFVIDM_GROUPSLAST))
  10552. {
  10553. TCHAR sz[MAX_PATH];
  10554. int idHelp = _fGroupView?IDS_GROUPBY_HELPTEXT:IDS_ARRANGEBY_HELPTEXT;
  10555. LoadString(HINST_THISDLL, idHelp, sz, ARRAYSIZE(sz));
  10556. wnsprintf(pszText, cchText, sz, _vs.GetColumnName((UINT)id - SFVIDM_GROUPSFIRST));
  10557. }
  10558. else if (InRange(id, SFVIDM_GROUPSEXTENDEDFIRST, SFVIDM_GROUPSEXTENDEDLAST))
  10559. {
  10560. // Can't think of anything descriptive
  10561. }
  10562. else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST))
  10563. {
  10564. if ((id == SFVIDM_EDIT_UNDO) && IsUndoAvailable())
  10565. {
  10566. GetUndoText(pszText, cchText, UNDO_STATUSTEXT);
  10567. }
  10568. else
  10569. {
  10570. UINT idHelp = (UINT)id + SFVIDS_MH_FIRST;
  10571. // Unfortunatly, this starts to hit other ranges, so I'm just hard coding this one instead of
  10572. // using the table. If you add more, we need another table method of associating ids and help strings
  10573. if (id == SFVIDM_GROUPBY)
  10574. idHelp = IDS_GROUPBYITEM_HELPTEXT;
  10575. LoadString(HINST_THISDLL, idHelp, pszText, cchText);
  10576. }
  10577. }
  10578. }
  10579. void CDefView::_GetToolTipText(UINT_PTR id, LPTSTR pszText, UINT cchText)
  10580. {
  10581. *pszText = 0;
  10582. if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST) && HasCB())
  10583. {
  10584. _GetCBText(id, SFVM_GETTOOLTIPTEXT, SFVM_GETTOOLTIPTEXTA, SFVM_GETTOOLTIPTEXTW, pszText, cchText);
  10585. }
  10586. else if (InRange(id, SFVIDM_FIRST, SFVIDM_LAST))
  10587. {
  10588. if (id == SFVIDM_EDIT_UNDO)
  10589. {
  10590. if (IsUndoAvailable())
  10591. {
  10592. GetUndoText(pszText, cchText, UNDO_MENUTEXT);
  10593. return;
  10594. }
  10595. }
  10596. LoadString(HINST_THISDLL, (UINT)(IDS_TT_SFVIDM_FIRST + id), pszText, cchText);
  10597. }
  10598. else
  10599. {
  10600. // REVIEW: This might be an assert situation: missing tooltip info...
  10601. TraceMsg(TF_WARNING, "_GetToolTipText: tip request for unknown object");
  10602. }
  10603. }
  10604. LRESULT CDefView::_OnMenuSelect(UINT id, UINT mf, HMENU hmenu)
  10605. {
  10606. TCHAR szHelpText[80 + 2*MAX_PATH]; // Lots of stack!
  10607. // If we dismissed the edit restore our status bar...
  10608. if (!hmenu && LOWORD(mf)==0xffff)
  10609. {
  10610. _psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 0, 0, NULL);
  10611. return 0;
  10612. }
  10613. if (mf & (MF_SYSMENU | MF_SEPARATOR))
  10614. return 0;
  10615. szHelpText[0] = 0; // in case of failures below
  10616. if (mf & MF_POPUP)
  10617. {
  10618. MENUITEMINFO miiSubMenu;
  10619. miiSubMenu.cbSize = sizeof(MENUITEMINFO);
  10620. miiSubMenu.fMask = MIIM_ID;
  10621. miiSubMenu.cch = 0; // just in case
  10622. if (!GetMenuItemInfo(hmenu, id, TRUE, &miiSubMenu))
  10623. return 0;
  10624. // Change the parameters to simulate a "normal" menu item
  10625. id = miiSubMenu.wID;
  10626. mf &= ~MF_POPUP;
  10627. }
  10628. _GetMenuHelpText(id, szHelpText, ARRAYSIZE(szHelpText));
  10629. _fBackgroundStatusTextValid = FALSE;
  10630. _psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, SBT_NOBORDERS | 255, (LPARAM)szHelpText, NULL);
  10631. _psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 1, 0, NULL);
  10632. return 0;
  10633. }
  10634. //
  10635. // This function dismisses the name edit mode if there is any.
  10636. //
  10637. // REVIEW: Moving the focus away from the edit window will
  10638. // dismiss the name edit mode. Should we introduce
  10639. // a LV_DISMISSEDIT instead?
  10640. //
  10641. void CDefView::_DismissEdit()
  10642. {
  10643. if (_uState == SVUIA_ACTIVATE_FOCUS)
  10644. {
  10645. ListView_CancelEditLabel(_hwndListview);
  10646. }
  10647. }
  10648. void CDefView::_OnInitMenu()
  10649. {
  10650. // We need to dismiss the edit mode if it is any.
  10651. _DismissEdit();
  10652. }
  10653. void _RemoveContextMenuItems(HMENU hmInit)
  10654. {
  10655. int i;
  10656. for (i = GetMenuItemCount(hmInit) - 1; i >= 0; --i)
  10657. {
  10658. MENUITEMINFO mii;
  10659. mii.cbSize = sizeof(mii);
  10660. mii.fMask = MIIM_ID | MIIM_ID;
  10661. mii.cch = 0; // just in case
  10662. if (GetMenuItemInfo(hmInit, i, TRUE, &mii))
  10663. {
  10664. if (InRange(mii.wID, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST))
  10665. {
  10666. TraceMsg(TF_DEFVIEW, "_RemoveContextMenuItems: setting bDeleteItems at %d, %d", i, mii.wID);
  10667. //bDeleteItems = TRUE;
  10668. DeleteMenu(hmInit, i, MF_BYPOSITION);
  10669. }
  10670. }
  10671. }
  10672. }
  10673. BOOL HasClientItems(HMENU hmenu)
  10674. {
  10675. int cItems = GetMenuItemCount(hmenu);
  10676. for (int i = 0; i < cItems; i++)
  10677. {
  10678. UINT id = GetMenuItemID(hmenu, i);
  10679. if (InRange(id, SFVIDM_CLIENT_FIRST, SFVIDM_CLIENT_LAST))
  10680. return TRUE;
  10681. }
  10682. return FALSE;
  10683. }
  10684. LRESULT CDefView::_OnInitMenuPopup(HMENU hmInit, int nIndex, BOOL fSystemMenu)
  10685. {
  10686. if (_hmenuCur)
  10687. {
  10688. // This old code makes sure we only switch on the wID for one of our top-level windows
  10689. // The id shouldn't be re-used, so this probably isn't needed. But it doesn't hurt...
  10690. //
  10691. MENUITEMINFO mii = {0};
  10692. mii.cbSize = sizeof(mii);
  10693. mii.fMask = MIIM_SUBMENU | MIIM_ID;
  10694. if (GetMenuItemInfo(_hmenuCur, nIndex, TRUE, &mii) && mii.hSubMenu == hmInit)
  10695. {
  10696. switch (mii.wID)
  10697. {
  10698. case FCIDM_MENU_FILE:
  10699. // PERF note: we could avoid the rip-down-and-re-build our File menu
  10700. // if we have a _pcmFile and the _uState is the same as last
  10701. // time and the selection is identical to last time.
  10702. // First of all, clean up our last _pcmFile usage:
  10703. // remove all the menu items we've added
  10704. // remove the named separators for defcm
  10705. _RemoveContextMenuItems(hmInit);
  10706. SHUnprepareMenuForDefcm(hmInit, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST);
  10707. IUnknown_SetSite(_pcmFile, NULL);
  10708. ATOMICRELEASE(_pcmFile);
  10709. // Second, handle the focus/nofocus menus
  10710. if (_uState == SVUIA_ACTIVATE_FOCUS)
  10711. {
  10712. // Enable/disable our menuitems in the "File" pulldown.
  10713. Def_InitFileCommands(_AttributesFromSel(SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANLINK | SFGAO_HASPROPSHEET),
  10714. hmInit, SFVIDM_FIRST, FALSE);
  10715. // Collect our new _pcmFile context menu
  10716. IContextMenu* pcmSel = NULL;
  10717. _CreateSelectionContextMenu(IID_PPV_ARG(IContextMenu, &pcmSel));
  10718. IContextMenu* pcmBack = NULL;
  10719. _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &pcmBack));
  10720. IContextMenu* rgpcm[] = { pcmSel, pcmBack };
  10721. Create_ContextMenuOnContextMenuArray(rgpcm, ARRAYSIZE(rgpcm), IID_PPV_ARG(IContextMenu, &_pcmFile));
  10722. if (pcmSel)
  10723. pcmSel->Release();
  10724. if (pcmBack)
  10725. pcmBack->Release();
  10726. }
  10727. else if (_uState == SVUIA_ACTIVATE_NOFOCUS)
  10728. {
  10729. _pshf->CreateViewObject(_hwndMain, IID_PPV_ARG(IContextMenu, &_pcmFile));
  10730. }
  10731. // Third, merge in the context menu items
  10732. {
  10733. HRESULT hrPrepare = SHPrepareMenuForDefcm(hmInit, 0, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST);
  10734. if (_pcmFile)
  10735. {
  10736. IUnknown_SetSite(_pcmFile, SAFECAST(this, IShellView2*));
  10737. _pcmFile->QueryContextMenu(hmInit, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, CMF_DVFILE | CMF_NODEFAULT);
  10738. }
  10739. SHPrettyMenuForDefcm(hmInit, 0, SFVIDM_CONTEXT_FIRST, SFVIDM_CONTEXT_LAST, hrPrepare);
  10740. }
  10741. break;
  10742. case FCIDM_MENU_EDIT:
  10743. // Enable/disable menuitems in the "Edit" pulldown.
  10744. Def_InitEditCommands(_AttributesFromSel(SFGAO_CANCOPY | SFGAO_CANMOVE), hmInit, SFVIDM_FIRST, _pdtgtBack, 0);
  10745. _SHPrettyMenu(hmInit);
  10746. break;
  10747. case FCIDM_MENU_VIEW:
  10748. InitViewMenu(hmInit);
  10749. break;
  10750. }
  10751. }
  10752. }
  10753. // Check for a context menu's popup:
  10754. // assume the first item in the menu identifies the range
  10755. BOOL fHandled;
  10756. _ForwardMenuMessages(GetMenuItemID(hmInit, 0), WM_INITMENUPOPUP, (WPARAM)hmInit, MAKELPARAM(nIndex, fSystemMenu), NULL, &fHandled);
  10757. // Maybe this is the callback's menu then?
  10758. if (!fHandled && _hmenuCur && HasCB() && HasClientItems(hmInit))
  10759. {
  10760. CallCB(SFVM_INITMENUPOPUP, MAKEWPARAM(SFVIDM_CLIENT_FIRST, nIndex), (LPARAM)hmInit);
  10761. }
  10762. return 0;
  10763. }
  10764. // IShellView::AddPropertySheetPages
  10765. STDMETHODIMP CDefView::AddPropertySheetPages(DWORD dwRes, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam)
  10766. {
  10767. SFVM_PROPPAGE_DATA data;
  10768. ASSERT(IS_VALID_CODE_PTR(lpfn, FNADDPROPSHEETPAGE));
  10769. data.dwReserved = dwRes;
  10770. data.pfn = lpfn;
  10771. data.lParam = lParam;
  10772. // Call the callback to add pages
  10773. CallCB(SFVM_ADDPROPERTYPAGES, 0, (LPARAM)&data);
  10774. return S_OK;
  10775. }
  10776. STDMETHODIMP CDefView::SaveViewState()
  10777. {
  10778. HRESULT hr;
  10779. IPropertyBag* ppb;
  10780. hr = IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb));
  10781. if (SUCCEEDED(hr))
  10782. {
  10783. hr = _vs.SaveToPropertyBag(this, ppb);
  10784. ppb->Release();
  10785. }
  10786. else
  10787. {
  10788. IStream *pstm;
  10789. hr = _psb->GetViewStateStream(STGM_WRITE, &pstm);
  10790. if (SUCCEEDED(hr))
  10791. {
  10792. hr = _vs.SaveToStream(this, pstm);
  10793. pstm->Release();
  10794. }
  10795. else
  10796. {
  10797. // There are cases where we may not save out the complete view state
  10798. // but we do want to save out the column information (like Docfind...)
  10799. if (SUCCEEDED(CallCB(SFVM_GETCOLSAVESTREAM, STGM_READ, (LPARAM)&pstm)))
  10800. {
  10801. hr = _vs.SaveColumns(this, pstm);
  10802. pstm->Release();
  10803. }
  10804. }
  10805. }
  10806. return hr;
  10807. }
  10808. // 99/02/05 #226140 vtan: Function used to get the storage
  10809. // stream for the default view state of the current DefView.
  10810. // Typically this will be CLSID_ShellFSFolder but can be
  10811. // others.
  10812. HRESULT CDefView::_GetStorageStream (DWORD grfMode, IStream* *ppIStream)
  10813. {
  10814. *ppIStream = NULL;
  10815. CLSID clsid;
  10816. HRESULT hr = IUnknown_GetClassID(_pshf, &clsid);
  10817. if (SUCCEEDED(hr))
  10818. {
  10819. TCHAR szCLSID[64]; // enough for the CLSID
  10820. if (IsEqualGUID(CLSID_MyDocuments, clsid))
  10821. clsid = CLSID_ShellFSFolder;
  10822. TINT(SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID)));
  10823. *ppIStream = OpenRegStream(HKEY_CURRENT_USER,
  10824. REGSTR_PATH_EXPLORER TEXT("\\Streams\\Defaults"), szCLSID, grfMode);
  10825. if (*ppIStream == NULL)
  10826. hr = E_FAIL;
  10827. }
  10828. return hr;
  10829. }
  10830. // 99/02/05 #226140 vtan: Function called from DefView's
  10831. // implementation of IOleCommandTarget::Exec() which is
  10832. // invoked from CShellBrowser2::SetAsDefFolderSettings().
  10833. HRESULT CDefView::_SaveGlobalViewState(void)
  10834. {
  10835. IStream *pstm;
  10836. HRESULT hr = _GetStorageStream(STGM_WRITE, &pstm);
  10837. if (SUCCEEDED(hr))
  10838. {
  10839. hr = _vs.SaveToStream(this, pstm);
  10840. if (SUCCEEDED(hr))
  10841. {
  10842. hr = (ERROR_SUCCESS == SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\Bags"))) ? S_OK : E_FAIL;
  10843. }
  10844. pstm->Release();
  10845. }
  10846. return hr;
  10847. }
  10848. // 99/02/05 #226140 vtan: Function called from
  10849. // GetViewState to get the default view state
  10850. // for this class.
  10851. HRESULT CDefView::_LoadGlobalViewState(IStream* *ppIStream)
  10852. {
  10853. return _GetStorageStream(STGM_READ, ppIStream);
  10854. }
  10855. // 99/02/09 #226140 vtan: Function used to reset the
  10856. // global view states stored by deleting the key
  10857. // that stores all of them.
  10858. HRESULT CDefView::_ResetGlobalViewState(void)
  10859. {
  10860. SHDeleteKey(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER TEXT("\\Streams\\Defaults"));
  10861. LONG lRetVal = SHDeleteKey(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\ShellNoRoam\\Bags"));
  10862. return (ERROR_SUCCESS == lRetVal) ? S_OK : E_FAIL;
  10863. }
  10864. void CDefView::_RestoreAllGhostedFileView()
  10865. {
  10866. ListView_SetItemState(_hwndListview, -1, 0, LVIS_CUT);
  10867. UINT c = ListView_GetItemCount(_hwndListview);
  10868. for (UINT i = 0; i < c; i++)
  10869. {
  10870. if (_Attributes(_GetPIDL(i), SFGAO_GHOSTED))
  10871. ListView_SetItemState(_hwndListview, i, LVIS_CUT, LVIS_CUT);
  10872. }
  10873. }
  10874. HRESULT CDefView::SelectAndPositionItem(LPCITEMIDLIST pidlItem, UINT uFlags, POINT *ppt)
  10875. {
  10876. HRESULT hr;
  10877. if (NULL == pidlItem)
  10878. hr = _SelectAndPosition(-1, uFlags, ppt);
  10879. else if (ILFindLastID(pidlItem) == pidlItem)
  10880. {
  10881. if (_fInBackgroundGrouping)
  10882. {
  10883. Pidl_Set(&_pidlSelectAndPosition, pidlItem);
  10884. _uSelectAndPositionFlags = uFlags;
  10885. hr = S_OK;
  10886. }
  10887. else
  10888. {
  10889. int iItem = _FindItem(pidlItem, NULL, FALSE);
  10890. if (iItem != -1)
  10891. hr = _SelectAndPosition(iItem, uFlags, ppt);
  10892. else
  10893. hr = S_OK;
  10894. }
  10895. }
  10896. else
  10897. {
  10898. RIP(ILFindLastID(pidlItem) == pidlItem);
  10899. hr = E_INVALIDARG;
  10900. }
  10901. return hr;
  10902. }
  10903. HRESULT CDefView::_SelectAndPosition(int iItem, UINT uFlags, POINT *ppt)
  10904. {
  10905. HRESULT hr = S_OK; // assume all is good
  10906. // See if we should first deselect everything else
  10907. if (-1 == iItem)
  10908. {
  10909. if (uFlags == SVSI_DESELECTOTHERS)
  10910. {
  10911. ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
  10912. _RestoreAllGhostedFileView();
  10913. }
  10914. else
  10915. {
  10916. hr = E_INVALIDARG; // I only know how to deselect everything
  10917. }
  10918. }
  10919. else
  10920. {
  10921. if (_pDUIView)
  10922. {
  10923. _fBarrierDisplayed = FALSE;
  10924. _pDUIView->EnableBarrier(FALSE);
  10925. }
  10926. if (uFlags & SVSI_TRANSLATEPT)
  10927. {
  10928. //The caller is asking us to take this point and convert it from screen Coords
  10929. // to the Client of the Listview.
  10930. LVUtil_ScreenToLV(_hwndListview, ppt);
  10931. }
  10932. // set the position first so that the ensure visible scrolls to
  10933. // the new position
  10934. if (ppt)
  10935. {
  10936. _SetItemPosition(iItem, ppt->x, ppt->y);
  10937. }
  10938. else if ((SVSI_POSITIONITEM & uFlags) && _bMouseMenu && _IsPositionedView())
  10939. {
  10940. _SetItemPosition(iItem, _ptDragAnchor.x, _ptDragAnchor.y);
  10941. }
  10942. if ((uFlags & SVSI_EDIT) == SVSI_EDIT)
  10943. {
  10944. // Grab focus if the listview (or any of it's children) don't already have focus
  10945. HWND hwndFocus = GetFocus();
  10946. if (SHIsChildOrSelf(_hwndListview, hwndFocus) != S_OK)
  10947. SetFocus(_hwndListview);
  10948. ListView_EditLabel(_hwndListview, iItem);
  10949. }
  10950. else
  10951. {
  10952. // change the item state
  10953. if (!(uFlags & SVSI_NOSTATECHANGE))
  10954. {
  10955. UINT stateMask = LVIS_SELECTED;
  10956. UINT state = (uFlags & SVSI_SELECT) ? LVIS_SELECTED : 0;
  10957. if (uFlags & SVSI_FOCUSED)
  10958. {
  10959. state |= LVIS_FOCUSED;
  10960. stateMask |= LVIS_FOCUSED;
  10961. }
  10962. // See if we should first deselect everything else
  10963. if (uFlags & SVSI_DESELECTOTHERS)
  10964. {
  10965. ListView_SetItemState(_hwndListview, -1, 0, LVIS_SELECTED);
  10966. _RestoreAllGhostedFileView();
  10967. }
  10968. ListView_SetItemState(_hwndListview, iItem, state, stateMask);
  10969. }
  10970. if (uFlags & SVSI_ENSUREVISIBLE)
  10971. ListView_EnsureVisible(_hwndListview, iItem, FALSE);
  10972. // we should only set focus when SVUIA_ACTIVATE_FOCUS
  10973. // bug fixing that might break find target code
  10974. if (uFlags & SVSI_FOCUSED)
  10975. SetFocus(_hwndListview);
  10976. if (uFlags & SVSI_SELECTIONMARK)
  10977. ListView_SetSelectionMark(_hwndListview, iItem);
  10978. // if this is a check select view then set the state of that item accordingly
  10979. if (_fs.fFlags & FWF_CHECKSELECT)
  10980. ListView_SetCheckState(_hwndListview, iItem, (uFlags & SVSI_CHECK));
  10981. }
  10982. }
  10983. return hr;
  10984. }
  10985. STDMETHODIMP CDefView::SelectItem(int iItem, DWORD uFlags)
  10986. {
  10987. return _SelectAndPosition(iItem, uFlags, NULL);
  10988. }
  10989. typedef struct {
  10990. LPITEMIDLIST pidl;
  10991. UINT uFlagsSelect;
  10992. } DELAY_SEL_ITEM;
  10993. STDMETHODIMP CDefView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags)
  10994. {
  10995. // if the listview isn't shown, there's nothing to select yet.
  10996. // Likewise if we are in the process of being created we should defer.
  10997. if (!_IsListviewVisible())
  10998. {
  10999. if (!_hdsaSelect)
  11000. {
  11001. _hdsaSelect = DSA_Create(sizeof(DELAY_SEL_ITEM), 4);
  11002. if (!_hdsaSelect)
  11003. return E_OUTOFMEMORY;
  11004. }
  11005. HRESULT hr = E_OUTOFMEMORY;
  11006. DELAY_SEL_ITEM dvdsi;
  11007. dvdsi.pidl = ILClone(pidlItem);
  11008. if (dvdsi.pidl)
  11009. {
  11010. dvdsi.uFlagsSelect = uFlags;
  11011. if (DSA_AppendItem(_hdsaSelect, &dvdsi) == DSA_ERR)
  11012. ILFree(dvdsi.pidl);
  11013. else
  11014. hr = S_OK;
  11015. }
  11016. return hr;
  11017. }
  11018. return SelectAndPositionItem(pidlItem, uFlags, NULL);
  11019. }
  11020. // IFolderView
  11021. STDMETHODIMP CDefView::GetCurrentViewMode(UINT *pViewMode)
  11022. {
  11023. *pViewMode = _fs.ViewMode;
  11024. return S_OK;
  11025. }
  11026. STDMETHODIMP CDefView::SetCurrentViewMode(UINT uViewMode)
  11027. {
  11028. ASSERT(FVM_FIRST <= uViewMode && uViewMode <= FVM_LAST);
  11029. if (uViewMode != _vs._ViewMode)
  11030. _ClearItemPositions();
  11031. return _SwitchToViewFVM(uViewMode);
  11032. }
  11033. STDMETHODIMP CDefView::GetFolder(REFIID riid, void **ppv)
  11034. {
  11035. if (_pshf)
  11036. return _pshf->QueryInterface(riid, ppv);
  11037. *ppv = NULL;
  11038. return E_NOINTERFACE;
  11039. }
  11040. STDMETHODIMP CDefView::Item(int iItemIndex, LPITEMIDLIST *ppidl)
  11041. {
  11042. HRESULT hr = E_FAIL;
  11043. LPCITEMIDLIST pidl = _GetPIDL(iItemIndex);
  11044. if (pidl)
  11045. {
  11046. hr = SHILClone(pidl, ppidl);
  11047. }
  11048. return hr;
  11049. }
  11050. STDMETHODIMP CDefView::ItemCount(UINT uFlags, int *pcItems)
  11051. {
  11052. *pcItems = _GetItemArray(NULL, NULL, uFlags);
  11053. return S_OK;
  11054. }
  11055. HRESULT CDefView::_EnumThings(UINT uWhat, IEnumIDList **ppenum)
  11056. {
  11057. *ppenum = NULL;
  11058. LPCITEMIDLIST *apidl;
  11059. UINT cItems;
  11060. HRESULT hr = _GetItemObjects(&apidl, uWhat, &cItems);
  11061. if (SUCCEEDED(hr))
  11062. {
  11063. hr = CreateIEnumIDListOnIDLists(apidl, cItems, ppenum);
  11064. LocalFree(apidl);
  11065. }
  11066. return hr;
  11067. }
  11068. STDMETHODIMP CDefView::Items(UINT uWhat, REFIID riid, void **ppv)
  11069. {
  11070. HRESULT hr = E_NOINTERFACE;
  11071. if (IID_IEnumIDList == riid)
  11072. {
  11073. hr = _EnumThings(uWhat, (IEnumIDList**)ppv);
  11074. }
  11075. else if (IID_IDataObject == riid)
  11076. {
  11077. if ((uWhat & SVGIO_TYPE_MASK) == SVGIO_SELECTION)
  11078. {
  11079. if (_pSelectionShellItemArray)
  11080. {
  11081. hr = _pSelectionShellItemArray->BindToHandler(NULL, BHID_DataObject, riid, ppv);
  11082. }
  11083. }
  11084. else
  11085. {
  11086. hr = _GetUIObjectFromItem(riid, ppv, uWhat, FALSE);
  11087. }
  11088. }
  11089. return hr;
  11090. }
  11091. // inverse of ::SelectItem(..., SVSI_SELECTIONMARK)
  11092. STDMETHODIMP CDefView::GetSelectionMarkedItem(int *piItem)
  11093. {
  11094. *piItem = ListView_GetSelectionMark(_hwndListview);
  11095. return (-1 == *piItem) ? S_FALSE : S_OK;
  11096. }
  11097. STDMETHODIMP CDefView::GetFocusedItem(int *piItem)
  11098. {
  11099. *piItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
  11100. return (-1 == *piItem) ? S_FALSE : S_OK;
  11101. }
  11102. BOOL CDefView::_GetItemPosition(LPCITEMIDLIST pidl, POINT *ppt)
  11103. {
  11104. int i = _FindItem(pidl, NULL, FALSE);
  11105. if (i != -1)
  11106. return ListView_GetItemPosition(_hwndListview, i, ppt);
  11107. return FALSE;
  11108. }
  11109. STDMETHODIMP CDefView::GetItemPosition(LPCITEMIDLIST pidl, POINT *ppt)
  11110. {
  11111. return _GetItemPosition(pidl, ppt) ? S_OK : E_FAIL;
  11112. }
  11113. STDMETHODIMP CDefView::GetSpacing(POINT* ppt)
  11114. {
  11115. if (ppt)
  11116. {
  11117. if (_fs.ViewMode != FVM_TILE)
  11118. {
  11119. BOOL fSmall;
  11120. switch (_fs.ViewMode)
  11121. {
  11122. case FVM_SMALLICON:
  11123. case FVM_LIST:
  11124. case FVM_DETAILS:
  11125. fSmall = TRUE;
  11126. break;
  11127. case FVM_ICON:
  11128. case FVM_THUMBNAIL:
  11129. case FVM_THUMBSTRIP:
  11130. default:
  11131. fSmall = FALSE;
  11132. break;
  11133. }
  11134. DWORD dwSize = ListView_GetItemSpacing(_hwndListview, fSmall);
  11135. ppt->x = GET_X_LPARAM(dwSize);
  11136. ppt->y = GET_Y_LPARAM(dwSize);
  11137. }
  11138. else
  11139. {
  11140. LVTILEVIEWINFO tvi;
  11141. tvi.cbSize = sizeof(tvi);
  11142. tvi.dwMask = LVTVIM_TILESIZE;
  11143. if (ListView_GetTileViewInfo(_hwndListview, &tvi))
  11144. {
  11145. ppt->x = tvi.sizeTile.cx;
  11146. ppt->y = tvi.sizeTile.cy;
  11147. }
  11148. else
  11149. {
  11150. // guess.
  11151. ppt->x = 216;
  11152. ppt->y = 56;
  11153. }
  11154. }
  11155. }
  11156. return _IsPositionedView() ? S_OK : S_FALSE;
  11157. }
  11158. STDMETHODIMP CDefView::GetDefaultSpacing(POINT* ppt)
  11159. {
  11160. ASSERT(ppt);
  11161. if (_fs.ViewMode != FVM_THUMBNAIL && _fs.ViewMode != FVM_THUMBSTRIP && _fs.ViewMode != FVM_TILE)
  11162. {
  11163. DWORD dwSize = ListView_GetItemSpacing(_hwndListview, FALSE);
  11164. ppt->x = GET_X_LPARAM(dwSize);
  11165. ppt->y = GET_Y_LPARAM(dwSize);
  11166. }
  11167. else
  11168. {
  11169. // Bug #163528 (edwardp 8/15/00) Should get this data from comctl.
  11170. ppt->x = GetSystemMetrics(SM_CXICONSPACING);
  11171. ppt->y = GetSystemMetrics(SM_CYICONSPACING);
  11172. }
  11173. return S_OK;
  11174. }
  11175. // IShellFolderView
  11176. STDMETHODIMP CDefView::GetAutoArrange()
  11177. {
  11178. return _IsAutoArrange() ? S_OK : S_FALSE;
  11179. }
  11180. void CDefView::_ClearPendingSelectedItems()
  11181. {
  11182. if (_hdsaSelect)
  11183. {
  11184. HDSA hdsa = _hdsaSelect;
  11185. _hdsaSelect = NULL;
  11186. int cItems = DSA_GetItemCount(hdsa);
  11187. for (int i = 0; i < cItems; i++)
  11188. {
  11189. DELAY_SEL_ITEM *pdvdsi = (DELAY_SEL_ITEM*)DSA_GetItemPtr(hdsa, i);
  11190. if (pdvdsi)
  11191. ILFree(pdvdsi->pidl);
  11192. }
  11193. DSA_Destroy(hdsa);
  11194. }
  11195. }
  11196. // Call this whenever the state changes such that SelectItem (above)
  11197. void CDefView::SelectPendingSelectedItems()
  11198. {
  11199. ASSERT(_IsListviewVisible());
  11200. if (_hdsaSelect)
  11201. {
  11202. //
  11203. // Listview quirk: If the following conditions are met..
  11204. //
  11205. // 1. WM_SETREDRAW(FALSE) or ShowWindow(SW_HIDE)
  11206. // 2. Listview has never painted yet
  11207. // 3. LVS_LIST
  11208. //
  11209. // then ListView_LGetRects doesn't work. And consequently,
  11210. // everything that relies on known item rectangles (e.g.,
  11211. // LVM_ENSUREVISIBLE, sent by below SelectItem call) doesn't work.
  11212. //
  11213. // (1) ShowHideListView did a ShowWindow(SW_SHOW), but
  11214. // FillDone does a WM_SETREDRAW(FALSE).
  11215. // check _fListviewRedraw to see if condition (1) is met
  11216. //
  11217. // (2) We just showed the listview, if it's the first time,
  11218. // then Condition (2) has been met
  11219. //
  11220. // But wait, there's also a listview bug where SetWindowPos
  11221. // doesn't trigger it into thinking that the window is visible.
  11222. // So you have to send a manual WM_SHOWWINDOW, too.
  11223. //
  11224. // So if we detect that condition (3) is also met, we temporarily
  11225. // enable redraw (thereby cancelling condition 1), tell listview
  11226. // "No really, you're visible" -- this tickles it into computing
  11227. // column stuff -- then turn redraw back off.
  11228. //
  11229. if (_fListviewRedraw &&
  11230. (GetWindowStyle(_hwndListview) & LVS_TYPEMASK) == LVS_LIST)
  11231. {
  11232. // Evil hack (fix comctl32.dll v6.0 someday NTRAID#182448)
  11233. SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)TRUE, 0);
  11234. SendMessage(_hwndListview, WM_SHOWWINDOW, TRUE, 0);
  11235. SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)FALSE, 0);
  11236. }
  11237. // End of listview hack workaround
  11238. HDSA hdsa = _hdsaSelect;
  11239. _hdsaSelect = NULL;
  11240. int cItems = DSA_GetItemCount(hdsa);
  11241. for (int i = 0; i < cItems; i++)
  11242. {
  11243. DELAY_SEL_ITEM *pdvdsi = (DELAY_SEL_ITEM*)DSA_GetItemPtr(hdsa, i);
  11244. if (pdvdsi)
  11245. {
  11246. SelectItem(pdvdsi->pidl, pdvdsi->uFlagsSelect);
  11247. ILFree(pdvdsi->pidl);
  11248. }
  11249. }
  11250. DSA_Destroy(hdsa);
  11251. }
  11252. }
  11253. HRESULT CDefView::_GetIPersistHistoryObject(IPersistHistory **ppph)
  11254. {
  11255. // See to see if specific folder wants to handle it...
  11256. HRESULT hr = CallCB(SFVM_GETIPERSISTHISTORY, 0, (LPARAM)ppph);
  11257. if (FAILED(hr))
  11258. {
  11259. // Here we can decide if we want to default should be to always save
  11260. // the default defview stuff or not. For now we will assume that we do
  11261. if (ppph)
  11262. {
  11263. CDefViewPersistHistory *pdvph = new CDefViewPersistHistory();
  11264. if (pdvph)
  11265. {
  11266. hr = pdvph->QueryInterface(IID_PPV_ARG(IPersistHistory, ppph));
  11267. pdvph->Release();
  11268. }
  11269. else
  11270. {
  11271. *ppph = NULL;
  11272. hr = E_OUTOFMEMORY;
  11273. }
  11274. }
  11275. else
  11276. hr = S_FALSE; // still succeeds but can detect on other side if desired...
  11277. }
  11278. return hr;
  11279. }
  11280. STDMETHODIMP CDefView::GetItemObject(UINT uWhat, REFIID riid, void **ppv)
  11281. {
  11282. HRESULT hr = E_NOINTERFACE;
  11283. *ppv = NULL;
  11284. switch (uWhat & SVGIO_TYPE_MASK)
  11285. {
  11286. case SVGIO_BACKGROUND:
  11287. if (IsEqualIID(riid, IID_IContextMenu) ||
  11288. IsEqualIID(riid, IID_IContextMenu2) ||
  11289. IsEqualIID(riid, IID_IContextMenu3))
  11290. {
  11291. hr = _CBackgrndMenu_CreateInstance(riid, ppv);
  11292. }
  11293. else if (IsEqualIID(riid, IID_IDispatch) ||
  11294. IsEqualIID(riid, IID_IDefViewScript))
  11295. {
  11296. if (!_pauto)
  11297. {
  11298. // try to create an Instance of the Shell dispatch for folder views...
  11299. IDispatch *pdisp;
  11300. if (SUCCEEDED(SHExtCoCreateInstance(NULL, &CLSID_ShellFolderView, NULL,
  11301. IID_PPV_ARG(IDispatch, &pdisp))))
  11302. {
  11303. SetAutomationObject(pdisp); // we hold a ref here
  11304. ASSERT(_pauto); // the above grabbed this
  11305. pdisp->Release();
  11306. }
  11307. }
  11308. // return the IDispath interface.
  11309. if (_pauto)
  11310. hr = _pauto->QueryInterface(riid, ppv);
  11311. }
  11312. else if (IsEqualIID(riid, IID_IPersistHistory))
  11313. {
  11314. // See if the folder wants a chance at this. The main
  11315. // case for this is the search results windows.
  11316. hr = _GetIPersistHistoryObject((IPersistHistory**)ppv);
  11317. if (SUCCEEDED(hr))
  11318. {
  11319. IUnknown_SetSite((IUnknown*)*ppv, SAFECAST(this, IShellView2*));
  11320. }
  11321. }
  11322. else if (_cFrame.IsWebView() && _cFrame._pOleObj)
  11323. {
  11324. hr = _cFrame._pOleObj->QueryInterface(riid, ppv);
  11325. }
  11326. break;
  11327. case SVGIO_ALLVIEW:
  11328. if (_hwndStatic)
  11329. {
  11330. DECLAREWAITCURSOR;
  11331. SetWaitCursor();
  11332. do
  11333. {
  11334. // If _hwndStatic is around, we must be filling the
  11335. // view in a background thread, so we will peek for
  11336. // messages to it (so SendMessages will get through)
  11337. // and dispatch only _hwndStatic messages so we get the
  11338. // animation effect.
  11339. // Note there is no timeout, so this could take
  11340. // a while on a slow link, but there really isn't
  11341. // much else I can do
  11342. MSG msg;
  11343. // Since _hwndStatic can only be destroyed on a WM_DSV_BACKGROUNDENUMDONE
  11344. // message, we should never get a RIP
  11345. // We also need to allow WM_DSV_FILELISTFILLDONE since it can destroy _hwndStatic
  11346. if (PeekMessage(&msg, _hwndView, WM_DSV_BACKGROUNDENUMDONE,
  11347. WM_DSV_BACKGROUNDENUMDONE, PM_REMOVE) ||
  11348. PeekMessage(&msg, _hwndView, WM_DSV_FILELISTFILLDONE,
  11349. WM_DSV_FILELISTFILLDONE, PM_REMOVE) ||
  11350. PeekMessage(&msg, _hwndView, WM_DSV_GROUPINGDONE,
  11351. WM_DSV_GROUPINGDONE, PM_REMOVE) ||
  11352. PeekMessage(&msg, _hwndStatic, 0, 0, PM_REMOVE))
  11353. {
  11354. TranslateMessage(&msg);
  11355. DispatchMessage(&msg);
  11356. }
  11357. } while (_hwndStatic);
  11358. ResetWaitCursor();
  11359. }
  11360. // Fall through
  11361. case SVGIO_SELECTION:
  11362. hr = _GetUIObjectFromItem(riid, ppv, uWhat, TRUE);
  11363. break;
  11364. }
  11365. return hr;
  11366. }
  11367. HRESULT CDefView::PreCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool)
  11368. {
  11369. ASSERT(hwndContaining != NULL);
  11370. ASSERT(_FindPendingInfotip(hwndContaining, uToolID, NULL, FALSE) == S_FALSE);
  11371. PENDING_INFOTIP *ppi = new PENDING_INFOTIP;
  11372. HRESULT hr;
  11373. if (ppi)
  11374. {
  11375. ppi->hwndContaining = hwndContaining;
  11376. ppi->uToolID = uToolID;
  11377. ppi->rectTool = *prectTool;
  11378. if (_tlistPendingInfotips.AddTail(ppi))
  11379. {
  11380. hr = S_OK;
  11381. }
  11382. else
  11383. {
  11384. hr = E_OUTOFMEMORY;
  11385. delete ppi;
  11386. }
  11387. }
  11388. else
  11389. {
  11390. hr = E_OUTOFMEMORY;
  11391. }
  11392. return hr;
  11393. }
  11394. HRESULT CDefView::PostCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, HINSTANCE hinst, UINT_PTR uInfotipID, LPARAM lParam)
  11395. {
  11396. ASSERT(hwndContaining != NULL);
  11397. TOOLINFO *pti = new TOOLINFO;
  11398. HRESULT hr;
  11399. if (pti)
  11400. {
  11401. pti->cbSize = sizeof(TOOLINFO);
  11402. pti->uFlags = 0;
  11403. pti->hwnd = hwndContaining;
  11404. pti->uId = uToolID;
  11405. //pti->rect = initialized in _OnPostCreateInfotip()
  11406. pti->hinst = hinst;
  11407. pti->lpszText = (LPWSTR)uInfotipID;
  11408. pti->lParam = lParam;
  11409. hr = PostMessage(_hwndView, WM_DSV_POSTCREATEINFOTIP, (WPARAM)pti, lParam) ? S_OK : E_FAIL;
  11410. if (FAILED(hr))
  11411. {
  11412. delete pti;
  11413. }
  11414. }
  11415. else
  11416. {
  11417. hr = E_OUTOFMEMORY;
  11418. }
  11419. return hr;
  11420. }
  11421. HRESULT CDefView::PostCreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPCWSTR pwszInfotip, LPARAM lParam)
  11422. {
  11423. HRESULT hr = SHStrDup(pwszInfotip, (LPWSTR *)&pwszInfotip);
  11424. if (SUCCEEDED(hr))
  11425. {
  11426. hr = PostCreateInfotip(hwndContaining, uToolID, NULL, (UINT_PTR)pwszInfotip, lParam);
  11427. if (FAILED(hr))
  11428. {
  11429. CoTaskMemFree((LPVOID)pwszInfotip);
  11430. }
  11431. }
  11432. return hr;
  11433. }
  11434. HRESULT CDefView::_OnPostCreateInfotip(TOOLINFO *pti, LPARAM lParam)
  11435. {
  11436. HRESULT hr = _FindPendingInfotip(pti->hwnd, pti->uId, &pti->rect, TRUE);
  11437. if (hr == S_OK)
  11438. {
  11439. hr = SendMessage(_hwndInfotip, TTM_ADDTOOL, 0, (LPARAM)pti) ? S_OK : E_FAIL;
  11440. }
  11441. _OnPostCreateInfotipCleanup(pti);
  11442. return hr;
  11443. }
  11444. HRESULT CDefView::_OnPostCreateInfotipCleanup(TOOLINFO *pti)
  11445. {
  11446. if (!pti->hinst)
  11447. CoTaskMemFree(pti->lpszText);
  11448. delete pti;
  11449. return S_OK;
  11450. }
  11451. HRESULT CDefView::_FindPendingInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, BOOL bRemoveAndDestroy)
  11452. {
  11453. CLISTPOS posNext = _tlistPendingInfotips.GetHeadPosition();
  11454. CLISTPOS posCurrent;
  11455. PENDING_INFOTIP *ppi;
  11456. HRESULT hr = S_FALSE;
  11457. while (posNext)
  11458. {
  11459. posCurrent = posNext;
  11460. ppi = _tlistPendingInfotips.GetNext(posNext);
  11461. if (ppi->hwndContaining == hwndContaining && ppi->uToolID == uToolID)
  11462. {
  11463. if (bRemoveAndDestroy)
  11464. {
  11465. if (prectTool)
  11466. {
  11467. // Use prectTool as out param.
  11468. *prectTool = ppi->rectTool;
  11469. }
  11470. _tlistPendingInfotips.RemoveAt(posCurrent);
  11471. delete ppi;
  11472. }
  11473. else
  11474. {
  11475. if (prectTool)
  11476. {
  11477. // Use prectTool as in param.
  11478. ppi->rectTool = *prectTool;
  11479. }
  11480. }
  11481. hr = S_OK;
  11482. break;
  11483. }
  11484. }
  11485. // Post Contition -- callers expect only S_OK or S_FALSE.
  11486. ASSERT(hr == S_OK || hr == S_FALSE);
  11487. return hr;
  11488. }
  11489. HRESULT CDefView::CreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, HINSTANCE hinst, UINT_PTR uInfotipID, LPARAM lParam)
  11490. {
  11491. ASSERT(hwndContaining != NULL);
  11492. // CreateInfotip() is not for use with PreCreateInfotip()/PostCreateInfotip().
  11493. ASSERT(_FindPendingInfotip(hwndContaining, uToolID, NULL, FALSE) == S_FALSE);
  11494. TOOLINFO ti;
  11495. ti.cbSize = sizeof(TOOLINFO);
  11496. ti.uFlags = 0;
  11497. ti.hwnd = hwndContaining;
  11498. ti.uId = uToolID;
  11499. ti.rect = *prectTool;
  11500. ti.hinst = hinst;
  11501. ti.lpszText = (LPWSTR)uInfotipID;
  11502. ti.lParam = lParam;
  11503. return SendMessage(_hwndInfotip, TTM_ADDTOOL, 0, (LPARAM)&ti) ? S_OK : E_FAIL;
  11504. }
  11505. HRESULT CDefView::CreateInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool, LPCWSTR pwszInfotip, LPARAM lParam)
  11506. {
  11507. return CreateInfotip(hwndContaining, uToolID, prectTool, NULL, (UINT_PTR)pwszInfotip, lParam);
  11508. }
  11509. HRESULT CDefView::DestroyInfotip(HWND hwndContaining, UINT_PTR uToolID)
  11510. {
  11511. ASSERT(hwndContaining != NULL);
  11512. if (_FindPendingInfotip(hwndContaining, uToolID, NULL, TRUE) == S_FALSE)
  11513. {
  11514. TOOLINFO ti = { 0 };
  11515. ti.cbSize = sizeof(ti);
  11516. ti.hwnd = hwndContaining;
  11517. ti.uId = uToolID;
  11518. SendMessage(_hwndInfotip, TTM_DELTOOL, 0, (LPARAM)&ti);
  11519. }
  11520. return S_OK;
  11521. }
  11522. // Note:
  11523. // Coordinates in prectTool must be relative to the hwnd in hwndContaining.
  11524. //
  11525. HRESULT CDefView::RepositionInfotip(HWND hwndContaining, UINT_PTR uToolID, LPRECT prectTool)
  11526. {
  11527. if (_FindPendingInfotip(hwndContaining, uToolID, prectTool, FALSE) == S_FALSE)
  11528. {
  11529. TOOLINFO ti = { 0 };
  11530. ti.cbSize = sizeof(ti);
  11531. ti.hwnd = hwndContaining;
  11532. ti.uId = uToolID;
  11533. ti.rect = *prectTool;
  11534. SendMessage(_hwndInfotip, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
  11535. }
  11536. return S_OK;
  11537. }
  11538. HRESULT CDefView::RelayInfotipMessage(HWND hwndFrom, UINT uMsg, WPARAM wParam, LPARAM lParam)
  11539. {
  11540. HRESULT hr;
  11541. if (_hwndInfotip)
  11542. {
  11543. MSG msg;
  11544. msg.hwnd = hwndFrom;
  11545. msg.message = uMsg;
  11546. msg.wParam = wParam;
  11547. msg.lParam = lParam;
  11548. SendMessage(_hwndInfotip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
  11549. hr = S_OK;
  11550. }
  11551. else
  11552. {
  11553. hr = E_FAIL;
  11554. }
  11555. return hr;
  11556. }
  11557. STDMETHODIMP CDefView::QueryInterface(REFIID riid, void **ppvObj)
  11558. {
  11559. static const QITAB qit[] = {
  11560. QITABENT(CDefView, IShellView2), // IID_IShellView2
  11561. QITABENTMULTI(CDefView, IShellView, IShellView2), // IID_IShellView
  11562. QITABENT(CDefView, IViewObject), // IID_IViewObject
  11563. QITABENT(CDefView, IDropTarget), // IID_IDropTarget
  11564. QITABENT(CDefView, IShellFolderView), // IID_IShellFolderView
  11565. QITABENT(CDefView, IFolderView), // IID_IFolderView
  11566. QITABENT(CDefView, IOleCommandTarget), // IID_IOleCommandTarget
  11567. QITABENT(CDefView, IServiceProvider), // IID_IServiceProvider
  11568. QITABENT(CDefView, IDefViewFrame3), // IID_IDefViewFrame
  11569. QITABENT(CDefView, IDefViewFrame), // IID_IDefViewFrame
  11570. QITABENT(CDefView, IDocViewSite), // IID_IDocViewSite
  11571. QITABENT(CDefView, IInternetSecurityMgrSite), // IID_IInternetSecurityMgrSite
  11572. QITABENT(CDefView, IObjectWithSite), // IID_IObjectWithSite
  11573. QITABENT(CDefView, IPersistIDList), // IID_IPersistIDList
  11574. QITABENT(CDefView, IDVGetEnum), // IID_IDVGetEnum
  11575. QITABENT(CDefView, IContextMenuSite), // IID_IContextMenuSite
  11576. QITABENT(CDefView, IDefViewSafety), // IID_IDefViewSafety
  11577. QITABENT(CDefView, IUICommandTarget), // IID_IUICommandTarget
  11578. { 0 }
  11579. };
  11580. HRESULT hr = QISearch(this, qit, riid, ppvObj);
  11581. if (FAILED(hr))
  11582. {
  11583. // special case this one as it simply casts this...
  11584. if (IsEqualIID(riid, IID_CDefView))
  11585. {
  11586. *ppvObj = (void *)this;
  11587. AddRef();
  11588. hr = S_OK;
  11589. }
  11590. }
  11591. return hr;
  11592. }
  11593. STDMETHODIMP_(ULONG) CDefView::AddRef()
  11594. {
  11595. return InterlockedIncrement(&_cRef);
  11596. }
  11597. STDMETHODIMP_(ULONG) CDefView::Release()
  11598. {
  11599. ASSERT( 0 != _cRef );
  11600. ULONG cRef = InterlockedDecrement(&_cRef);
  11601. if ( 0 == cRef )
  11602. {
  11603. delete this;
  11604. }
  11605. return cRef;
  11606. }
  11607. //===========================================================================
  11608. // Constructor of CDefView class
  11609. //===========================================================================
  11610. CDefView::CDefView(IShellFolder *psf, IShellFolderViewCB *psfvcb,
  11611. IShellView *psvOuter) : _cRef(1), _cCallback(psfvcb)
  11612. {
  11613. psf->QueryInterface(IID_PPV_ARG(IShellFolder, &_pshf));
  11614. psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &_pshf2));
  11615. LPITEMIDLIST pidlFull = _GetViewPidl();
  11616. if (pidlFull)
  11617. {
  11618. LPCITEMIDLIST pidlRelative;
  11619. if (SUCCEEDED(SHBindToFolderIDListParent(NULL, pidlFull, IID_PPV_ARG(IShellFolder, &_pshfParent), &pidlRelative)))
  11620. {
  11621. _pidlRelative = ILClone(pidlRelative);
  11622. _pshfParent->QueryInterface(IID_PPV_ARG(IShellFolder2, &_pshf2Parent));
  11623. }
  11624. ILFree(pidlFull);
  11625. }
  11626. CallCB(SFVM_FOLDERSETTINGSFLAGS, 0, (LPARAM)&_fs.fFlags);
  11627. _vs.InitWithDefaults(this);
  11628. _rgbBackColor = CLR_INVALID;
  11629. _sizeThumbnail.cx = -1; // non init state
  11630. _iIncrementCat = 1;
  11631. _wvLayout.dwLayout = -1; // an invalid value
  11632. // NOTE we dont AddRef() psvOuter
  11633. // it has a ref on us
  11634. _psvOuter = psvOuter;
  11635. // the client needs this info to be able to do anything with us,
  11636. // so set it REALLY early on in the creation process
  11637. IUnknown_SetSite(_cCallback.GetSFVCB(), SAFECAST(this, IShellFolderView*));
  11638. for (int i = 0; i < ARRAYSIZE(_crCustomColors); i++)
  11639. _crCustomColors[i] = CLR_MYINVALID;
  11640. _UpdateRegFlags();
  11641. IDLData_InitializeClipboardFormats();
  11642. if (SUCCEEDED(CoCreateInstance(CLSID_ShellTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellTaskScheduler2, &_pScheduler))))
  11643. {
  11644. // init a set a 60 second timeout
  11645. _pScheduler->Status(ITSSFLAG_KILL_ON_DESTROY, DEFVIEW_THREAD_IDLE_TIMEOUT);
  11646. }
  11647. // Catch unexpected STACK allocations which would break us.
  11648. ASSERT(_hwndInfotip == NULL);
  11649. }
  11650. STDMETHODIMP CDefView::Init()
  11651. {
  11652. HRESULT hr;
  11653. _hdpaGroupingListActive = DPA_Create(16);
  11654. _hdpaGroupingListBackup = DPA_Create(16);
  11655. if (_hdpaGroupingListActive && _hdpaGroupingListBackup)
  11656. {
  11657. hr = S_OK;
  11658. }
  11659. else
  11660. {
  11661. hr = E_OUTOFMEMORY;
  11662. }
  11663. return hr;
  11664. }
  11665. STDAPI SHCreateShellFolderView(const SFV_CREATE* pcsfv, IShellView ** ppsv)
  11666. {
  11667. *ppsv = NULL;
  11668. HRESULT hr = E_INVALIDARG;
  11669. if (pcsfv && sizeof(*pcsfv) == pcsfv->cbSize)
  11670. {
  11671. CDefView *pdsv = new CDefView(pcsfv->pshf, pcsfv->psfvcb, pcsfv->psvOuter);
  11672. if (pdsv)
  11673. {
  11674. hr = pdsv->Init();
  11675. if (SUCCEEDED(hr))
  11676. {
  11677. *ppsv = pdsv;
  11678. }
  11679. else
  11680. {
  11681. pdsv->Release();
  11682. }
  11683. }
  11684. }
  11685. return hr;
  11686. }
  11687. void CDVDropTarget::LeaveAndReleaseData()
  11688. {
  11689. DragLeave();
  11690. }
  11691. void CDVDropTarget::ReleaseDataObject()
  11692. {
  11693. ATOMICRELEASE(_pdtobj);
  11694. }
  11695. void CDVDropTarget::ReleaseCurrentDropTarget()
  11696. {
  11697. CDefView *pdv = IToClass(CDefView, _dvdt, this);
  11698. if (_pdtgtCur)
  11699. {
  11700. _pdtgtCur->DragLeave();
  11701. ATOMICRELEASE(_pdtgtCur);
  11702. }
  11703. pdv->_itemCur = -2;
  11704. // WARNING: Never touch pdv->itemOver in this function.
  11705. }
  11706. HRESULT CDVDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  11707. {
  11708. CDefView *pdv = IToClass(CDefView, _dvdt, this);
  11709. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  11710. // Don't allow a drop from our webview content to ourself!
  11711. _fIgnoreSource = FALSE;
  11712. IOleCommandTarget* pct;
  11713. if (pdv->_cFrame.IsWebView() && SUCCEEDED(pdv->_cFrame.GetCommandTarget(&pct)))
  11714. {
  11715. VARIANTARG v = {0};
  11716. if (SUCCEEDED(pct->Exec(&CGID_ShellDocView, SHDVID_ISDRAGSOURCE, 0, NULL, &v)))
  11717. {
  11718. pct->Release();
  11719. if (v.lVal)
  11720. {
  11721. *pdwEffect = DROPEFFECT_NONE;
  11722. _fIgnoreSource = TRUE;
  11723. return S_OK;
  11724. }
  11725. }
  11726. }
  11727. g_fDraggingOverSource = FALSE;
  11728. _grfKeyState = grfKeyState;
  11729. ASSERT(_pdtgtCur == NULL);
  11730. // don't really need to do this, but this sets the target state
  11731. ReleaseCurrentDropTarget();
  11732. _itemOver = -2;
  11733. //
  11734. // In case of Desktop, we should not lock the enter screen.
  11735. //
  11736. HWND hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain;
  11737. GetWindowRect(hwndLock, &_rcLockWindow);
  11738. DAD_DragEnterEx3(hwndLock, ptl, pdtobj);
  11739. DAD_InitScrollData(&_asd);
  11740. _ptLast.x = _ptLast.y = 0x7fffffff; // put bogus value to force redraw
  11741. return S_OK;
  11742. }
  11743. #define DVAE_BEFORE 0x01
  11744. #define DVAE_AFTER 0x02
  11745. // this MUST set pdwEffect to 0 or DROPEFFECT_MOVE if it's a default drag drop
  11746. // in the same window
  11747. void CDefView::_AlterEffect(DWORD grfKeyState, DWORD *pdwEffect, UINT uFlags)
  11748. {
  11749. g_fDraggingOverSource = FALSE;
  11750. if (_IsDropOnSource(NULL))
  11751. {
  11752. if (_IsPositionedView())
  11753. {
  11754. // If this is default drag & drop, enable move.
  11755. if (uFlags & DVAE_AFTER)
  11756. {
  11757. if ((grfKeyState & (MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_ALT)) == MK_LBUTTON)
  11758. {
  11759. *pdwEffect = DROPEFFECT_MOVE;
  11760. g_fDraggingOverSource = TRUE;
  11761. }
  11762. else if (grfKeyState & MK_RBUTTON)
  11763. {
  11764. *pdwEffect |= DROPEFFECT_MOVE;
  11765. }
  11766. }
  11767. }
  11768. else
  11769. {
  11770. if (uFlags & DVAE_BEFORE)
  11771. {
  11772. // No. Disable move.
  11773. *pdwEffect &= ~DROPEFFECT_MOVE;
  11774. // default drag & drop, disable all.
  11775. if ((grfKeyState & (MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_ALT)) == MK_LBUTTON)
  11776. {
  11777. *pdwEffect = 0;
  11778. }
  11779. }
  11780. }
  11781. }
  11782. }
  11783. HRESULT CDVDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  11784. {
  11785. CDefView *pdv = IToClass(CDefView, _dvdt, this);
  11786. HRESULT hr = S_OK;
  11787. DWORD dwEffectScroll = 0;
  11788. DWORD dwEffectOut = 0;
  11789. DWORD dwEffectOutToCache;
  11790. BOOL fSameImage = FALSE;
  11791. if (_fIgnoreSource)
  11792. {
  11793. // for parity with win2k behavior, we need to bail out from DragOver
  11794. // if we hit the SHDVID_ISDRAGSOURCE in DragEnter.
  11795. // this is so when you have a stretched background in active desktop and
  11796. // show desktop icons is off, when you drag the background image around
  11797. // you'll get DROPEFFECT_NONE instead of a bad DROPEFFECT_COPY.
  11798. *pdwEffect = DROPEFFECT_NONE;
  11799. return S_OK;
  11800. }
  11801. POINT pt = {ptl.x, ptl.y}; // in screen coords
  11802. RECT rc;
  11803. GetWindowRect(pdv->_hwndListview, &rc);
  11804. BOOL fInRect = PtInRect(&rc, pt);
  11805. ScreenToClient(pdv->_hwndListview, &pt); // now in client
  11806. // assume coords of our window match listview
  11807. if (DAD_AutoScroll(pdv->_hwndListview, &_asd, &pt))
  11808. dwEffectScroll = DROPEFFECT_SCROLL;
  11809. // hilight an item, or unhilight all items (DropTarget returns -1)
  11810. int itemNew = fInRect ? pdv->_HitTest(&pt, TRUE) : -1;
  11811. // If we are dragging over on a different item, get its IDropTarget
  11812. // interface or adjust itemNew to -1.
  11813. if (_itemOver != itemNew)
  11814. {
  11815. IDropTarget *pdtgtNew = NULL;
  11816. _dwLastTime = GetTickCount(); // keep track for auto-expanding the tree
  11817. _itemOver = itemNew;
  11818. // Avoid dropping onto drag source objects.
  11819. if ((itemNew != -1) && pdv->_bDragSource)
  11820. {
  11821. UINT uState = ListView_GetItemState(pdv->_hwndListview, itemNew, LVIS_SELECTED);
  11822. if (uState & LVIS_SELECTED)
  11823. itemNew = -1;
  11824. }
  11825. // If we are dragging over an item, try to get its IDropTarget.
  11826. if (itemNew != -1)
  11827. {
  11828. // We are dragging over an item.
  11829. LPCITEMIDLIST apidl[1] = { pdv->_GetPIDL(itemNew) };
  11830. if (apidl[0])
  11831. {
  11832. pdv->_pshf->GetUIObjectOf(pdv->_hwndMain, 1, apidl, IID_PPV_ARG_NULL(IDropTarget, &pdtgtNew));
  11833. ASSERT(itemNew != pdv->_itemCur); // MUST not be the same
  11834. }
  11835. if (pdtgtNew == NULL)
  11836. {
  11837. // If the item is not a drop target, don't hightlight it
  11838. // treat it as transparent.
  11839. itemNew = -1;
  11840. }
  11841. }
  11842. // If the new target is different from the current one, switch it.
  11843. if (pdv->_itemCur != itemNew)
  11844. {
  11845. // Release previous drop target, if any.
  11846. ReleaseCurrentDropTarget();
  11847. ASSERT(_pdtgtCur==NULL);
  11848. // Update pdv->_itemCur which indicates the current target.
  11849. // (Note that it might be different from _itemOver).
  11850. pdv->_itemCur = itemNew;
  11851. // If we are dragging over the background or over non-sink item,
  11852. // get the drop target for the folder.
  11853. if (itemNew == -1)
  11854. {
  11855. // We are dragging over the background, this can be NULL
  11856. ASSERT(pdtgtNew == NULL);
  11857. _pdtgtCur = pdv->_pdtgtBack;
  11858. if (_pdtgtCur)
  11859. _pdtgtCur->AddRef();
  11860. }
  11861. else
  11862. {
  11863. ASSERT(pdtgtNew);
  11864. _pdtgtCur = pdtgtNew;
  11865. }
  11866. // Hilight the sink item (itemNew != -1) or unhilight all (-1).
  11867. LVUtil_DragSelectItem(pdv->_hwndListview, itemNew);
  11868. // Call IDropTarget::DragEnter of the target object.
  11869. if (_pdtgtCur)
  11870. {
  11871. // pdwEffect is in/out parameter.
  11872. dwEffectOut = *pdwEffect; // pdwEffect in
  11873. // Special case if we are dragging within a source window
  11874. pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE);
  11875. hr = _pdtgtCur->DragEnter(_pdtobj, grfKeyState, ptl, &dwEffectOut);
  11876. pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_AFTER);
  11877. }
  11878. else
  11879. {
  11880. ASSERT(dwEffectOut==0);
  11881. pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE | DVAE_AFTER);
  11882. }
  11883. TraceMsg(TF_DEFVIEW, "CDV::DragOver dwEIn=%x, dwEOut=%x", *pdwEffect, dwEffectOut);
  11884. }
  11885. else
  11886. {
  11887. ASSERT(pdtgtNew == NULL); // It must be NULL
  11888. goto NoChange;
  11889. }
  11890. // Every time we're over a new item, record this information so we can handle the insertmark.
  11891. _fItemOverNotADropTarget = (itemNew == -1);
  11892. }
  11893. else
  11894. {
  11895. NoChange:
  11896. if (_itemOver != -1)
  11897. {
  11898. DWORD dwNow = GetTickCount();
  11899. if ((dwNow - _dwLastTime) >= 1000)
  11900. {
  11901. _dwLastTime = dwNow;
  11902. // DAD_ShowDragImage(FALSE);
  11903. // OpenItem(pdv, _itemOver);
  11904. // DAD_ShowDragImage(TRUE);
  11905. }
  11906. }
  11907. //
  11908. // No change in the selection. We assume that *pdwEffect stays
  11909. // the same during the same drag-loop as long as the key state doesn't change.
  11910. //
  11911. if ((_grfKeyState != grfKeyState) && _pdtgtCur)
  11912. {
  11913. // Note that pdwEffect is in/out parameter.
  11914. dwEffectOut = *pdwEffect; // pdwEffect in
  11915. // Special case if we are dragging within a source window
  11916. pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_BEFORE);
  11917. hr = _pdtgtCur->DragOver(grfKeyState, ptl, &dwEffectOut);
  11918. pdv->_AlterEffect(grfKeyState, &dwEffectOut, DVAE_AFTER);
  11919. }
  11920. else
  11921. {
  11922. // Same item and same key state. Use the previous dwEffectOut.
  11923. dwEffectOut = _dwEffectOut;
  11924. fSameImage = TRUE;
  11925. hr = S_OK;
  11926. }
  11927. }
  11928. // Cache the calculated dwEffectOut (BEFORE making local modifications below).
  11929. dwEffectOutToCache = dwEffectOut;
  11930. // Activate/deactivate insertmark, if appropriate.
  11931. LVINSERTMARK lvim = { sizeof(LVINSERTMARK), 0, -1, 0 };
  11932. if (_fItemOverNotADropTarget)
  11933. {
  11934. // Only do the insertion mark stuff if we're in a view mode that makes sense for these:
  11935. if (pdv->_IsAutoArrange() || (pdv->_fs.fFlags & FWF_SNAPTOGRID))
  11936. {
  11937. ListView_InsertMarkHitTest(pdv->_hwndListview, &pt, &lvim);
  11938. if (pdv->_bDragSource && pdv->_IsAutoArrange() && (lvim.iItem == -1))
  11939. {
  11940. // a "move" drop here won't do anything so set the effect appropriately
  11941. if (dwEffectOut & DROPEFFECT_MOVE)
  11942. {
  11943. // fall back to "copy" drop effect (if supported)
  11944. if (*pdwEffect & DROPEFFECT_COPY)
  11945. {
  11946. dwEffectOut |= DROPEFFECT_COPY;
  11947. }
  11948. // fall back to "link" drop effect (if supported)
  11949. else if (*pdwEffect & DROPEFFECT_LINK)
  11950. {
  11951. dwEffectOut |= DROPEFFECT_LINK;
  11952. }
  11953. // fall back to no drop effect
  11954. dwEffectOut &= ~DROPEFFECT_MOVE;
  11955. }
  11956. // NOTE: a DROPEFFECT_MOVE still comes through the ::Drop for a left-drop...
  11957. // we might want to remember that we're exclududing move (_bDragSourceDropOnDragItem)
  11958. }
  11959. }
  11960. }
  11961. ListView_SetInsertMark(pdv->_hwndListview, &lvim);
  11962. _grfKeyState = grfKeyState; // store these for the next Drop
  11963. _dwEffectOut = dwEffectOutToCache; // and DragOver
  11964. // OLE does not call IDropTarget::Drop if we return something
  11965. // valid. We force OLE call it by returning DROPEFFECT_SCROLL.
  11966. if (g_fDraggingOverSource)
  11967. dwEffectScroll = DROPEFFECT_SCROLL;
  11968. *pdwEffect = dwEffectOut | dwEffectScroll; // pdwEffect out
  11969. if (!(fSameImage && pt.x == _ptLast.x && pt.y == _ptLast.y))
  11970. {
  11971. HWND hwndLock = pdv->_IsDesktop() ? pdv->_hwndView : pdv->_hwndMain;
  11972. DAD_DragMoveEx(hwndLock, ptl);
  11973. _ptLast.x = ptl.x;
  11974. _ptLast.y = ptl.y;
  11975. }
  11976. return hr;
  11977. }
  11978. HRESULT CDVDropTarget::DragLeave()
  11979. {
  11980. CDefView *pdv = IToClass(CDefView, _dvdt, this);
  11981. //
  11982. // Make it possible to call it more than necessary.
  11983. //
  11984. if (_pdtobj)
  11985. {
  11986. TraceMsg(TF_DEFVIEW, "CDVDropTarget::DragLeave");
  11987. ReleaseCurrentDropTarget();
  11988. _itemOver = -2;
  11989. ReleaseDataObject();
  11990. DAD_DragLeave();
  11991. LVUtil_DragSelectItem(pdv->_hwndListview, -1);
  11992. }
  11993. g_fDraggingOverSource = FALSE;
  11994. ASSERT(_pdtgtCur == NULL);
  11995. ASSERT(_pdtobj == NULL);
  11996. LVINSERTMARK lvim = { sizeof(LVINSERTMARK), 0, -1, 0 }; // clear insert mark (-1)
  11997. ListView_SetInsertMark(pdv->_hwndListview, &lvim);
  11998. return S_OK;
  11999. }
  12000. HRESULT CDVDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  12001. {
  12002. CDefView *pdv = IToClass(CDefView, _dvdt, this);
  12003. IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
  12004. pdv->_ptDrop.x = pt.x;
  12005. pdv->_ptDrop.y = pt.y;
  12006. ScreenToClient(pdv->_hwndListview, &pdv->_ptDrop);
  12007. //
  12008. // handle moves within the same window here.
  12009. // depend on _AlterEffect forcing in DROPEFFECT_MOVE and only
  12010. // dropeffect move when drag in same window
  12011. //
  12012. // Notes: We need to use _grfKeyState instead of grfKeyState
  12013. // to see if the left mouse was used or not during dragging.
  12014. //
  12015. pdv->_AlterEffect(_grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
  12016. if ((_grfKeyState & MK_LBUTTON) && (*pdwEffect == DROPEFFECT_MOVE) &&
  12017. (pdv->_IsDropOnSource(NULL)))
  12018. {
  12019. // This means we are left-dropping on ourselves, so we just move
  12020. // the icons.
  12021. DAD_DragLeave();
  12022. pdv->_SameViewMoveIcons();
  12023. SetForegroundWindow(pdv->_hwndMain);
  12024. ASSERT(pdv->_bDropAnchor == FALSE);
  12025. *pdwEffect = 0; // the underlying objects didn't 'move' anywhere
  12026. ReleaseCurrentDropTarget();
  12027. }
  12028. else if (_pdtgtCur)
  12029. {
  12030. // use this local because if pdtgtCur::Drop does a UnlockWindow
  12031. // then hits an error and needs to put up a dialog,
  12032. // we could get re-entered and clobber the defview's pdtgtCur
  12033. IDropTarget *pdtgtCur = _pdtgtCur;
  12034. _pdtgtCur = NULL;
  12035. //
  12036. // HACK ALERT!!!!
  12037. //
  12038. // If we don't call LVUtil_DragEnd here, we'll be able to leave
  12039. // dragged icons visible when the menu is displayed. However, because
  12040. // we are calling IDropTarget::Drop() which may create some modeless
  12041. // dialog box or something, we can not ensure the locked state of
  12042. // the list view -- LockWindowUpdate() can lock only one window at
  12043. // a time. Therefore, we skip this call only if the pdtgtCur
  12044. // is a subclass of CIDLDropTarget, assuming its Drop calls
  12045. // CDefView::DragEnd (or CIDLDropTarget_DragDropMenu) appropriately.
  12046. //
  12047. pdv->_bDropAnchor = TRUE;
  12048. if (!DoesDropTargetSupportDAD(pdtgtCur))
  12049. {
  12050. // This will hide the dragged image.
  12051. DAD_DragLeave();
  12052. // reset the drag image list so that the user
  12053. // can start another drag&drop while we are in this
  12054. // Drop() member function call.
  12055. DAD_SetDragImage(NULL, NULL);
  12056. }
  12057. // Special case if we are dragging within a source window
  12058. pdv->_AlterEffect(grfKeyState, pdwEffect, DVAE_BEFORE | DVAE_AFTER);
  12059. IUnknown_SetSite(pdtgtCur, SAFECAST(pdv, IShellView2*));
  12060. pdtgtCur->Drop(pdtobj, grfKeyState, pt, pdwEffect);
  12061. IUnknown_SetSite(pdtgtCur, NULL);
  12062. pdtgtCur->Release();
  12063. DAD_DragLeave();
  12064. pdv->_bDropAnchor = FALSE;
  12065. }
  12066. else
  12067. {
  12068. // We come here if Drop is called without DragMove (with DragEnter).
  12069. *pdwEffect = 0;
  12070. }
  12071. DragLeave(); // DoDragDrop does not call DragLeave() after Drop()
  12072. return S_OK;
  12073. }
  12074. BOOL CDefView::_IsBkDropTarget(IDropTarget *pdtg)
  12075. {
  12076. BOOL fRet = FALSE;
  12077. if (_bContextMenuMode)
  12078. {
  12079. if (ListView_GetSelectedCount(_hwndListview) == 0)
  12080. {
  12081. fRet = TRUE;
  12082. }
  12083. }
  12084. POINT pt;
  12085. if (!fRet)
  12086. {
  12087. if (_GetInsertPoint(&pt)) // If there is an insert point, then the background is the drop target.
  12088. return TRUE;
  12089. if (_GetDropPoint(&pt))
  12090. {
  12091. // The Drop point is returned in internal listview coordinates
  12092. // space, so we need to convert it back to client space
  12093. // before we call this function...
  12094. LVUtil_LVToClient(_hwndListview, &pt);
  12095. if (_HitTest(&pt) == -1)
  12096. {
  12097. fRet = TRUE;
  12098. }
  12099. }
  12100. }
  12101. return fRet;
  12102. }
  12103. // IShellFolderView::Rearrange
  12104. STDMETHODIMP CDefView::Rearrange(LPARAM lParamSort)
  12105. {
  12106. return _OnRearrange(lParamSort, TRUE);
  12107. }
  12108. // end user initiated arrange (click on col header, etc)
  12109. HRESULT CDefView::_OnRearrange(LPARAM lParamSort, BOOL fAllowToggle)
  12110. {
  12111. DECLAREWAITCURSOR;
  12112. _vs._iLastColumnClick = (int) _vs._lParamSort;
  12113. _vs._lParamSort = lParamSort;
  12114. // toggle the direction of the sort if on the same column
  12115. if (fAllowToggle && !_IsPositionedView() && _vs._iLastColumnClick == (int) lParamSort)
  12116. _vs._iDirection = -_vs._iDirection;
  12117. else
  12118. _vs._iDirection = 1;
  12119. SetWaitCursor();
  12120. HRESULT hr = _Sort();
  12121. // reset to the state that no items have been moved if currently in a positioned mode
  12122. // so auto-arraning works.
  12123. if (_IsPositionedView())
  12124. {
  12125. _ClearItemPositions();
  12126. }
  12127. ResetWaitCursor();
  12128. return hr;
  12129. }
  12130. STDMETHODIMP CDefView::ArrangeGrid()
  12131. {
  12132. _OnCommand(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_GRID, 0, 0));
  12133. return S_OK;
  12134. }
  12135. STDMETHODIMP CDefView::AutoArrange()
  12136. {
  12137. _OnCommand(NULL, GET_WM_COMMAND_MPS(SFVIDM_ARRANGE_AUTO, 0, 0));
  12138. return S_OK;
  12139. }
  12140. STDMETHODIMP CDefView::GetArrangeParam(LPARAM *plParamSort)
  12141. {
  12142. *plParamSort = _vs._lParamSort;
  12143. return S_OK;
  12144. }
  12145. STDMETHODIMP CDefView::AddObject(LPITEMIDLIST pidl, UINT *puItem)
  12146. {
  12147. LPITEMIDLIST pidlCopy = ILClone(pidl);
  12148. if (pidlCopy)
  12149. {
  12150. *puItem = _AddObject(pidlCopy); // takes pidl ownership.
  12151. }
  12152. else
  12153. {
  12154. *puItem = (UINT)-1;
  12155. }
  12156. // must cast to "int" because UINTs are never negative so we would
  12157. // otherwise never be able to detect failure
  12158. return (int)*puItem >= 0 ? S_OK : E_OUTOFMEMORY;
  12159. }
  12160. STDMETHODIMP CDefView::GetObjectCount(UINT *puCount)
  12161. {
  12162. *puCount = ListView_GetItemCount(_hwndListview);
  12163. return S_OK;
  12164. }
  12165. STDMETHODIMP CDefView::SetObjectCount(UINT uCount, UINT dwFlags)
  12166. {
  12167. // Mask over to the flags that map directly accross
  12168. DWORD dw = dwFlags & SFVSOC_NOSCROLL;
  12169. UINT uCountOld = 0;
  12170. GetObjectCount(&uCountOld);
  12171. if ((dwFlags & SFVSOC_INVALIDATE_ALL) == 0)
  12172. dw |= LVSICF_NOINVALIDATEALL; // gross transform
  12173. HRESULT hr = (HRESULT)SendMessage(_hwndListview, LVM_SETITEMCOUNT, (WPARAM)uCount, (LPARAM)dw);
  12174. // Notify automation if we're going from 0 to 1 or more items
  12175. if (!uCountOld && uCount)
  12176. {
  12177. _PostNoItemStateChangedMessage();
  12178. }
  12179. return hr;
  12180. }
  12181. STDMETHODIMP CDefView::GetObject(LPITEMIDLIST *ppidl, UINT uItem)
  12182. {
  12183. // Worse hack, if -42 then return our own pidl...
  12184. if (uItem == (UINT)-42)
  12185. {
  12186. *ppidl = (LPITEMIDLIST)_pidlMonitor;
  12187. return *ppidl ? S_OK : E_UNEXPECTED;
  12188. }
  12189. // Hack, if item is -2, this implies return the focused item
  12190. if (uItem == (UINT)-2)
  12191. uItem = ListView_GetNextItem(_hwndListview, -1, LVNI_FOCUSED);
  12192. *ppidl = (LPITEMIDLIST)_GetPIDL(uItem); // cast due to bad interface def
  12193. return *ppidl ? S_OK : E_UNEXPECTED;
  12194. }
  12195. STDMETHODIMP CDefView::RemoveObject(LPITEMIDLIST pidl, UINT *puItem)
  12196. {
  12197. *puItem = _RemoveObject(pidl, FALSE);
  12198. // must cast to "int" because UINTs are never negative so we would
  12199. // otherwise never be able to detect failure
  12200. return (int)*puItem >= 0 ? S_OK : E_INVALIDARG;
  12201. }
  12202. STDMETHODIMP CDefView::UpdateObject(LPITEMIDLIST pidlOld, LPITEMIDLIST pidlNew, UINT *puItem)
  12203. {
  12204. *puItem = _UpdateObject(pidlOld, pidlNew);
  12205. return (int)(*puItem) >= 0 ? S_OK : E_INVALIDARG;
  12206. }
  12207. STDMETHODIMP CDefView::RefreshObject(LPITEMIDLIST pidl, UINT *puItem)
  12208. {
  12209. *puItem = _RefreshObject(&pidl);
  12210. // must cast to "int" because UINTs are never negative so we would
  12211. // otherwise never be able to detect failure
  12212. return (int)*puItem >= 0 ? S_OK : E_INVALIDARG;
  12213. }
  12214. STDMETHODIMP CDefView::SetRedraw(BOOL bRedraw)
  12215. {
  12216. SendMessage(_hwndListview, WM_SETREDRAW, (WPARAM)bRedraw, 0);
  12217. return S_OK;
  12218. }
  12219. STDMETHODIMP CDefView::GetSelectedObjects(LPCITEMIDLIST **pppidl, UINT *puItems)
  12220. {
  12221. return _GetItemObjects(pppidl, SVGIO_SELECTION, puItems);
  12222. }
  12223. STDMETHODIMP CDefView::GetSelectedCount(UINT *puSelected)
  12224. {
  12225. *puSelected = ListView_GetSelectedCount(_hwndListview);
  12226. return S_OK;
  12227. }
  12228. BOOL CDefView::_IsDropOnSource(IDropTarget *pdtgt)
  12229. {
  12230. // context menu paste (_bMouseMenu shows context menu, cut stuff shows source)
  12231. if (_bMouseMenu && _bHaveCutStuff)
  12232. {
  12233. int iItem = ListView_GetNextItem(_hwndListview, -1, LVNI_SELECTED);
  12234. if (iItem == -1)
  12235. return TRUE;
  12236. }
  12237. if (_itemCur != -1 || !_bDragSource)
  12238. {
  12239. // We did not drag onto the background of the source
  12240. return FALSE;
  12241. }
  12242. return TRUE;
  12243. }
  12244. STDMETHODIMP CDefView::IsDropOnSource(IDropTarget *pDropTarget)
  12245. {
  12246. return _IsDropOnSource(pDropTarget) ? S_OK : S_FALSE;
  12247. }
  12248. STDMETHODIMP CDefView::MoveIcons(IDataObject *pdtobj)
  12249. {
  12250. return E_NOTIMPL;
  12251. }
  12252. STDMETHODIMP CDefView::GetDropPoint(POINT *ppt)
  12253. {
  12254. return _GetDropPoint(ppt) ? S_OK : S_FALSE;
  12255. }
  12256. STDMETHODIMP CDefView::GetDragPoint(POINT *ppt)
  12257. {
  12258. return _GetDragPoint(ppt) ? S_OK : S_FALSE;
  12259. }
  12260. STDMETHODIMP CDefView::SetItemPos(LPCITEMIDLIST pidl, POINT *ppt)
  12261. {
  12262. SFV_SETITEMPOS sip;
  12263. sip.pidl = pidl;
  12264. sip.pt = *ppt;
  12265. _SetItemPos(&sip);
  12266. return S_OK;
  12267. }
  12268. STDMETHODIMP CDefView::IsBkDropTarget(IDropTarget *pDropTarget)
  12269. {
  12270. return _IsBkDropTarget(pDropTarget) ? S_OK : S_FALSE;
  12271. }
  12272. STDMETHODIMP CDefView::SetClipboard(BOOL bMove)
  12273. {
  12274. _OnSetClipboard(bMove); // do this always, even if not current active view
  12275. return S_OK;
  12276. }
  12277. // defcm.cpp asks us to setup the points of the currently selected objects
  12278. // into the data object on Copy/Cut commands
  12279. STDMETHODIMP CDefView::SetPoints(IDataObject *pdtobj)
  12280. {
  12281. LPCITEMIDLIST *apidl;
  12282. UINT cItems;
  12283. HRESULT hr = GetSelectedObjects(&apidl, &cItems);
  12284. if (SUCCEEDED(hr) && cItems)
  12285. {
  12286. _SetPoints(cItems, apidl, pdtobj);
  12287. LocalFree((HLOCAL)apidl);
  12288. }
  12289. return hr;
  12290. }
  12291. STDMETHODIMP CDefView::GetItemSpacing(ITEMSPACING *pSpacing)
  12292. {
  12293. return _GetItemSpacing(pSpacing) ? S_OK : S_FALSE;
  12294. }
  12295. STDMETHODIMP CDefView::SetCallback(IShellFolderViewCB* pNewCB, IShellFolderViewCB** ppOldCB)
  12296. {
  12297. *ppOldCB = NULL;
  12298. return _cCallback.SetCallback(pNewCB, ppOldCB);
  12299. }
  12300. const UINT c_rgiSelectFlags[][2] =
  12301. {
  12302. { SFVS_SELECT_ALLITEMS, SFVIDM_SELECT_ALL },
  12303. { SFVS_SELECT_NONE, SFVIDM_DESELECT_ALL },
  12304. { SFVS_SELECT_INVERT, SFVIDM_SELECT_INVERT }
  12305. };
  12306. STDMETHODIMP CDefView::Select(UINT dwFlags)
  12307. {
  12308. // translate the flag into the menu ID
  12309. for (int i = 0; i < ARRAYSIZE(c_rgiSelectFlags); i++)
  12310. {
  12311. if (c_rgiSelectFlags[i][0] == dwFlags)
  12312. {
  12313. return (HRESULT)_OnCommand(NULL, c_rgiSelectFlags[i][1], 0);
  12314. }
  12315. }
  12316. return E_INVALIDARG;
  12317. }
  12318. STDMETHODIMP CDefView::QuerySupport(UINT * pdwSupport)
  12319. {
  12320. // *pdwSupport is an in/out param, we leave the out == in
  12321. return S_OK; // DefView supports all the operations...
  12322. }
  12323. STDMETHODIMP CDefView::SetAutomationObject(IDispatch *pdisp)
  12324. {
  12325. // release back pointers
  12326. IUnknown_SetOwner(_pauto, NULL);
  12327. IUnknown_SetSite(_pauto, NULL);
  12328. IUnknown_Set((IUnknown **)&_pauto, pdisp); // hold or free _pauto
  12329. // this connects the automation object to our view, so it can implement
  12330. // stuff like "SelectedItems"
  12331. IUnknown_SetOwner(_pauto, SAFECAST(this, IShellFolderView *));
  12332. // use the browser as the site so OM related QueryService calls will find
  12333. // the browser above us as the place to do security checks instead of defivew
  12334. // this is stuff that depends on the zone of the caller as the security check
  12335. IUnknown_SetSite(_pauto, _psb);
  12336. return S_OK;
  12337. }
  12338. STDMETHODIMP CDefView::SelectAndPositionItems(UINT cidl, LPCITEMIDLIST* apidl, POINT* apt, DWORD dwFlags)
  12339. {
  12340. for (UINT i = 0; i < cidl; i++)
  12341. SelectAndPositionItem(apidl[i], dwFlags, apt ? &apt[i] : NULL);
  12342. return S_OK;
  12343. }
  12344. // -------------- auto scroll stuff --------------
  12345. BOOL _AddTimeSample(AUTO_SCROLL_DATA *pad, const POINT *ppt, DWORD dwTime)
  12346. {
  12347. pad->pts[pad->iNextSample] = *ppt;
  12348. pad->dwTimes[pad->iNextSample] = dwTime;
  12349. pad->iNextSample++;
  12350. if (pad->iNextSample == ARRAYSIZE(pad->pts))
  12351. pad->bFull = TRUE;
  12352. pad->iNextSample = pad->iNextSample % ARRAYSIZE(pad->pts);
  12353. return pad->bFull;
  12354. }
  12355. #ifdef DEBUG
  12356. // for debugging, verify we have good averages
  12357. DWORD g_time = 0;
  12358. int g_distance = 0;
  12359. #endif
  12360. int _CurrentVelocity(AUTO_SCROLL_DATA *pad)
  12361. {
  12362. int i, iStart, iNext;
  12363. int dx, dy, distance;
  12364. DWORD time;
  12365. ASSERT(pad->bFull);
  12366. distance = 0;
  12367. time = 1; // avoid div by zero
  12368. i = iStart = pad->iNextSample % ARRAYSIZE(pad->pts);
  12369. do {
  12370. iNext = (i + 1) % ARRAYSIZE(pad->pts);
  12371. dx = abs(pad->pts[i].x - pad->pts[iNext].x);
  12372. dy = abs(pad->pts[i].y - pad->pts[iNext].y);
  12373. distance += (dx + dy);
  12374. time += abs(pad->dwTimes[i] - pad->dwTimes[iNext]);
  12375. i = iNext;
  12376. } while (i != iStart);
  12377. #ifdef DEBUG
  12378. g_time = time;
  12379. g_distance = distance;
  12380. #endif
  12381. // scale this so we don't loose accuracy
  12382. return (distance * 1024) / time;
  12383. }
  12384. // NOTE: this is duplicated in shell32.dll
  12385. //
  12386. // checks to see if we are at the end position of a scroll bar
  12387. // to avoid scrolling when not needed (avoid flashing)
  12388. //
  12389. // in:
  12390. // code SB_VERT or SB_HORZ
  12391. // bDown FALSE is up or left
  12392. // TRUE is down or right
  12393. BOOL CanScroll(HWND hwnd, int code, BOOL bDown)
  12394. {
  12395. SCROLLINFO si;
  12396. si.cbSize = sizeof(si);
  12397. si.fMask = (SIF_RANGE | SIF_PAGE | SIF_POS);
  12398. GetScrollInfo(hwnd, code, &si);
  12399. if (bDown)
  12400. {
  12401. if (si.nPage)
  12402. si.nMax -= si.nPage - 1;
  12403. return si.nPos < si.nMax;
  12404. }
  12405. else
  12406. {
  12407. return si.nPos > si.nMin;
  12408. }
  12409. }
  12410. #define DSD_NONE 0x0000
  12411. #define DSD_UP 0x0001
  12412. #define DSD_DOWN 0x0002
  12413. #define DSD_LEFT 0x0004
  12414. #define DSD_RIGHT 0x0008
  12415. DWORD DAD_DragScrollDirection(HWND hwnd, const POINT *ppt)
  12416. {
  12417. RECT rcOuter, rc;
  12418. DWORD dwDSD = DSD_NONE;
  12419. DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
  12420. #define g_cxVScroll GetSystemMetrics(SM_CXVSCROLL)
  12421. #define g_cyHScroll GetSystemMetrics(SM_CYHSCROLL)
  12422. GetClientRect(hwnd, &rc);
  12423. if (dwStyle & WS_HSCROLL)
  12424. rc.bottom -= g_cyHScroll;
  12425. if (dwStyle & WS_VSCROLL)
  12426. rc.right -= g_cxVScroll;
  12427. // the explorer forwards us drag/drop things outside of our client area
  12428. // so we need to explictly test for that before we do things
  12429. //
  12430. rcOuter = rc;
  12431. InflateRect(&rcOuter, g_cxSmIcon, g_cySmIcon);
  12432. InflateRect(&rc, -g_cxIcon, -g_cyIcon);
  12433. if (!PtInRect(&rc, *ppt) && PtInRect(&rcOuter, *ppt))
  12434. {
  12435. // Yep - can we scroll?
  12436. if (dwStyle & WS_HSCROLL)
  12437. {
  12438. if (ppt->x < rc.left)
  12439. {
  12440. if (CanScroll(hwnd, SB_HORZ, FALSE))
  12441. dwDSD |= DSD_LEFT;
  12442. }
  12443. else if (ppt->x > rc.right)
  12444. {
  12445. if (CanScroll(hwnd, SB_HORZ, TRUE))
  12446. dwDSD |= DSD_RIGHT;
  12447. }
  12448. }
  12449. if (dwStyle & WS_VSCROLL)
  12450. {
  12451. if (ppt->y < rc.top)
  12452. {
  12453. if (CanScroll(hwnd, SB_VERT, FALSE))
  12454. dwDSD |= DSD_UP;
  12455. }
  12456. else if (ppt->y > rc.bottom)
  12457. {
  12458. if (CanScroll(hwnd, SB_VERT, TRUE))
  12459. dwDSD |= DSD_DOWN;
  12460. }
  12461. }
  12462. }
  12463. return dwDSD;
  12464. }
  12465. #define SCROLL_FREQUENCY (GetDoubleClickTime()/2) // 1 line scroll every 1/4 second
  12466. #define MIN_SCROLL_VELOCITY 20 // scaled mouse velocity
  12467. BOOL WINAPI DAD_AutoScroll(HWND hwnd, AUTO_SCROLL_DATA *pad, const POINT *pptNow)
  12468. {
  12469. // first time we've been called, init our state
  12470. int v;
  12471. DWORD dwTimeNow = GetTickCount();
  12472. DWORD dwDSD = DAD_DragScrollDirection(hwnd, pptNow);
  12473. if (!_AddTimeSample(pad, pptNow, dwTimeNow))
  12474. return dwDSD;
  12475. v = _CurrentVelocity(pad);
  12476. if (v <= MIN_SCROLL_VELOCITY)
  12477. {
  12478. // Nope, do some scrolling.
  12479. if ((dwTimeNow - pad->dwLastScroll) < SCROLL_FREQUENCY)
  12480. dwDSD = 0;
  12481. if (dwDSD & DSD_UP)
  12482. {
  12483. DAD_ShowDragImage(FALSE);
  12484. FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
  12485. }
  12486. else if (dwDSD & DSD_DOWN)
  12487. {
  12488. DAD_ShowDragImage(FALSE);
  12489. FORWARD_WM_VSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
  12490. }
  12491. if (dwDSD & DSD_LEFT)
  12492. {
  12493. DAD_ShowDragImage(FALSE);
  12494. FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEUP, 1, SendMessage);
  12495. }
  12496. else if (dwDSD & DSD_RIGHT)
  12497. {
  12498. DAD_ShowDragImage(FALSE);
  12499. FORWARD_WM_HSCROLL(hwnd, NULL, SB_LINEDOWN, 1, SendMessage);
  12500. }
  12501. DAD_ShowDragImage(TRUE);
  12502. if (dwDSD)
  12503. {
  12504. TraceMsg(TF_DEFVIEW, "v=%d", v);
  12505. pad->dwLastScroll = dwTimeNow;
  12506. }
  12507. }
  12508. return dwDSD; // bits set if in scroll region
  12509. }
  12510. // warning: global data holding COM objects that may span apartment boundaries
  12511. // be very careful
  12512. HDSA g_hdsaDefViewCopyHook = NULL;
  12513. typedef struct {
  12514. HWND hwndView;
  12515. CDefView *pdv;
  12516. } DVCOPYHOOK;
  12517. void CDefView::AddCopyHook()
  12518. {
  12519. ENTERCRITICAL;
  12520. if (!g_hdsaDefViewCopyHook)
  12521. {
  12522. g_hdsaDefViewCopyHook = DSA_Create(sizeof(DVCOPYHOOK), 4);
  12523. TraceMsg(TF_DEFVIEW, "AddCopyHook creating the dsa");
  12524. }
  12525. if (g_hdsaDefViewCopyHook)
  12526. {
  12527. DVCOPYHOOK dvch = { _hwndView, this };
  12528. ASSERT(dvch.hwndView);
  12529. if (DSA_AppendItem(g_hdsaDefViewCopyHook, &dvch)!=-1)
  12530. {
  12531. AddRef();
  12532. TraceMsg(TF_DEFVIEW, "AddCopyHook successfully added (total=%d)",
  12533. DSA_GetItemCount(g_hdsaDefViewCopyHook));
  12534. }
  12535. }
  12536. LEAVECRITICAL;
  12537. }
  12538. int CDefView::FindCopyHook(BOOL fRemoveInvalid)
  12539. {
  12540. ASSERTCRITICAL;
  12541. if (g_hdsaDefViewCopyHook)
  12542. {
  12543. int item = DSA_GetItemCount(g_hdsaDefViewCopyHook);
  12544. while (--item >= 0)
  12545. {
  12546. const DVCOPYHOOK *pdvch = (const DVCOPYHOOK *)DSA_GetItemPtr(g_hdsaDefViewCopyHook, item);
  12547. if (pdvch)
  12548. {
  12549. if (fRemoveInvalid)
  12550. {
  12551. if (!IsWindow(pdvch->hwndView))
  12552. {
  12553. TraceMsg(TF_WARNING, "FindCopyHook: found a invalid element, removing...");
  12554. DSA_DeleteItem(g_hdsaDefViewCopyHook, item);
  12555. continue;
  12556. }
  12557. }
  12558. if ((pdvch->hwndView == _hwndView) && (pdvch->pdv == this))
  12559. {
  12560. return item;
  12561. }
  12562. }
  12563. else
  12564. {
  12565. ASSERT(0);
  12566. }
  12567. }
  12568. }
  12569. return -1; // not found
  12570. }
  12571. void CDefView::RemoveCopyHook()
  12572. {
  12573. IShellView *psv = NULL;
  12574. ENTERCRITICAL;
  12575. if (g_hdsaDefViewCopyHook)
  12576. {
  12577. int item = FindCopyHook(TRUE);
  12578. if (item != -1)
  12579. {
  12580. DVCOPYHOOK *pdvch = (DVCOPYHOOK *)DSA_GetItemPtr(g_hdsaDefViewCopyHook, item);
  12581. psv = pdvch->pdv;
  12582. TraceMsg(TF_DEFVIEW, "RemoveCopyHook removing an element");
  12583. DSA_DeleteItem(g_hdsaDefViewCopyHook, item);
  12584. //
  12585. // If this is the last guy, destroy it.
  12586. //
  12587. if (DSA_GetItemCount(g_hdsaDefViewCopyHook) == 0)
  12588. {
  12589. TraceMsg(TF_DEFVIEW, "RemoveCopyHook destroying hdsa (no element)");
  12590. DSA_Destroy(g_hdsaDefViewCopyHook);
  12591. g_hdsaDefViewCopyHook = NULL;
  12592. }
  12593. }
  12594. }
  12595. LEAVECRITICAL;
  12596. //
  12597. // Release it outside the critical section.
  12598. //
  12599. ATOMICRELEASE(psv);
  12600. }
  12601. STDAPI_(UINT) DefView_CopyHook(const COPYHOOKINFO *pchi)
  12602. {
  12603. UINT idRet = IDYES;
  12604. if (g_hdsaDefViewCopyHook==NULL)
  12605. {
  12606. return idRet;
  12607. }
  12608. for (int item = 0; ; item++)
  12609. {
  12610. DVCOPYHOOK dvch = { NULL, NULL };
  12611. // We should minimize this critical section (and must not
  12612. // call pfnCallBack which may popup UI!).
  12613. ENTERCRITICAL;
  12614. if (g_hdsaDefViewCopyHook && DSA_GetItem(g_hdsaDefViewCopyHook, item, &dvch))
  12615. {
  12616. dvch.pdv->AddRef();
  12617. }
  12618. LEAVECRITICAL;
  12619. if (dvch.pdv)
  12620. {
  12621. if (IsWindow(dvch.hwndView))
  12622. {
  12623. HRESULT hr = dvch.pdv->CallCB(SFVM_NOTIFYCOPYHOOK, 0, (LPARAM)pchi);
  12624. ATOMICRELEASE(dvch.pdv);
  12625. if (SUCCEEDED(hr) && (hr != S_OK))
  12626. {
  12627. idRet = HRESULT_CODE(hr);
  12628. ASSERT(idRet==IDYES || idRet==IDCANCEL || idRet==IDNO);
  12629. break;
  12630. }
  12631. item++;
  12632. }
  12633. else
  12634. {
  12635. TraceMsg(TF_DEFVIEW, "DefView_CopyHook list has an invalid element");
  12636. ATOMICRELEASE(dvch.pdv);
  12637. }
  12638. }
  12639. else
  12640. {
  12641. break; // no more item.
  12642. }
  12643. }
  12644. return idRet;
  12645. }
  12646. // IOleCommandTarget stuff - just forward to the webview
  12647. STDMETHODIMP CDefView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  12648. {
  12649. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  12650. BOOL fQSCalled = FALSE;
  12651. if (_cFrame.IsWebView())
  12652. {
  12653. IOleCommandTarget* pct;
  12654. if (SUCCEEDED(_cFrame.GetCommandTarget(&pct)))
  12655. {
  12656. hr = pct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
  12657. fQSCalled = SUCCEEDED(hr);
  12658. pct->Release();
  12659. }
  12660. }
  12661. if (pguidCmdGroup == NULL)
  12662. {
  12663. if (rgCmds == NULL)
  12664. return E_INVALIDARG;
  12665. for (UINT i = 0; i < cCmds; i++)
  12666. {
  12667. // ONLY say that we support the stuff we support in ::OnExec
  12668. switch (rgCmds[i].cmdID)
  12669. {
  12670. case OLECMDID_REFRESH:
  12671. rgCmds[i].cmdf = OLECMDF_ENABLED;
  12672. break;
  12673. default:
  12674. // don't disable if the webview has already answered
  12675. if (!fQSCalled)
  12676. {
  12677. rgCmds[i].cmdf = 0;
  12678. }
  12679. break;
  12680. }
  12681. }
  12682. }
  12683. else if (IsEqualGUID(_clsid, *pguidCmdGroup))
  12684. {
  12685. if (pcmdtext)
  12686. {
  12687. switch (pcmdtext->cmdtextf)
  12688. {
  12689. case OLECMDTEXTF_NAME:
  12690. // It's a query for the button tooltip text.
  12691. ASSERT(cCmds == 1);
  12692. _GetToolTipText(rgCmds[0].cmdID, pcmdtext->rgwz, pcmdtext->cwBuf);
  12693. // ensure NULL termination
  12694. pcmdtext->rgwz[pcmdtext->cwBuf - 1] = 0;
  12695. pcmdtext->cwActual = lstrlenW(pcmdtext->rgwz);
  12696. hr = S_OK;
  12697. break;
  12698. default:
  12699. hr = E_FAIL;
  12700. break;
  12701. }
  12702. }
  12703. else
  12704. {
  12705. DWORD dwAttr = _AttributesFromSel(SFGAO_RELEVANT);
  12706. for (UINT i = 0; i < cCmds; i++)
  12707. {
  12708. if (_ShouldEnableToolbarButton(rgCmds[i].cmdID, dwAttr, -1))
  12709. rgCmds[i].cmdf = OLECMDF_ENABLED;
  12710. else
  12711. rgCmds[i].cmdf = 0;
  12712. }
  12713. hr = S_OK;
  12714. }
  12715. }
  12716. return hr;
  12717. }
  12718. STDMETHODIMP CDefView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  12719. {
  12720. HRESULT hr;
  12721. // Hold a ref to ourselves on Exec. In the camera name space if the view context menu is up when the camera
  12722. // is unplugged, explorer faults because the view is torn down and the context menu exec tries to unwind
  12723. // after defview is gone. This holds a ref on defview while in exec so defview doesn't dissappear.
  12724. //
  12725. AddRef();
  12726. hr = _Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  12727. Release();
  12728. return hr;
  12729. }
  12730. HRESULT CDefView::_Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  12731. {
  12732. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  12733. if (pguidCmdGroup == NULL)
  12734. {
  12735. switch (nCmdID)
  12736. {
  12737. case OLECMDID_REFRESH:
  12738. _fAllowSearchingWindow = TRUE; // this exec typically comes from a user action (F5, Refresh)
  12739. if (FAILED(_ReloadContent()))
  12740. {
  12741. //This invalidation deletes the WebView and also avoid
  12742. //unpainted areas in ListView areas whose paint messages
  12743. //are eaten by the visible WebView
  12744. InvalidateRect(_hwndView, NULL, TRUE);
  12745. }
  12746. hr = S_OK;
  12747. break;
  12748. }
  12749. }
  12750. else if (IsEqualGUID(CGID_DefView, *pguidCmdGroup))
  12751. {
  12752. switch (nCmdID)
  12753. {
  12754. case DVID_SETASDEFAULT:
  12755. // 99/02/05 #226140 vtan: Exec command issued from
  12756. // CShellBrowser2::_SaveDefViewDefaultFolderSettings()
  12757. // when user clicks "Like Current Folder" in folder
  12758. // options "View" tab.
  12759. ASSERTMSG(nCmdexecopt == OLECMDEXECOPT_DODEFAULT, "nCmdexecopt must be OLECMDEXECOPT_DODEFAULT");
  12760. ASSERTMSG(pvarargIn == NULL, "pvarargIn must be NULL");
  12761. ASSERTMSG(pvarargOut == NULL, "pvarargOut must be NULL");
  12762. hr = _SaveGlobalViewState();
  12763. break;
  12764. case DVID_RESETDEFAULT:
  12765. // 99/02/05 #226140 vtan: Exec command issued from
  12766. // CShellBrowser2::_ResetDefViewDefaultFolderSettings()
  12767. // when user clicks "Reset All Folders" in folder
  12768. // options "View" tab.
  12769. ASSERTMSG(nCmdexecopt == OLECMDEXECOPT_DODEFAULT, "nCmdexecopt must be OLECMDEXECOPT_DODEFAULT");
  12770. ASSERTMSG(pvarargIn == NULL, "pvarargIn must be NULL");
  12771. ASSERTMSG(pvarargOut == NULL, "pvarargOut must be NULL");
  12772. hr = _ResetGlobalViewState();
  12773. break;
  12774. default:
  12775. break;
  12776. }
  12777. }
  12778. else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
  12779. {
  12780. switch (nCmdID)
  12781. {
  12782. case SHDVID_CANACTIVATENOW:
  12783. return _fCanActivateNow ? S_OK : S_FALSE;
  12784. // NOTE: for a long time IOleCommandTarget was implemented
  12785. // BUT it wasn't in the QI! At this late stage of the game
  12786. // I'll be paranoid and not forward everything down to the
  12787. // webview. We'll just pick off CANACTIVATENOW...
  12788. //
  12789. default:
  12790. return OLECMDERR_E_UNKNOWNGROUP;
  12791. }
  12792. }
  12793. else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
  12794. {
  12795. switch (nCmdID)
  12796. {
  12797. case SBCMDID_GETPANE:
  12798. V_I4(pvarargOut) = PANE_NONE;
  12799. CallCB(SFVM_GETPANE, nCmdexecopt, (LPARAM)&V_I4(pvarargOut));
  12800. return S_OK;
  12801. case SBCMDID_MIXEDZONE:
  12802. if (pvarargOut)
  12803. return _cFrame._GetCurrentZone(NULL, pvarargOut);
  12804. break;
  12805. default:
  12806. break;
  12807. }
  12808. }
  12809. else if (IsEqualGUID(IID_IExplorerToolbar, *pguidCmdGroup))
  12810. {
  12811. // handle the ones coming FROM itbar:
  12812. switch (nCmdID)
  12813. {
  12814. case ETCMDID_GETBUTTONS:
  12815. pvarargOut->vt = VT_BYREF;
  12816. pvarargOut->byref = (void *)_pbtn;
  12817. *pvarargIn->plVal = _cTotalButtons;
  12818. return S_OK;
  12819. case ETCMDID_RELOADBUTTONS:
  12820. MergeToolBar(TRUE);
  12821. return S_OK;
  12822. }
  12823. }
  12824. else if (IsEqualGUID(_clsid, *pguidCmdGroup))
  12825. {
  12826. UEMFireEvent(&UEMIID_BROWSER, UEME_UITOOLBAR, UEMF_XEVENT, UIG_OTHER, nCmdID);
  12827. DFVCMDDATA cd;
  12828. cd.pva = pvarargIn;
  12829. cd.hwnd = _hwndMain;
  12830. cd.nCmdIDTranslated = 0;
  12831. _OnCommand(NULL, nCmdID, (LPARAM)&cd);
  12832. }
  12833. // no need to pass OLECMDID_REFRESH on to the webview, as we
  12834. // just nuked and replaced the webview above -- a super refresh of sorts.
  12835. if (_cFrame.IsWebView() && hr != S_OK)
  12836. {
  12837. // Do not pass IDM_PARSECOMPLETE back to MSHTML. This will cause them to load mshtmled.dll
  12838. // unecessarily for webview which is a significant performance hit.
  12839. if (!(pguidCmdGroup && IsEqualGUID(CGID_MSHTML, *pguidCmdGroup) && (nCmdID == IDM_PARSECOMPLETE)))
  12840. {
  12841. IOleCommandTarget* pct;
  12842. if (SUCCEEDED(_cFrame.GetCommandTarget(&pct)))
  12843. {
  12844. hr = pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  12845. pct->Release();
  12846. }
  12847. }
  12848. }
  12849. return hr;
  12850. }
  12851. void CDefView::_ShowAndActivate()
  12852. {
  12853. // Can't call SetFocus because it rips focus away from such nice
  12854. // UI elements like the TREE pane...
  12855. // UIActivate will steal focus only if _uState is SVUIA_ACTIVATE_FOCUS
  12856. UIActivate(_uState);
  12857. }
  12858. // IDefViewFrame (available only through QueryService from sfvext!)
  12859. //
  12860. HRESULT CDefView::GetShellFolder(IShellFolder **ppsf)
  12861. {
  12862. *ppsf = _pshf;
  12863. if (*ppsf)
  12864. _pshf->AddRef();
  12865. return *ppsf ? S_OK : E_FAIL;
  12866. }
  12867. // IDefViewFrame3
  12868. //
  12869. HRESULT CDefView::GetWindowLV(HWND * phwnd)
  12870. {
  12871. if (!_IsDesktop())
  12872. {
  12873. if (!_fGetWindowLV)
  12874. {
  12875. _fGetWindowLV = TRUE;
  12876. // Caller will call ShowHideListView for us
  12877. *phwnd = _hwndListview;
  12878. }
  12879. TraceMsg(TF_DEFVIEW, "GetWindowLV - TAKEN");
  12880. return S_OK;
  12881. }
  12882. else
  12883. {
  12884. *phwnd = NULL;
  12885. return E_FAIL;
  12886. }
  12887. }
  12888. HRESULT CDefView::OnResizeListView()
  12889. {
  12890. _AutoAutoArrange(0);
  12891. return S_OK;
  12892. }
  12893. HRESULT CDefView::ReleaseWindowLV()
  12894. {
  12895. _fGetWindowLV = FALSE;
  12896. WndSize(_hwndView); // Make sure we resize _hwndListview
  12897. ShowHideListView();
  12898. return S_OK;
  12899. }
  12900. HRESULT CDefView::DoRename()
  12901. {
  12902. return HandleRename(NULL);
  12903. }
  12904. // IServiceProvider
  12905. STDMETHODIMP CDefView::QueryService(REFGUID guidService, REFIID riid, void **ppv)
  12906. {
  12907. HRESULT hr = E_FAIL;
  12908. *ppv = NULL;
  12909. if (guidService == SID_DefView) // private service ID
  12910. {
  12911. // DefViewOCs request this interface
  12912. if (riid != IID_IDefViewFrame || !_IsDesktop())
  12913. hr = QueryInterface(riid, ppv);
  12914. }
  12915. else if (guidService == SID_ShellTaskScheduler)
  12916. {
  12917. if (_pScheduler)
  12918. {
  12919. hr = _pScheduler->QueryInterface(riid, ppv);
  12920. }
  12921. }
  12922. else if ((guidService == SID_SContextMenuSite) ||
  12923. (guidService == SID_SFolderView)) // documented service ID
  12924. {
  12925. hr = QueryInterface(riid, ppv);
  12926. }
  12927. else if (guidService == SID_ShellFolderViewCB) // access to the view callback object
  12928. {
  12929. IShellFolderViewCB * psfvcb = _cCallback.GetSFVCB();
  12930. if (psfvcb)
  12931. hr = psfvcb->QueryInterface(riid, ppv);
  12932. }
  12933. else if (guidService == SID_WebViewObject)
  12934. {
  12935. if (_cFrame.IsWebView())
  12936. {
  12937. if (_cFrame._pOleObj)
  12938. {
  12939. //
  12940. // We hit this codepath while navigating away (while saving history),
  12941. // so there should not be any pending _cFrame._pOleObjNew as this
  12942. // view is going to be destroyed.
  12943. //
  12944. ASSERTMSG(!_cFrame._pOleObjNew, "Ambiguous Oleobj while peristing trident history in webview");
  12945. hr = _cFrame._pOleObj->QueryInterface(riid, ppv);
  12946. }
  12947. else if (_cFrame._pOleObjNew)
  12948. {
  12949. //
  12950. // We hit this codepath if we are navigating to the view (while loading history),
  12951. // we have not yet called _cFrame._SwitchToNewOleObj(), so we'll use
  12952. // the pending oleobj as CDefViewPersistHistory::LoadHistory()
  12953. // expects to get the right IPersistHistory interface from it.
  12954. //
  12955. hr = _cFrame._pOleObjNew->QueryInterface(riid, ppv);
  12956. }
  12957. }
  12958. }
  12959. else if (guidService == SID_SProgressUI)
  12960. {
  12961. // return a new instance of the progress dialog to the caller
  12962. hr = CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, riid, ppv);
  12963. }
  12964. else if (_psb)
  12965. {
  12966. hr = IUnknown_QueryService(_psb, guidService, riid, ppv); // send up the to the browser
  12967. }
  12968. else
  12969. {
  12970. hr = IUnknown_QueryService(_punkSite, guidService, riid, ppv); // or our site
  12971. }
  12972. return hr;
  12973. }
  12974. STDMETHODIMP CDefView::OnSetTitle(VARIANTARG *pvTitle)
  12975. {
  12976. return E_NOTIMPL;
  12977. }
  12978. BOOL CDefView::_LoadCategory(GUID *pguidGroupID)
  12979. {
  12980. BOOL fRet = FALSE;
  12981. LPITEMIDLIST pidl = _GetViewPidl();
  12982. if (pidl)
  12983. {
  12984. IPropertyBag *ppb;
  12985. if (SUCCEEDED(IUnknown_QueryServicePropertyBag(_psb, SHGVSPB_FOLDER, IID_PPV_ARG(IPropertyBag, &ppb))))
  12986. {
  12987. fRet = SUCCEEDED(SHPropertyBag_ReadGUID(ppb, TEXT("Categorize"), pguidGroupID));
  12988. ppb->Release();
  12989. }
  12990. ILFree(pidl);
  12991. }
  12992. return fRet;
  12993. }
  12994. void SHGetThumbnailSize(SIZE *psize)
  12995. {
  12996. psize->cx = psize->cy = 96;
  12997. DWORD dw = 0, cb = sizeof(dw);
  12998. SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
  12999. TEXT("ThumbnailSize"), NULL, &dw, &cb, FALSE, NULL, 0);
  13000. if (dw >= 32 && dw <= 256) // constrain to reason
  13001. {
  13002. psize->cx = psize->cy = (int)dw;
  13003. }
  13004. }
  13005. void SHGetThumbnailSizeForThumbsDB(SIZE *psize)
  13006. {
  13007. SHGetThumbnailSize(psize);
  13008. // Due to tnail.cpp restriction buffer sizes, we can only go to 120 (since that's all we've tested at)
  13009. if (psize->cx > 120)
  13010. psize->cx = psize->cy = 120;
  13011. }
  13012. void CDefView::_GetThumbnailSize(SIZE *psize)
  13013. {
  13014. if (-1 == _sizeThumbnail.cx)
  13015. {
  13016. SHGetThumbnailSize(&_sizeThumbnail);
  13017. }
  13018. *psize = _sizeThumbnail;
  13019. }
  13020. #ifdef _X86_
  13021. //************
  13022. //
  13023. // More of the Hijaak Hack
  13024. //
  13025. // We return no attributes (specifically, Hijaak looks for
  13026. // SFGAO_FILESYSTEM) and Hijaak will say, "Whoa, I don't know
  13027. // how to patch this guy; I'll leave it alone."
  13028. STDAPI FakeHijaak_GetAttributesOf(void *_this, UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  13029. {
  13030. *rgfInOut = 0; // Move along, nothing to see here
  13031. return S_OK;
  13032. }
  13033. const struct FakeHijaakFolderVtbl
  13034. {
  13035. FARPROC Dummy[9];
  13036. FARPROC GetAttributesOf;
  13037. } c_FakeHijaakFolderVtbl = { { 0 }, (FARPROC)FakeHijaak_GetAttributesOf };
  13038. const LPVOID c_FakeHijaakFolder = (const LPVOID)&c_FakeHijaakFolderVtbl;
  13039. //
  13040. // End of the Hijaak Hack
  13041. //
  13042. //************
  13043. #endif // _X86_
  13044. CBackgroundDefviewInfo::CBackgroundDefviewInfo(LPCITEMIDLIST pidl, UINT uId) :
  13045. _pidl(pidl), _uId(uId)
  13046. {
  13047. }
  13048. CBackgroundDefviewInfo::~CBackgroundDefviewInfo (void)
  13049. {
  13050. ILFree(const_cast<LPITEMIDLIST>(_pidl));
  13051. }
  13052. CBackgroundColInfo::CBackgroundColInfo(LPCITEMIDLIST pidl, UINT uId, UINT uiCol, STRRET& strRet) :
  13053. CBackgroundDefviewInfo(pidl, uId),
  13054. _uiCol(uiCol)
  13055. {
  13056. StrRetToBuf(&strRet, NULL, const_cast<TCHAR*>(_szText), ARRAYSIZE(_szText));
  13057. }
  13058. CBackgroundColInfo::~CBackgroundColInfo(void)
  13059. {
  13060. }
  13061. // Takes ownership of pidl, copies rguColumns.
  13062. CBackgroundTileInfo::CBackgroundTileInfo(LPCITEMIDLIST pidl, UINT uId, UINT rguColumns[], UINT cColumns) :
  13063. CBackgroundDefviewInfo(pidl, uId),
  13064. _cColumns(cColumns)
  13065. {
  13066. ASSERT(cColumns <= ARRAYSIZE(_rguColumns));
  13067. for (UINT i = 0; (i < cColumns) && (i < ARRAYSIZE(_rguColumns)); i++)
  13068. _rguColumns[i] = rguColumns[i];
  13069. }
  13070. CBackgroundTileInfo::~CBackgroundTileInfo(void)
  13071. {
  13072. }
  13073. // Helper function that scales the given size by some percentage where the percentage
  13074. // is defined in the resources for the localizers to adjust as approp. Range is 0 to 30% larger
  13075. INT ScaleSizeBasedUponLocalization (INT iSize)
  13076. {
  13077. TCHAR szPercentageIncrease[3];
  13078. INT iReturnValue = iSize;
  13079. INT iPercentageIncrease;
  13080. if (iSize > 0)
  13081. {
  13082. if (LoadString(HINST_THISDLL, IDS_SIZE_INCREASE_PERCENTAGE, szPercentageIncrease, ARRAYSIZE(szPercentageIncrease)))
  13083. {
  13084. iPercentageIncrease = StrToInt(szPercentageIncrease);
  13085. if (iPercentageIncrease > 0)
  13086. {
  13087. if (iPercentageIncrease > 30)
  13088. {
  13089. iPercentageIncrease = 30;
  13090. }
  13091. iReturnValue += ((iPercentageIncrease * iSize) / 100);
  13092. }
  13093. }
  13094. }
  13095. return iReturnValue;
  13096. }
  13097. CBackgroundGroupInfo::CBackgroundGroupInfo (LPCITEMIDLIST pidl, UINT uId, DWORD dwGroupId):
  13098. CBackgroundDefviewInfo(pidl, uId), _dwGroupId(dwGroupId)
  13099. {
  13100. }
  13101. BOOL CBackgroundGroupInfo::VerifyGroupExists(HWND hwnd, ICategorizer* pcat)
  13102. {
  13103. if (!pcat)
  13104. return FALSE;
  13105. if (!ListView_HasGroup(hwnd, _dwGroupId))
  13106. {
  13107. CATEGORY_INFO ci;
  13108. pcat->GetCategoryInfo(_dwGroupId, &ci);
  13109. LVINSERTGROUPSORTED igrp;
  13110. igrp.pfnGroupCompare = GroupCompare;
  13111. igrp.pvData = (void *)pcat;
  13112. igrp.lvGroup.cbSize = sizeof(LVGROUP);
  13113. igrp.lvGroup.mask = LVGF_HEADER | LVGF_GROUPID;
  13114. igrp.lvGroup.pszHeader= ci.wszName;
  13115. igrp.lvGroup.iGroupId = (int)_dwGroupId;
  13116. ListView_InsertGroupSorted(hwnd, &igrp);
  13117. }
  13118. return TRUE;
  13119. }