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

862 lines
28 KiB

  1. #include "precomp.hxx"
  2. #pragma hdrstop
  3. #include "util.h"
  4. #include "dll.h"
  5. #include "resource.h"
  6. #include "prop.h"
  7. #include <shellids.h> // IDH_ values
  8. #include "shlguidp.h"
  9. #include "inetreg.h"
  10. typedef struct {
  11. HWND hDlg;
  12. BOOL bDirty;
  13. BOOL bInitDone;
  14. BOOL bSetToDefault;
  15. TCHAR szFolder[MAX_PATH];
  16. UINT csidl;
  17. } CUSTINFO;
  18. const static DWORD rgdwHelpTarget[] = {
  19. IDD_TARGET_TXT, IDH_MYDOCS_TARGET,
  20. IDD_TARGET, IDH_MYDOCS_TARGET,
  21. IDD_FIND, IDH_MYDOCS_FIND_TARGET,
  22. IDD_BROWSE, IDH_MYDOCS_BROWSE,
  23. IDD_RESET, IDH_MYDOCS_RESET,
  24. 0, 0
  25. };
  26. // Scans a desktop.ini file for sections to see if all of them are empty...
  27. BOOL IsDesktopIniEmpty(LPCTSTR pIniFile)
  28. {
  29. TCHAR szSections[1024]; // for section names
  30. if (GetPrivateProfileSectionNames(szSections, ARRAYSIZE(szSections), pIniFile))
  31. {
  32. for (LPTSTR pTmp = szSections; *pTmp; pTmp += lstrlen(pTmp) + 1)
  33. {
  34. TCHAR szSection[1024]; // for section key names and values
  35. GetPrivateProfileSection(pTmp, szSection, ARRAYSIZE(szSection), pIniFile);
  36. if (szSection[0])
  37. {
  38. return FALSE;
  39. }
  40. }
  41. }
  42. return TRUE;
  43. }
  44. void CleanupSystemFolder(LPCTSTR pszPath)
  45. {
  46. TCHAR szIniFile[MAX_PATH];
  47. PathCombine(szIniFile, pszPath, TEXT("desktop.ini"));
  48. DWORD dwAttrb;
  49. if (PathFileExistsAndAttributes(szIniFile, &dwAttrb))
  50. {
  51. // Remove CLSID2, InfoTip, Icon
  52. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), NULL, szIniFile);
  53. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("InfoTip"), NULL, szIniFile);
  54. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconFile"), NULL, szIniFile);
  55. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconIndex"), NULL, szIniFile);
  56. // get rid of delete on copy entries to see if we can generate an empty .ini file
  57. WritePrivateProfileSection(TEXT("DeleteOnCopy"), NULL, szIniFile);
  58. if (IsDesktopIniEmpty(szIniFile))
  59. {
  60. dwAttrb &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
  61. SetFileAttributes(szIniFile, dwAttrb);
  62. DeleteFile(szIniFile);
  63. }
  64. // see if we can cleanout an old thumbs.db file too
  65. // so we have a better chance of deleting an empty folder
  66. PathCombine(szIniFile, pszPath, TEXT("thumbs.db"));
  67. DeleteFile(szIniFile);
  68. PathUnmakeSystemFolder(pszPath);
  69. }
  70. // in case it is empty try to delete it
  71. // this will fail if there are contents in the folder
  72. if (RemoveDirectory(pszPath))
  73. {
  74. // it is gone, let people know
  75. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pszPath, NULL);
  76. }
  77. else
  78. {
  79. // attribute bits changed for this folder, refresh views of it
  80. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, pszPath, NULL);
  81. }
  82. }
  83. HRESULT ChangeFolderPath(UINT csidl, LPCTSTR pszNew, LPCTSTR pszOld)
  84. {
  85. HRESULT hr = SHSetFolderPath(csidl, NULL, 0, pszNew);
  86. if (SUCCEEDED(hr))
  87. {
  88. // now we can cleanup the old folder... now that we have the new folder
  89. // established
  90. if (*pszOld)
  91. {
  92. CleanupSystemFolder(pszOld);
  93. }
  94. // force the per user init stuff on the new folder
  95. TCHAR szPath[MAX_PATH];
  96. hr = SHGetFolderPath(NULL, csidl | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, szPath);
  97. if (SUCCEEDED(hr))
  98. {
  99. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, szPath, NULL);
  100. }
  101. }
  102. return hr;
  103. }
  104. // test to see if pszToTest is a sub folder of pszFolder
  105. BOOL PathIsDirectChildOf(LPCTSTR pszFolder, LPCTSTR pszMaybeChild)
  106. {
  107. return PATH_IS_CHILD == ComparePaths(pszMaybeChild, pszFolder);
  108. }
  109. LPTSTR GetMessageTitle(CUSTINFO *pci, LPTSTR psz, UINT cch)
  110. {
  111. TCHAR szFormat[64], szName[MAX_PATH];
  112. LoadString(g_hInstance, IDS_PROP_ERROR_TITLE, szFormat, ARRAYSIZE(szFormat));
  113. GetFolderDisplayName(pci->csidl, szName, ARRAYSIZE(szName));
  114. wnsprintf(psz, cch, szFormat, szName);
  115. return psz;
  116. }
  117. void GetTargetExpandedPath(HWND hDlg, LPTSTR pszPath, UINT cch)
  118. {
  119. *pszPath = 0;
  120. TCHAR szUnExPath[MAX_PATH];
  121. if (GetDlgItemText(hDlg, IDD_TARGET, szUnExPath, ARRAYSIZE(szUnExPath)))
  122. {
  123. // Turn "c:" into "c:\", but don't change other paths:
  124. PathAddBackslash(szUnExPath);
  125. PathRemoveBackslash(szUnExPath);
  126. SHExpandEnvironmentStrings(szUnExPath, pszPath, cch);
  127. }
  128. }
  129. // Check known key in the registry to see if policy has disabled changing
  130. // of My Docs location.
  131. BOOL PolicyAllowsFolderPathChange(CUSTINFO *pci)
  132. {
  133. BOOL bChange = TRUE;
  134. if (pci->csidl == CSIDL_PERSONAL)
  135. {
  136. HKEY hkey;
  137. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), 0, KEY_READ, &hkey))
  138. {
  139. bChange = (ERROR_SUCCESS != RegQueryValueEx(hkey, TEXT("DisablePersonalDirChange"), NULL, NULL, NULL, NULL));
  140. RegCloseKey(hkey);
  141. }
  142. }
  143. return bChange;
  144. }
  145. BOOL InitTargetPage(HWND hDlg, LPARAM lParam)
  146. {
  147. CUSTINFO *pci = (CUSTINFO *)LocalAlloc(LPTR, sizeof(*pci));
  148. if (pci)
  149. {
  150. TCHAR szPath[MAX_PATH];
  151. TCHAR szFormat[MAX_PATH];
  152. TCHAR szText[ARRAYSIZE(szFormat) + MAX_NAME_LEN];
  153. TCHAR szName[MAX_PATH];
  154. SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)pci);
  155. pci->hDlg = hDlg;
  156. pci->csidl = CSIDL_PERSONAL;
  157. // Fill in title/instructions...
  158. GetFolderDisplayName(pci->csidl, szName, ARRAYSIZE(szName));
  159. if (lstrlen(szName) > MAX_NAME_LEN)
  160. {
  161. lstrcpy(&szName[MAX_NAME_LEN], TEXT("..."));
  162. }
  163. LoadString(g_hInstance, IDS_PROP_INSTRUCTIONS, szFormat, ARRAYSIZE(szFormat));
  164. wnsprintf(szText, ARRAYSIZE(szText), szFormat, szName);
  165. SetDlgItemText(hDlg, IDD_INSTRUCTIONS, szText);
  166. // Limit edit field to MAX_PATH-13 characters. Why -13?
  167. // Well, 13 is the number of characters in a DOS style 8.3
  168. // filename with a '\', and CreateDirectory will fail if you try to create
  169. // a directory that can't at least contain 8.3 file names.
  170. SendDlgItemMessage(hDlg, IDD_TARGET, EM_SETLIMITTEXT, MAX_DIR_PATH, 0);
  171. // Check whether path can be changed
  172. if (PolicyAllowsFolderPathChange(pci))
  173. {
  174. SHAutoComplete(GetDlgItem(hDlg, IDD_TARGET), SHACF_FILESYS_DIRS);
  175. }
  176. else
  177. {
  178. // Make edit field read only
  179. SendDlgItemMessage(hDlg, IDD_TARGET, EM_SETREADONLY, (WPARAM)TRUE, 0);
  180. ShowWindow(GetDlgItem(hDlg, IDD_RESET), SW_HIDE);
  181. ShowWindow(GetDlgItem(hDlg, IDD_FIND), SW_HIDE);
  182. ShowWindow(GetDlgItem(hDlg, IDD_BROWSE), SW_HIDE);
  183. }
  184. SHGetFolderPath(NULL, pci->csidl | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath);
  185. if (szPath[0])
  186. {
  187. PathRemoveBackslash(szPath); // keep path without trailing backslash
  188. lstrcpy(pci->szFolder, szPath);
  189. SetDlgItemText(hDlg, IDD_TARGET, szPath);
  190. }
  191. LPITEMIDLIST pidl;
  192. if (SUCCEEDED(SHGetFolderLocation(NULL, pci->csidl | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, &pidl)))
  193. {
  194. SHFILEINFO sfi;
  195. SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_PIDL);
  196. if (sfi.hIcon)
  197. {
  198. if (sfi.hIcon = (HICON)SendDlgItemMessage(hDlg, IDD_ITEMICON, STM_SETICON, (WPARAM)sfi.hIcon, 0))
  199. DestroyIcon(sfi.hIcon);
  200. }
  201. ILFree(pidl);
  202. }
  203. pci->bInitDone = TRUE;
  204. }
  205. return pci ? TRUE : FALSE;
  206. }
  207. const UINT c_rgRedirectCanidates[] =
  208. {
  209. CSIDL_MYPICTURES,
  210. CSIDL_MYMUSIC,
  211. CSIDL_MYVIDEO,
  212. CSIDL_MYDOCUMENTS,
  213. };
  214. int MoveFilesForRedirect(HWND hdlg, LPCTSTR pszNewPath, LPCTSTR pszOldPath)
  215. {
  216. int iRet = 0; // success
  217. // since we use FOF_RENAMEONCOLLISION when moving files from the old location
  218. // to the new we want to special case target folders if they are the shell special
  219. // folders that may live under the folder being redirected
  220. // this code implements a merge of those folders doing "rename on collision" at the
  221. // level below the folder. this keeps us from generating "copy of xxx" for each of
  222. // the special folders
  223. for (UINT i = 0; (iRet == 0) && (i < ARRAYSIZE(c_rgRedirectCanidates)); i++)
  224. {
  225. TCHAR szOld[MAX_PATH];
  226. if (SUCCEEDED(SHGetFolderPath(NULL, c_rgRedirectCanidates[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szOld)) &&
  227. PathIsDirectChildOf(pszOldPath, szOld))
  228. {
  229. TCHAR szDestPath[MAX_PATH] = {0}; // zero init for SHFileOperation()
  230. PathCombine(szDestPath, pszNewPath, PathFindFileName(szOld));
  231. DWORD dwAtt;
  232. if (PathFileExistsAndAttributes(szDestPath, &dwAtt) &&
  233. (FILE_ATTRIBUTE_DIRECTORY & dwAtt))
  234. {
  235. // reset the folder with the system before the move
  236. ChangeFolderPath(c_rgRedirectCanidates[i], szDestPath, szOld);
  237. // the above may have emptied and deleted the old location
  238. // but if not we need to move the contents
  239. if (PathFileExistsAndAttributes(szOld, &dwAtt))
  240. {
  241. // Move items in current MyPics to new location
  242. TCHAR szSrcPath[MAX_PATH + 1] = {0}; // +1 for double null
  243. PathCombine(szSrcPath, szOld, TEXT("*.*"));
  244. SHFILEOPSTRUCT fo = {0};
  245. fo.hwnd = hdlg;
  246. fo.wFunc = FO_MOVE;
  247. fo.fFlags = FOF_RENAMEONCOLLISION;
  248. fo.pFrom = szSrcPath;
  249. fo.pTo = szDestPath;
  250. iRet = SHFileOperation(&fo);
  251. if ((0 == iRet) && !fo.fAnyOperationsAborted)
  252. {
  253. // since the above was a full move no files should
  254. // be left behind so this should work
  255. if (RemoveDirectory(szOld))
  256. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, szOld, NULL);
  257. }
  258. }
  259. }
  260. }
  261. }
  262. // above failed or canceled?
  263. if (0 == iRet)
  264. {
  265. // move the rest of the stuff
  266. TCHAR szSrcPath[MAX_PATH + 1] = {0}; // +1 for double null
  267. PathCombine(szSrcPath, pszOldPath, TEXT("*.*"));
  268. TCHAR szDestPath[MAX_PATH] = {0}; // zero init for dbl null term
  269. lstrcpy(szDestPath, pszNewPath);
  270. SHFILEOPSTRUCT fo = {0};
  271. fo.hwnd = hdlg;
  272. fo.wFunc = FO_MOVE;
  273. fo.fFlags = FOF_RENAMEONCOLLISION; // don't want any "replace file" prompts
  274. fo.pFrom = szSrcPath;
  275. fo.pTo = szDestPath;
  276. iRet = SHFileOperation(&fo);
  277. if (0 == iRet)
  278. {
  279. // if the above worked we try to clean up the old path
  280. // now that it it empty
  281. if (RemoveDirectory(pszOldPath))
  282. SHChangeNotify(SHCNE_RMDIR, SHCNF_PATH, pszOldPath, NULL);
  283. }
  284. }
  285. return iRet;
  286. }
  287. // Ask the user if they want to create the directory of a given path.
  288. // Returns TRUE if the user decided to create it, FALSE if not.
  289. // If TRUE, the dir attributes are returned in pdwAttr.
  290. BOOL QueryCreateTheDirectory(CUSTINFO *pci, LPCTSTR pPath, DWORD *pdwAttr)
  291. {
  292. *pdwAttr = 0;
  293. UINT id = IDYES;
  294. if (pci->bSetToDefault)
  295. id = IDYES;
  296. else
  297. id = ShellMessageBox(g_hInstance, pci->hDlg, MAKEINTRESOURCE(IDS_CREATE_FOLDER), MAKEINTRESOURCE(IDS_CREATE_FOLDER_TITLE),
  298. MB_YESNO | MB_ICONQUESTION, pPath);
  299. if (IDYES == id)
  300. {
  301. // user asked us to create the folder
  302. if (ERROR_SUCCESS == SHCreateDirectoryEx(pci->hDlg, pPath, NULL))
  303. *pdwAttr = GetFileAttributes(pPath);
  304. }
  305. return IDYES == id;
  306. }
  307. void _MaybeUnpinOldFolder(LPCTSTR pszPath, HWND hwnd, BOOL fPromptUnPin)
  308. {
  309. //
  310. // Convert the path to canonical UNC form (the CSC and CSCUI
  311. // functions require the path to be in this form)
  312. //
  313. // WNetGetUniversalName fails if you give it a path that's already
  314. // in canonical UNC form, so in the failure case just try using
  315. // pszPath. CSCQueryFileStatus will validate it.
  316. //
  317. LPCTSTR pszUNC;
  318. struct {
  319. UNIVERSAL_NAME_INFO uni;
  320. TCHAR szBuf[MAX_PATH];
  321. } s;
  322. DWORD cbBuf = sizeof(s);
  323. if (ERROR_SUCCESS == WNetGetUniversalName(pszPath, UNIVERSAL_NAME_INFO_LEVEL,
  324. &s, &cbBuf))
  325. {
  326. pszUNC = s.uni.lpUniversalName;
  327. }
  328. else
  329. {
  330. pszUNC = pszPath;
  331. }
  332. //
  333. // Ask CSC if the folder is pinned for this user
  334. //
  335. DWORD dwHintFlags = 0;
  336. if (CSCQueryFileStatus(pszUNC, NULL, NULL, &dwHintFlags))
  337. {
  338. if (dwHintFlags & FLAG_CSC_HINT_PIN_USER)
  339. {
  340. //
  341. // Yes; figure out if we should unpin it
  342. //
  343. BOOL fUnpin;
  344. if (fPromptUnPin)
  345. {
  346. //
  347. // Give the unconverted path name in the message box, since
  348. // that's the name the user knows
  349. //
  350. UINT id = ShellMessageBox(g_hInstance, hwnd,
  351. MAKEINTRESOURCE(IDS_UNPIN_OLDTARGET), MAKEINTRESOURCE(IDS_UNPIN_OLD_TITLE),
  352. MB_YESNO | MB_ICONQUESTION | MB_TOPMOST | MB_DEFBUTTON2,
  353. pszPath);
  354. fUnpin = (id == IDNO);
  355. }
  356. else
  357. {
  358. fUnpin = TRUE;
  359. }
  360. if (fUnpin)
  361. {
  362. CSCUIRemoveFolderFromCache(pszUNC, 0, NULL, 0);
  363. }
  364. }
  365. }
  366. }
  367. void ComputChildrenOf(LPCTSTR pszOld, UINT rgChildren[], UINT sizeArray)
  368. {
  369. UINT iCanidate = 0;
  370. ZeroMemory(rgChildren, sizeof(rgChildren[0]) * sizeArray);
  371. for (UINT i = 0; i < ARRAYSIZE(c_rgRedirectCanidates); i++)
  372. {
  373. TCHAR szPath[MAX_PATH];
  374. if (SUCCEEDED(SHGetFolderPath(NULL, c_rgRedirectCanidates[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath)))
  375. {
  376. if (PathIsDirectChildOf(pszOld, szPath))
  377. {
  378. if (iCanidate < sizeArray)
  379. {
  380. rgChildren[iCanidate++] = c_rgRedirectCanidates[i];
  381. }
  382. }
  383. }
  384. }
  385. }
  386. // if csidl DEFAULT VALUE ends up under the new folder we reset that folder
  387. // to that value
  388. HRESULT ResetSubFolderDefault(LPCTSTR pszNew, UINT csidl, LPCTSTR pszOldPath)
  389. {
  390. HRESULT hr = S_OK;
  391. // note: getting the default value for this path, not the current value!
  392. TCHAR szDefault[MAX_PATH];
  393. if (S_OK == SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_DEFAULT, szDefault))
  394. {
  395. if (PathIsDirectChildOf(pszNew, szDefault))
  396. {
  397. hr = SHSetFolderPath(csidl, NULL, 0, szDefault);
  398. if (SUCCEEDED(hr))
  399. {
  400. // we've written the registry, that is enough to cleanup the old folder
  401. if (*pszOldPath)
  402. CleanupSystemFolder(pszOldPath);
  403. hr = SHGetFolderPath(NULL, csidl | CSIDL_FLAG_CREATE | CSIDL_FLAG_PER_USER_INIT, NULL, SHGFP_TYPE_CURRENT, szDefault);
  404. }
  405. }
  406. }
  407. return hr;
  408. }
  409. void ResetNonMovedFolders(LPCTSTR pszNew, UINT rgChildren[], UINT sizeArray)
  410. {
  411. for (UINT i = 0; i < sizeArray; i++)
  412. {
  413. // for all of these folders that were sub folders of the old location
  414. // and are now not sub folders we try to restore them to the default
  415. TCHAR szPath[MAX_PATH];
  416. if (rgChildren[i] &&
  417. SUCCEEDED(SHGetFolderPath(NULL, rgChildren[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath)) &&
  418. !PathIsDirectChildOf(pszNew, szPath))
  419. {
  420. ResetSubFolderDefault(pszNew, rgChildren[i], szPath);
  421. }
  422. }
  423. }
  424. void _DoApply(CUSTINFO *pci)
  425. {
  426. LONG lres = PSNRET_NOERROR;
  427. TCHAR szNewFolder[MAX_PATH];
  428. DWORD dwAttr;
  429. GetTargetExpandedPath(pci->hDlg, szNewFolder, ARRAYSIZE(szNewFolder));
  430. if (pci->bDirty && (lstrcmpi(szNewFolder, pci->szFolder) != 0))
  431. {
  432. TCHAR szPropTitle[MAX_PATH + 32];
  433. DWORD dwRes = IsPathGoodMyDocsPath(pci->hDlg, szNewFolder);
  434. // all of the special cases
  435. switch (dwRes)
  436. {
  437. case PATH_IS_DESKTOP: // desktop is not good
  438. ShellMessageBox(g_hInstance, pci->hDlg,
  439. MAKEINTRESOURCE(IDS_NODESKTOP_FOLDERS), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  440. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  441. lres = PSNRET_INVALID_NOCHANGEPAGE;
  442. break;
  443. case PATH_IS_SYSTEM:
  444. case PATH_IS_WINDOWS: // these would be bad
  445. ShellMessageBox(g_hInstance, pci->hDlg,
  446. MAKEINTRESOURCE(IDS_NOWINDIR_FOLDER), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  447. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  448. lres = PSNRET_INVALID_NOCHANGEPAGE;
  449. break;
  450. case PATH_IS_PROFILE: // profile is bad
  451. ShellMessageBox(g_hInstance, pci->hDlg,
  452. MAKEINTRESOURCE(IDS_NOPROFILEDIR_FOLDER), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  453. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  454. lres = PSNRET_INVALID_NOCHANGEPAGE;
  455. break;
  456. case PATH_IS_NONEXISTENT:
  457. case PATH_IS_NONDIR:
  458. case PATH_IS_GOOD:
  459. dwAttr = GetFileAttributes(szNewFolder);
  460. if (dwAttr == 0xFFFFFFFF)
  461. {
  462. // Ask user if we should create the directory...
  463. if (!QueryCreateTheDirectory(pci, szNewFolder, &dwAttr))
  464. {
  465. // They don't want to create the directory.. break here
  466. lres = PSNRET_INVALID_NOCHANGEPAGE;
  467. break;
  468. }
  469. }
  470. if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
  471. {
  472. if (lstrcmpi(szNewFolder, pci->szFolder))
  473. {
  474. UINT rgChildren[10];
  475. ComputChildrenOf(pci->szFolder, rgChildren, ARRAYSIZE(rgChildren));
  476. if (SUCCEEDED(ChangeFolderPath(pci->csidl, szNewFolder, pci->szFolder)))
  477. {
  478. BOOL fNewSubdirOfOld = PathIsEqualOrSubFolder(pci->szFolder, szNewFolder);
  479. BOOL fPromptUnPin = TRUE;
  480. if (fNewSubdirOfOld)
  481. {
  482. // can't move old content to a subdir
  483. ShellMessageBox(g_hInstance, pci->hDlg,
  484. MAKEINTRESOURCE(IDS_CANT_MOVE_TO_SUBDIR), MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS_TITLE),
  485. MB_OK | MB_ICONINFORMATION | MB_TOPMOST,
  486. pci->szFolder);
  487. }
  488. else if (IDYES == ShellMessageBox(g_hInstance, pci->hDlg,
  489. MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS),
  490. MAKEINTRESOURCE(IDS_MOVE_DOCUMENTS_TITLE),
  491. MB_YESNO | MB_ICONQUESTION | MB_TOPMOST,
  492. pci->szFolder, szNewFolder))
  493. {
  494. // move old mydocs content -- returns 0 on success
  495. if (0 == MoveFilesForRedirect(pci->hDlg, szNewFolder, pci->szFolder))
  496. {
  497. // Move succeeded, the old target dir is now empty, so
  498. // no need to prompt about unpinning it (just go ahead
  499. // and do it).
  500. fPromptUnPin = FALSE;
  501. }
  502. else
  503. {
  504. // move failure
  505. ShellMessageBox(g_hInstance, pci->hDlg,
  506. MAKEINTRESOURCE(IDS_MOVE_ERROR), MAKEINTRESOURCE(IDS_MOVE_ERROR_TITLE),
  507. MB_OK | MB_ICONSTOP | MB_TOPMOST,
  508. szNewFolder, pci->szFolder);
  509. }
  510. }
  511. ResetNonMovedFolders(szNewFolder, rgChildren, ARRAYSIZE(rgChildren));
  512. if (!fNewSubdirOfOld && pci->szFolder[0])
  513. {
  514. // If the old folder was pinned, offer to unpin it.
  515. //
  516. // Do this only if new target is not a subdir of the
  517. // old target, since otherwise we'd end up unpinning
  518. // the new target as well
  519. _MaybeUnpinOldFolder(pci->szFolder, pci->hDlg, fPromptUnPin);
  520. }
  521. }
  522. else
  523. {
  524. ShellMessageBox(g_hInstance, pci->hDlg,
  525. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  526. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  527. lres = PSNRET_INVALID_NOCHANGEPAGE;
  528. }
  529. }
  530. }
  531. else if (dwAttr)
  532. {
  533. DWORD id = IDS_NONEXISTENT_FOLDER;
  534. // The user entered a path that doesn't exist or isn't a
  535. // directory...
  536. if (dwAttr != 0xFFFFFFFF)
  537. {
  538. id = IDS_NOT_DIRECTORY;
  539. }
  540. ShellMessageBox(g_hInstance, pci->hDlg,
  541. IntToPtr_(LPTSTR, id), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  542. MB_OK | MB_ICONERROR | MB_TOPMOST);
  543. lres = PSNRET_INVALID_NOCHANGEPAGE;
  544. }
  545. else
  546. {
  547. ShellMessageBox(g_hInstance, pci->hDlg,
  548. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  549. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  550. lres = PSNRET_INVALID_NOCHANGEPAGE;
  551. }
  552. break;
  553. default:
  554. // the path to something isn't allowed
  555. ShellMessageBox(g_hInstance, pci->hDlg,
  556. MAKEINTRESOURCE(IDS_NOTALLOWED_FOLDERS), GetMessageTitle(pci, szPropTitle, ARRAYSIZE(szPropTitle)),
  557. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  558. lres = PSNRET_INVALID_NOCHANGEPAGE;
  559. break;
  560. }
  561. }
  562. if (lres == PSNRET_NOERROR)
  563. {
  564. pci->bDirty = FALSE;
  565. lstrcpy(pci->szFolder, szNewFolder);
  566. }
  567. SetWindowLongPtr(pci->hDlg, DWLP_MSGRESULT, lres);
  568. }
  569. int _BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
  570. {
  571. switch (uMsg)
  572. {
  573. case BFFM_INITIALIZED:
  574. // Set the caption. ('Select a destination')
  575. TCHAR szTitle[100];
  576. LoadString(g_hInstance, IDS_BROWSE_CAPTION, szTitle, ARRAYSIZE(szTitle));
  577. SetWindowText(hwnd, szTitle);
  578. break;
  579. case BFFM_SELCHANGED:
  580. if (lParam)
  581. {
  582. TCHAR szPath[MAX_PATH];
  583. szPath[0] = 0;
  584. SHGetPathFromIDList((LPITEMIDLIST)lParam, szPath);
  585. DWORD dwRes = IsPathGoodMyDocsPath(hwnd, szPath);
  586. if (dwRes == PATH_IS_GOOD || dwRes == PATH_IS_MYDOCS)
  587. {
  588. SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM)TRUE);
  589. SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, 0);
  590. }
  591. else
  592. {
  593. TCHAR szStatus[128];
  594. SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
  595. szStatus[0] = 0;
  596. LoadString(g_hInstance, IDS_NOSHELLEXT_FOLDERS, szStatus, ARRAYSIZE(szStatus));
  597. SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szStatus);
  598. }
  599. }
  600. break;
  601. }
  602. return 0;
  603. }
  604. void _MakeDirty(CUSTINFO *pci)
  605. {
  606. pci->bDirty = TRUE;
  607. pci->bSetToDefault = FALSE;
  608. PropSheet_Changed(GetParent(pci->hDlg), pci->hDlg);
  609. }
  610. void _DoFind(CUSTINFO *pci)
  611. {
  612. TCHAR szPath[MAX_PATH];
  613. GetTargetExpandedPath(pci->hDlg, szPath, ARRAYSIZE(szPath));
  614. LPITEMIDLIST pidl = ILCreateFromPath(szPath);
  615. if (pidl)
  616. {
  617. SHOpenFolderAndSelectItems(pidl, 0, NULL, 0);
  618. ILFree(pidl);
  619. }
  620. else
  621. {
  622. ShellMessageBox(g_hInstance, pci->hDlg,
  623. MAKEINTRESOURCE(IDS_GENERAL_BADDIR), MAKEINTRESOURCE(IDS_INVALID_TITLE),
  624. MB_OK | MB_ICONSTOP | MB_TOPMOST);
  625. }
  626. }
  627. void _DoBrowse(CUSTINFO *pci)
  628. {
  629. BROWSEINFO bi = {0};
  630. TCHAR szTitle[128];
  631. LoadString(g_hInstance, IDS_BROWSE_TITLE, szTitle, ARRAYSIZE(szTitle));
  632. bi.hwndOwner = pci->hDlg;
  633. bi.lpszTitle = szTitle;
  634. bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE | BIF_UAHINT;
  635. bi.lpfn = _BrowseCallbackProc;
  636. // the default root for this folder is MyDocs so we don't need to set that up.
  637. LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
  638. if (pidl)
  639. {
  640. TCHAR szName[MAX_PATH];
  641. if (SHGetPathFromIDList(pidl, szName))
  642. {
  643. SetDlgItemText(pci->hDlg, IDD_TARGET, szName);
  644. _MakeDirty(pci);
  645. }
  646. ILFree(pidl);
  647. }
  648. }
  649. void DoReset(CUSTINFO *pci)
  650. {
  651. TCHAR szPath[MAX_PATH];
  652. if (S_OK == SHGetFolderPath(NULL, pci->csidl, NULL, SHGFP_TYPE_DEFAULT, szPath))
  653. {
  654. SetDlgItemText(pci->hDlg, IDD_TARGET, szPath);
  655. _MakeDirty(pci);
  656. pci->bSetToDefault = TRUE; // to avoid prompt to create
  657. }
  658. }
  659. INT_PTR CALLBACK TargetDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  660. {
  661. CUSTINFO *pci = (CUSTINFO *)GetWindowLongPtr(hDlg, DWLP_USER);
  662. switch (uMsg)
  663. {
  664. case WM_INITDIALOG:
  665. InitTargetPage(hDlg, lParam);
  666. return 1;
  667. case WM_DESTROY:
  668. LocalFree(pci);
  669. SetWindowLongPtr(hDlg, DWLP_USER, 0);
  670. return 1;
  671. case WM_COMMAND:
  672. switch (GET_WM_COMMAND_ID(wParam, lParam))
  673. {
  674. case IDD_RESET:
  675. DoReset(pci);
  676. return 1;
  677. case IDD_TARGET:
  678. if ((GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE) && pci && (pci->bInitDone) && (!pci->bDirty))
  679. {
  680. _MakeDirty(pci);
  681. }
  682. return 1;
  683. case IDD_FIND:
  684. _DoFind(pci);
  685. return 1;
  686. case IDD_BROWSE:
  687. _DoBrowse(pci);
  688. return 1;
  689. }
  690. break;
  691. case WM_HELP: /* F1 or title-bar help button */
  692. if ((((LPHELPINFO)lParam)->iCtrlId != IDD_ITEMICON) &&
  693. (((LPHELPINFO)lParam)->iCtrlId != IDD_INSTRUCTIONS) &&
  694. (((LPHELPINFO)lParam)->iCtrlId != IDC_TARGET_GBOX))
  695. {
  696. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle,
  697. NULL, HELP_WM_HELP, (DWORD_PTR) rgdwHelpTarget);
  698. }
  699. break;
  700. case WM_CONTEXTMENU: /* right mouse click */
  701. {
  702. POINT p;
  703. HWND hwndChild;
  704. INT ctrlid;
  705. //
  706. // Get the point where the user clicked...
  707. //
  708. p.x = GET_X_LPARAM(lParam);
  709. p.y = GET_Y_LPARAM(lParam);
  710. //
  711. // Now, map that to a child control if possible...
  712. //
  713. ScreenToClient(hDlg, &p);
  714. hwndChild = ChildWindowFromPoint((HWND)wParam, p);
  715. ctrlid = GetDlgCtrlID(hwndChild);
  716. //
  717. // Don't put up the help context menu for the items
  718. // that don't have help...
  719. //
  720. if ((ctrlid != IDD_ITEMICON) &&
  721. (ctrlid != IDD_INSTRUCTIONS))
  722. {
  723. WinHelp((HWND)wParam, NULL, HELP_CONTEXTMENU, (DWORD_PTR)rgdwHelpTarget);
  724. }
  725. }
  726. break;
  727. case WM_NOTIFY:
  728. switch (((NMHDR *)lParam)->code)
  729. {
  730. case PSN_APPLY:
  731. _DoApply(pci);
  732. return 1;
  733. }
  734. break;
  735. }
  736. return 0;
  737. }