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.

1368 lines
39 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: info.c
  6. //
  7. // This files contains dialog code for the Info property sheet
  8. //
  9. // History:
  10. // 08-06-93 ScottH Transferred from twin code
  11. //
  12. //---------------------------------------------------------------------------
  13. #include "brfprv.h" // common headers
  14. #include <brfcasep.h>
  15. #include "res.h"
  16. #include <help.h>
  17. //---------------------------------------------------------------------------
  18. // INFO dialog struct
  19. //---------------------------------------------------------------------------
  20. // State flags for the INFO dialog
  21. #define IS_ALLTYPES 0x0001
  22. #define IS_INCLUDESUBS 0x0002
  23. #define IS_DENYAPPLY 0x0004
  24. #define IS_CHANGED 0x0008
  25. #define IS_LAST_INCLUDESUBS 0x0010
  26. typedef struct tagINFO
  27. {
  28. HWND hwnd; // dialog handle
  29. PPAGEDATA ppagedata;
  30. PINFODATA pinfodata;
  31. int cselPrev; // previous count of selections
  32. LPTSTR pszExtListPrev; // alloc: last saved settings
  33. UINT uState;
  34. BOOL bInit;
  35. } INFO, * PINFO;
  36. // Struct for CHANGETWINPROC callback
  37. typedef struct tagCHANGEDATA
  38. {
  39. HBRFCASE hbrf;
  40. HFOLDERTWIN hft;
  41. HDPA hdpaTwins;
  42. int idpaTwin;
  43. HDPA hdpaFolders;
  44. int idpaStart;
  45. UINT uState;
  46. } CHANGEDATA, * PCHANGEDATA;
  47. typedef HRESULT (CALLBACK * CHANGETWINPROC)(PNEWFOLDERTWIN, TWINRESULT, PCHANGEDATA);
  48. // Struct for Info_AddTwins
  49. typedef struct tagADDTWINSDATA
  50. {
  51. CHANGETWINPROC pfnCallback;
  52. HDPA hdpaSortedFolders;
  53. int idpaStart;
  54. } ADDTWINSDATA, * PADDTWINSDATA;
  55. #define MAX_EXT_LEN 6 // Length for "*.ext"
  56. static TCHAR const c_szAllFilesExt[] = TEXT(".*");
  57. // Helper macros
  58. #define Info_StandAlone(this) ((this)->pinfodata->bStandAlone)
  59. #define Info_GetPtr(hwnd) (PINFO)GetWindowLongPtr(hwnd, DWLP_USER)
  60. #define Info_SetPtr(hwnd, lp) (PINFO)SetWindowLongPtr(hwnd, DWLP_USER, (LRESULT)(lp))
  61. SETbl const c_rgseInfo[4] = { // change in ibrfstg.c too
  62. { E_TR_OUT_OF_MEMORY, IDS_OOM_ADDFOLDER, MB_ERROR },
  63. { E_OUTOFMEMORY, IDS_OOM_ADDFOLDER, MB_ERROR },
  64. { E_TR_UNAVAILABLE_VOLUME, IDS_ERR_ADDFOLDER_UNAVAIL_VOL, MB_RETRYCANCEL | MB_ICONWARNING },
  65. { E_TR_SUBTREE_CYCLE_FOUND, IDS_ERR_ADD_SUBTREECYCLE, MB_WARNING },
  66. };
  67. //---------------------------------------------------------------------------
  68. // Info dialog functions
  69. //---------------------------------------------------------------------------
  70. /*----------------------------------------------------------
  71. Purpose: Searches for an occurrence of the given extension
  72. in the folder twin list.
  73. Returns: TRUE if the extension was found
  74. Cond: --
  75. */
  76. BOOL PRIVATE FindExtension(
  77. PFOLDERTWINLIST pftl,
  78. LPCTSTR pszExt)
  79. {
  80. PCFOLDERTWIN pcft;
  81. for (pcft = pftl->pcftFirst; pcft; pcft = pcft->pcftNext)
  82. {
  83. if (IsSzEqual(pszExt, pcft->pcszName))
  84. {
  85. return TRUE; // Found a match!
  86. }
  87. }
  88. return FALSE;
  89. }
  90. /*----------------------------------------------------------
  91. Purpose: Disable all the controls. Remove any selections.
  92. Returns: --
  93. Cond: --
  94. */
  95. void PRIVATE Info_DisableAll(
  96. PINFO this)
  97. {
  98. ASSERT(!Info_StandAlone(this));
  99. // Remove selections
  100. //
  101. ListBox_ResetContent(GetDlgItem(this->hwnd, IDC_LBINTYPES));
  102. Button_SetCheck(GetDlgItem(this->hwnd, IDC_RBINALL), 0);
  103. Button_SetCheck(GetDlgItem(this->hwnd, IDC_RBINSELECTED), 0);
  104. Button_SetCheck(GetDlgItem(this->hwnd, IDC_CHININCLUDE), 0);
  105. // Disable the controls
  106. //
  107. Button_Enable(GetDlgItem(this->hwnd, IDC_RBINALL), FALSE);
  108. Button_Enable(GetDlgItem(this->hwnd, IDC_RBINSELECTED), FALSE);
  109. ListBox_Enable(GetDlgItem(this->hwnd, IDC_LBINTYPES), FALSE);
  110. Button_Enable(GetDlgItem(this->hwnd, IDC_CHININCLUDE), FALSE);
  111. }
  112. /*----------------------------------------------------------
  113. Purpose: Initialize the labels for our formatted radio buttons
  114. Returns: --
  115. Cond: --
  116. */
  117. void PRIVATE Info_InitLabels(
  118. PINFO this)
  119. {
  120. HWND hwnd = this->hwnd;
  121. HWND hwndST = GetDlgItem(hwnd, IDC_CHININCLUDE);
  122. TCHAR sz[MAXMSGLEN];
  123. TCHAR szFmt[MAXBUFLEN];
  124. LPCTSTR pszPath = Atom_GetName(this->ppagedata->atomPath);
  125. LPTSTR pszFile;
  126. pszFile = PathFindFileName(pszPath);
  127. // Set static label
  128. //
  129. GetWindowText(hwndST, szFmt, ARRAYSIZE(szFmt));
  130. wsprintf(sz, szFmt, pszFile);
  131. SetWindowText(hwndST, sz);
  132. if (Info_StandAlone(this))
  133. {
  134. // Set title ("Create Twin of %s")
  135. //
  136. GetWindowText(hwnd, szFmt, ARRAYSIZE(szFmt));
  137. wsprintf(sz, szFmt, pszFile);
  138. SetWindowText(hwnd, sz);
  139. }
  140. }
  141. /*----------------------------------------------------------
  142. Purpose: Queries the registry for all the legal extensions that
  143. are registered. These extensions are returned as a
  144. space-separated list in buffer.
  145. Returns: --
  146. Cond: Caller must GFree *ppszBuffer
  147. */
  148. void PRIVATE GetExtensionList(
  149. LPTSTR * ppszBuffer)
  150. {
  151. HKEY hkRoot;
  152. *ppszBuffer = NULL;
  153. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, NULL, &hkRoot))
  154. {
  155. DWORD dwIndex;
  156. TCHAR szExt[MAX_PATH];
  157. // Enumerate this key
  158. for (dwIndex = 0;
  159. ERROR_SUCCESS == RegEnumKey(hkRoot, dwIndex, szExt, ARRAYSIZE(szExt));
  160. dwIndex++)
  161. {
  162. // Did we get a node that is an extension AND
  163. // is it a legal MS-DOS extension?
  164. if (TEXT('.') == *szExt &&
  165. 4 >= lstrlen(szExt))
  166. {
  167. // Yes; add this extension to our list
  168. lstrcat(szExt, TEXT(" "));
  169. if (FALSE == GCatString(ppszBuffer, szExt))
  170. {
  171. // Uh oh, something bad happened
  172. break;
  173. }
  174. }
  175. }
  176. RegCloseKey(hkRoot);
  177. }
  178. }
  179. /*----------------------------------------------------------
  180. Purpose: Fill the file types listbox
  181. Returns: --
  182. Cond: --
  183. */
  184. void PRIVATE Info_FillTypesList(
  185. PINFO this)
  186. {
  187. HWND hwndCtl = GetDlgItem(this->hwnd, IDC_LBINTYPES);
  188. LPTSTR pszExtList;
  189. GetExtensionList(&pszExtList);
  190. if (pszExtList)
  191. {
  192. int nTabWidth;
  193. TCHAR szExt[MAXBUFLEN];
  194. LPTSTR psz;
  195. LPTSTR pszT;
  196. UINT uLen;
  197. SHFILEINFO sfi;
  198. nTabWidth = 30;
  199. ListBox_SetTabStops(hwndCtl, 1, &nTabWidth);
  200. for (psz = pszExtList; *psz; psz = CharNext(psz))
  201. {
  202. // Skip any leading white-space
  203. for (; TEXT(' ') == *psz; psz = CharNext(psz))
  204. ;
  205. if (0 == *psz)
  206. {
  207. break; // End of string
  208. }
  209. // Skip to next white-space (or null)
  210. for (pszT = psz; TEXT(' ') < *pszT; pszT = CharNext(pszT))
  211. {
  212. // (This will also stop at null)
  213. }
  214. // (GetExtensionList should only get max 3 char extensions)
  215. uLen = (UINT)(pszT - psz);
  216. ASSERT(ARRAYSIZE(szExt) > uLen);
  217. lstrcpyn(szExt, psz, uLen+1);
  218. CharUpper(szExt);
  219. SHGetFileInfo(szExt, 0, &sfi, sizeof(sfi), SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES);
  220. // Although this forces the format for international versions,
  221. // it makes extraction much much easier.
  222. lstrcat(szExt, TEXT("\t("));
  223. lstrcat(szExt, sfi.szTypeName);
  224. lstrcat(szExt, TEXT(")"));
  225. ListBox_AddString(hwndCtl, szExt);
  226. psz = pszT; // To next extension
  227. }
  228. GFree(pszExtList);
  229. }
  230. }
  231. /*----------------------------------------------------------
  232. Purpose: Set the selection of the dialog controls
  233. Returns: --
  234. Cond: --
  235. */
  236. void PRIVATE Info_SetSelections(
  237. PINFO this)
  238. {
  239. HWND hwndLB = GetDlgItem(this->hwnd, IDC_LBINTYPES);
  240. int idBtn;
  241. int cItems = ListBox_GetCount(hwndLB);
  242. ListBox_SetSel(hwndLB, FALSE, -1); // deselect everything
  243. // Is this the 'Add Folder' dialog?
  244. if (Info_StandAlone(this))
  245. {
  246. // Yes; default to *.* settings
  247. SetFlag(this->uState, IS_ALLTYPES);
  248. SetFlag(this->uState, IS_INCLUDESUBS);
  249. }
  250. else
  251. {
  252. // No; query what the selections are
  253. TCHAR szExt[MAXBUFLEN];
  254. PFOLDERTWINLIST pftl;
  255. PCFOLDERTWIN pcft;
  256. int cItems;
  257. int i;
  258. BOOL bStarDotStar;
  259. LPTSTR psz;
  260. if (S_OK == PageData_Query(this->ppagedata, this->hwnd, NULL, &pftl))
  261. {
  262. // Determine the selections in the listbox
  263. szExt[0] = TEXT('*');
  264. cItems = ListBox_GetCount(hwndLB);
  265. for (i = 0; i < cItems; i++)
  266. {
  267. // Extract the extension (it will be the first part of the
  268. // string)
  269. ListBox_GetText(hwndLB, i, &szExt[1]);
  270. for (psz = szExt; *psz && TEXT('\t') != *psz; psz = CharNext(psz))
  271. ;
  272. ASSERT(TEXT('\t') == *psz);
  273. *psz = 0; // null terminate after the extension
  274. // Is this extension in the folder twin list?
  275. if (FindExtension(pftl, szExt))
  276. {
  277. // Yes; select the entry
  278. ListBox_SetSel(hwndLB, TRUE, i);
  279. }
  280. }
  281. ListBox_SetTopIndex(hwndLB, 0);
  282. this->cselPrev = ListBox_GetSelCount(hwndLB);
  283. // Determine the Include Subdirectories checkbox setting
  284. //
  285. bStarDotStar = FALSE;
  286. ClearFlag(this->uState, IS_INCLUDESUBS);
  287. for (pcft = pftl->pcftFirst; pcft; pcft = pcft->pcftNext)
  288. {
  289. if (IsFlagSet(pcft->dwFlags, FT_FL_SUBTREE))
  290. SetFlag(this->uState, IS_INCLUDESUBS);
  291. if (IsSzEqual(pcft->pcszName, c_szAllFiles))
  292. bStarDotStar = TRUE;
  293. }
  294. // Set the default radio button choice, and disable listbox
  295. // if necessary. The default radio choice will be IDC_RBINALL,
  296. // unless there are selections in the listbox AND there is no
  297. // *.* occurrence in the folder twin list.
  298. //
  299. if (0 == this->cselPrev || bStarDotStar)
  300. SetFlag(this->uState, IS_ALLTYPES);
  301. else
  302. ClearFlag(this->uState, IS_ALLTYPES);
  303. }
  304. else
  305. {
  306. // An error occurred or this is an orphan. Bail early.
  307. return;
  308. }
  309. }
  310. if (IsFlagSet(this->uState, IS_INCLUDESUBS))
  311. SetFlag(this->uState, IS_LAST_INCLUDESUBS);
  312. else
  313. ClearFlag(this->uState, IS_LAST_INCLUDESUBS);
  314. // Set the control settings
  315. Button_SetCheck(GetDlgItem(this->hwnd, IDC_CHININCLUDE), IsFlagSet(this->uState, IS_INCLUDESUBS));
  316. ListBox_Enable(hwndLB, IsFlagClear(this->uState, IS_ALLTYPES));
  317. idBtn = IsFlagSet(this->uState, IS_ALLTYPES) ? IDC_RBINALL : IDC_RBINSELECTED;
  318. CheckRadioButton(this->hwnd, IDC_RBINALL, IDC_RBINSELECTED, idBtn);
  319. // If listbox is empty, disable Selected Types radio button
  320. if (0 == cItems)
  321. {
  322. Button_Enable(GetDlgItem(this->hwnd, IDC_RBINSELECTED), FALSE);
  323. }
  324. }
  325. /*----------------------------------------------------------
  326. Purpose: Get the selected extensions in the listbox
  327. and place them as a list in *ppszExtList.
  328. .* is placed in the buffer if the Select All radio button
  329. is chosen instead.
  330. Returns: TRUE on success
  331. Cond: The caller must GFree *ppszExtList
  332. */
  333. BOOL PRIVATE Info_GetSelections(
  334. PINFO this,
  335. LPTSTR * ppszExtList)
  336. {
  337. BOOL bRet = FALSE;
  338. *ppszExtList = NULL;
  339. // Did user choose the All Types radio button?
  340. if (IsFlagSet(this->uState, IS_ALLTYPES))
  341. {
  342. // Yes; store the .* extension
  343. bRet = GSetString(ppszExtList, c_szAllFilesExt);
  344. }
  345. else
  346. {
  347. // No; user selected a bunch of wildcards to filter
  348. LPINT pisel;
  349. TCHAR szExt[MAXBUFLEN];
  350. int csel;
  351. int isel;
  352. HWND hwndCtl = GetDlgItem(this->hwnd, IDC_LBINTYPES);
  353. // Allocate memory for the selection buffer
  354. csel = ListBox_GetSelCount(hwndCtl);
  355. pisel = GAllocArray(int, csel);
  356. if (pisel)
  357. {
  358. // Get the selected extensions from the listbox
  359. LPTSTR psz;
  360. if (0 < csel)
  361. {
  362. ListBox_GetSelItems(hwndCtl, csel, pisel);
  363. for (isel = 0; isel < csel; isel++)
  364. {
  365. // Extract the extension (it will be the first part of the string)
  366. ListBox_GetText(hwndCtl, pisel[isel], szExt);
  367. for (psz = szExt; *psz && TEXT('\t') != *psz; psz = CharNext(psz))
  368. ;
  369. ASSERT(TEXT('\t') == *psz);
  370. *psz = 0;
  371. if (FALSE == GCatString(ppszExtList, szExt))
  372. {
  373. break;
  374. }
  375. }
  376. if (isel == csel)
  377. {
  378. bRet = TRUE; // Success
  379. }
  380. else
  381. {
  382. GFree(*ppszExtList);
  383. }
  384. }
  385. GFree(pisel);
  386. }
  387. }
  388. return bRet;
  389. }
  390. /*----------------------------------------------------------
  391. Purpose: Create a sorted DPA version of the folder twin list
  392. Returns: hdpa
  393. NULL on OOM
  394. Cond: --
  395. */
  396. HDPA PRIVATE CreateSortedFolderDPA(
  397. PFOLDERTWINLIST pftl)
  398. {
  399. HDPA hdpa;
  400. ASSERT(pftl);
  401. hdpa = DPA_Create(8);
  402. if (hdpa)
  403. {
  404. PCFOLDERTWIN pcft;
  405. for (pcft = pftl->pcftFirst; pcft; pcft = pcft->pcftNext)
  406. {
  407. // Use the dwUser field as a deletion flag
  408. ((PFOLDERTWIN)pcft)->dwUser = FALSE;
  409. if (DPA_ERR == DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)pcft))
  410. {
  411. DPA_Destroy(hdpa);
  412. return NULL;
  413. }
  414. }
  415. DPA_Sort(hdpa, NCompareFolders, CMP_FOLDERTWINS);
  416. }
  417. return hdpa;
  418. }
  419. /*----------------------------------------------------------
  420. Purpose: Process callback after adding a folder twin
  421. Returns: standard result
  422. Cond: --
  423. */
  424. HRESULT CALLBACK ChangeTwinProc(
  425. PNEWFOLDERTWIN pnft,
  426. TWINRESULT tr,
  427. PCHANGEDATA pcd)
  428. {
  429. HRESULT hres = NOERROR;
  430. // Is this a duplicate twin?
  431. if (TR_DUPLICATE_TWIN == tr)
  432. {
  433. // Yes; there's a wierd case to deal with. It's possible that the
  434. // only thing the user did was check/uncheck the Include Subdirs
  435. // checkbox. If this is true, then we delete the old twin and add
  436. // a new twin (with same filespec as before) with the flags set
  437. // differently.
  438. PCFOLDERTWIN pcft;
  439. HDPA hdpaFolders = pcd->hdpaFolders;
  440. int cdpa = DPA_GetPtrCount(hdpaFolders);
  441. int idpa;
  442. BOOL bOldInclude;
  443. // Find the correct pcfolder. We will either tag it or
  444. // we will delete it right now and re-add the new twin.
  445. for (idpa = pcd->idpaStart; idpa < cdpa; idpa++)
  446. {
  447. pcft = DPA_FastGetPtr(hdpaFolders, idpa);
  448. if (IsSzEqual(pcft->pcszName, pnft->pcszName))
  449. break; // found it!
  450. }
  451. ASSERT(idpa < cdpa);
  452. // Tag the twin to save from impending doom...
  453. ((PFOLDERTWIN)(DWORD_PTR)pcft)->dwUser = TRUE;
  454. // Has the Include Subfolders checkbox setting changed?
  455. bOldInclude = IsFlagSet(pcft->dwFlags, FT_FL_SUBTREE);
  456. if (bOldInclude ^ IsFlagSet(pcd->uState, IS_INCLUDESUBS))
  457. {
  458. // Yes; delete the twin anyway and add the new one.
  459. HFOLDERTWIN hft;
  460. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Deleting old folder twin")); )
  461. Sync_DeleteTwin(pcft->hftOther);
  462. // Add the new folder twin to the database
  463. tr = Sync_AddFolder(pcd->hbrf, pnft, &hft);
  464. if (TR_SUCCESS != tr)
  465. {
  466. // Adding the new twin failed
  467. DPA_DeletePtr(pcd->hdpaTwins, pcd->idpaTwin);
  468. hres = HRESULT_FROM_TR(tr);
  469. }
  470. else
  471. {
  472. // Set the new twin handle in the pcd->hdpaTwins list
  473. DPA_SetPtr(pcd->hdpaTwins, pcd->idpaTwin, (LPVOID)hft);
  474. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding new folder twin")); )
  475. DEBUG_CODE( Sync_Dump(pnft, NEWFOLDERTWIN); )
  476. }
  477. }
  478. else
  479. {
  480. // No; this isn't new, so don't add to list
  481. DPA_DeletePtr(pcd->hdpaTwins, pcd->idpaTwin);
  482. }
  483. }
  484. else if (tr != TR_SUCCESS)
  485. {
  486. // Sync_AddFolder failed
  487. DPA_DeletePtr(pcd->hdpaTwins, pcd->idpaTwin);
  488. hres = HRESULT_FROM_TR(tr);
  489. }
  490. else
  491. {
  492. // Sync_AddFolder succeeded
  493. DPA_SetPtr(pcd->hdpaTwins, pcd->idpaTwin, (LPVOID)pcd->hft);
  494. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding new folder twin")); )
  495. DEBUG_CODE( Sync_Dump(pnft, NEWFOLDERTWIN); )
  496. }
  497. return hres;
  498. }
  499. /*----------------------------------------------------------
  500. Purpose: Add folder twins based on the list of extensions
  501. Returns: standard result
  502. Cond: --
  503. */
  504. HRESULT PRIVATE Info_AddTwins(
  505. PINFO this,
  506. PNEWFOLDERTWIN pnft,
  507. PADDTWINSDATA patd, // May be NULL
  508. LPTSTR pszExtList) // This function writes in this buffer
  509. {
  510. HRESULT hres = NOERROR;
  511. CHANGEDATA cd;
  512. HDPA hdpa;
  513. int idpa;
  514. TCHAR szWildcard[MAX_EXT_LEN];
  515. LPTSTR psz;
  516. LPTSTR pszT;
  517. TCHAR ch;
  518. hdpa = this->pinfodata->hdpaTwins;
  519. cd.hbrf = PageData_GetHbrf(this->ppagedata);
  520. cd.hdpaTwins = hdpa;
  521. if (patd)
  522. {
  523. cd.hdpaFolders = patd->hdpaSortedFolders;
  524. cd.idpaStart = patd->idpaStart;
  525. }
  526. cd.uState = this->uState;
  527. pnft->pcszName = szWildcard;
  528. szWildcard[0] = TEXT('*');
  529. for (psz = pszExtList; *psz; )
  530. {
  531. TWINRESULT tr;
  532. HFOLDERTWIN hft = NULL;
  533. // Find the beginning of the next extension for the next iteration
  534. for (pszT = CharNext(psz); *pszT && TEXT('.') != *pszT; pszT = CharNext(pszT))
  535. ;
  536. ch = *pszT;
  537. *pszT = 0; // Temporary assignment
  538. // Copy the extension into the name string
  539. lstrcpy(&szWildcard[1], psz);
  540. *pszT = ch;
  541. psz = pszT;
  542. // First make sure we can add another handle to hdpaTwins
  543. if (DPA_ERR == (idpa = DPA_InsertPtr(hdpa, DPA_APPEND, (LPVOID)hft)))
  544. {
  545. hres = ResultFromScode(E_OUTOFMEMORY);
  546. break; // Failed
  547. }
  548. // Add the folder twin to the database
  549. tr = Sync_AddFolder(cd.hbrf, pnft, &hft);
  550. if (patd)
  551. {
  552. cd.idpaTwin = idpa;
  553. cd.hft = hft;
  554. ASSERT(patd->pfnCallback);
  555. if ( FAILED((hres = patd->pfnCallback(pnft, tr, &cd))) )
  556. {
  557. break;
  558. }
  559. }
  560. else if (TR_SUCCESS != tr)
  561. {
  562. // Sync_AddFolder failed
  563. DPA_DeletePtr(hdpa, idpa);
  564. hres = HRESULT_FROM_TR(tr);
  565. break;
  566. }
  567. else
  568. {
  569. // Sync_AddFolder succeeded
  570. DPA_SetPtr(hdpa, idpa, (LPVOID)hft);
  571. DEBUG_CODE( Sync_Dump(pnft, NEWFOLDERTWIN); )
  572. }
  573. }
  574. return hres;
  575. }
  576. /*----------------------------------------------------------
  577. Purpose: Add the folder twin to the database
  578. Returns: standard result
  579. Cond: --
  580. */
  581. HRESULT PRIVATE Info_CommitStandAlone(
  582. PINFO this)
  583. {
  584. HRESULT hres;
  585. NEWFOLDERTWIN nft;
  586. LPTSTR pszExtList;
  587. RETRY_BEGIN(FALSE)
  588. {
  589. ZeroInit(&nft, NEWFOLDERTWIN);
  590. nft.ulSize = sizeof(nft);
  591. nft.pcszFolder1 = Atom_GetName(this->ppagedata->atomPath);
  592. nft.pcszFolder2 = Atom_GetName(this->pinfodata->atomTo);
  593. // nft.pcszName is set in Info_AddTwins()
  594. nft.dwAttributes = OBJECT_TWIN_ATTRIBUTES;
  595. nft.dwFlags = IsFlagSet(this->uState, IS_INCLUDESUBS) ? NFT_FL_SUBTREE : 0;
  596. // Create an extension list based on the dialog settings
  597. if (!Info_GetSelections(this, &pszExtList))
  598. {
  599. // Failed
  600. hres = ResultFromScode(E_OUTOFMEMORY);
  601. }
  602. else
  603. {
  604. // Add the twins
  605. hres = Info_AddTwins(this, &nft, NULL, pszExtList);
  606. GFree(pszExtList);
  607. }
  608. if (SUCCEEDED(hres))
  609. {
  610. // Since the engine does not create folders if the folder is empty,
  611. // we will create the folder now (whether it is empty or not).
  612. // If the folder already exists, CreateDirectory will fail.
  613. // Big deal.
  614. CreateDirectory(nft.pcszFolder2, NULL);
  615. PathNotifyShell(nft.pcszFolder2, NSE_MKDIR, FALSE);
  616. }
  617. else
  618. {
  619. DWORD dwError = GetLastError();
  620. int id;
  621. // Unavailable disk?
  622. if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError)
  623. {
  624. // Yes
  625. hres = E_TR_UNAVAILABLE_VOLUME;
  626. }
  627. id = SEMsgBox(this->hwnd, IDS_CAP_INFO, hres, c_rgseInfo, ARRAYSIZE(c_rgseInfo));
  628. if (IDRETRY == id)
  629. {
  630. // Try the operation again
  631. RETRY_SET();
  632. }
  633. }
  634. }
  635. RETRY_END()
  636. return hres;
  637. }
  638. /*----------------------------------------------------------
  639. Purpose: Commit the user changes to the database. We delete
  640. all old hFolderTwins, and add new ones.
  641. Returns: standard result
  642. Cond: --
  643. */
  644. HRESULT PRIVATE Info_CommitChange(
  645. PINFO this)
  646. {
  647. HRESULT hres;
  648. PFOLDERTWINLIST pftl;
  649. hres = PageData_Query(this->ppagedata, this->hwnd, NULL, &pftl);
  650. if (S_FALSE == hres)
  651. {
  652. // The folder has become an orphan right under our nose.
  653. // Don't do anything.
  654. Info_DisableAll(this);
  655. }
  656. else if (S_OK == hres)
  657. {
  658. LPCTSTR pszPath = Atom_GetName(this->ppagedata->atomPath);
  659. ADDTWINSDATA atd;
  660. DECLAREHOURGLASS;
  661. SetHourglass();
  662. atd.pfnCallback = ChangeTwinProc;
  663. // Create a sorted DPA based on the folder twin list
  664. atd.hdpaSortedFolders = CreateSortedFolderDPA(pftl);
  665. if (atd.hdpaSortedFolders)
  666. {
  667. // Create an extension list based on the dialog settings
  668. LPTSTR pszExtList = NULL;
  669. if (Info_GetSelections(this, &pszExtList))
  670. {
  671. NEWFOLDERTWIN nft;
  672. PCFOLDERTWIN pcft;
  673. PCFOLDERTWIN pcftLast;
  674. int idpa;
  675. int cdpa;
  676. // Now add new folder twins. Iterate thru atd.hdpaSortedFolders.
  677. // For each unique folder twin in this list, we add a new twin,
  678. // using the old lpcszFolder as the lpcszFolder2 field in our
  679. // NEWFOLDERTWIN structure.
  680. //
  681. ZeroInit(&nft, NEWFOLDERTWIN);
  682. nft.ulSize = sizeof(NEWFOLDERTWIN);
  683. nft.pcszFolder1 = pszPath;
  684. // nft.pcszFolder2 is set in loop below
  685. // nft.pcszName is set in Info_AddTwins()
  686. nft.dwAttributes = OBJECT_TWIN_ATTRIBUTES;
  687. nft.dwFlags = IsFlagSet(this->uState, IS_INCLUDESUBS) ? NFT_FL_SUBTREE : 0;
  688. // Iterate thru existing folder twins. Act on each unique one.
  689. cdpa = DPA_GetPtrCount(atd.hdpaSortedFolders);
  690. pcftLast = NULL;
  691. for (idpa = 0; idpa < cdpa; idpa++)
  692. {
  693. pcft = DPA_FastGetPtr(atd.hdpaSortedFolders, idpa);
  694. // Unique?
  695. if (pcftLast && pcft->pcszOtherFolder == pcftLast->pcszOtherFolder)
  696. {
  697. // No; skip to next one
  698. continue;
  699. }
  700. // This is a unique folder. Add it using the extensions in
  701. // pszExtList.
  702. atd.idpaStart = idpa;
  703. nft.pcszFolder2 = pcft->pcszOtherFolder;
  704. hres = Info_AddTwins(this, &nft, &atd, pszExtList);
  705. if (FAILED(hres))
  706. {
  707. goto Cleanup;
  708. }
  709. pcftLast = pcft;
  710. }
  711. // Delete any old twins
  712. for (pcft = pftl->pcftFirst; pcft; pcft = pcft->pcftNext)
  713. {
  714. // Is it okay to delete this twin?
  715. if (pcft->hftOther && FALSE == pcft->dwUser)
  716. {
  717. // Yes
  718. TRACE_MSG(TF_GENERAL, TEXT("Deleting folder twin with extension '%s'"), pcft->pcszName);
  719. Sync_DeleteTwin(pcft->hftOther);
  720. }
  721. }
  722. Cleanup:
  723. GFree(pszExtList);
  724. }
  725. DPA_Destroy(atd.hdpaSortedFolders);
  726. }
  727. ResetHourglass();
  728. // Notify the shell of the change
  729. PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE);
  730. // Throw out the last saved settings and reset
  731. GFree(this->pszExtListPrev);
  732. Info_GetSelections(this, &this->pszExtListPrev);
  733. this->ppagedata->bRecalc = TRUE;
  734. if (FAILED(hres))
  735. {
  736. static SETbl const c_rgseInfoChange[] = {
  737. { E_TR_OUT_OF_MEMORY, IDS_OOM_CHANGETYPES, MB_ERROR },
  738. { E_OUTOFMEMORY, IDS_OOM_CHANGETYPES, MB_ERROR },
  739. { E_TR_SUBTREE_CYCLE_FOUND, IDS_ERR_ADD_SUBTREECYCLE, MB_WARNING },
  740. };
  741. SEMsgBox(this->hwnd, IDS_CAP_INFO, hres, c_rgseInfoChange, ARRAYSIZE(c_rgseInfoChange));
  742. }
  743. }
  744. return hres;
  745. }
  746. /*----------------------------------------------------------
  747. Purpose: Info WM_INITDIALOG Handler
  748. Returns:
  749. Cond: --
  750. */
  751. BOOL PRIVATE Info_OnInitDialog(
  752. PINFO this,
  753. HWND hwndFocus,
  754. LPARAM lParam) // LPPROPSHEETINFO
  755. {
  756. this->ppagedata = (PPAGEDATA)((LPPROPSHEETPAGE)lParam)->lParam;
  757. this->pinfodata = (PINFODATA)this->ppagedata->lParam;
  758. // Set the text of the controls
  759. Info_InitLabels(this);
  760. // Fill listbox and set the control selections
  761. Info_FillTypesList(this);
  762. if (Info_StandAlone(this))
  763. {
  764. Info_SetSelections(this);
  765. Info_GetSelections(this, &this->pszExtListPrev);
  766. }
  767. this->bInit = TRUE;
  768. return TRUE;
  769. }
  770. /*----------------------------------------------------------
  771. Purpose: PSN_APPLY handler
  772. Returns: FALSE if everything is OK
  773. TRUE to have the property sheet switch to this page to
  774. correct something.
  775. Cond: --
  776. */
  777. BOOL PRIVATE Info_OnApply(
  778. PINFO this)
  779. {
  780. BOOL bRet;
  781. LPTSTR pszExtList;
  782. ASSERT(!Info_StandAlone(this));
  783. Info_GetSelections(this, &pszExtList);
  784. // Deny the apply?
  785. if (IsFlagSet(this->uState, IS_DENYAPPLY))
  786. {
  787. // Yes; don't let the apply go thru
  788. MsgBox(this->hwnd, MAKEINTRESOURCE(IDS_MSG_SPECIFYTYPE),
  789. MAKEINTRESOURCE(IDS_CAP_INFO), NULL, MB_ERROR);
  790. bRet = PSNRET_INVALID;
  791. }
  792. // Have any settings changed?
  793. else if (pszExtList && this->pszExtListPrev &&
  794. // (Assume extensions are always listed in same order)
  795. IsSzEqual(this->pszExtListPrev, pszExtList) &&
  796. IsFlagSet(this->uState, IS_INCLUDESUBS) == IsFlagSet(this->uState, IS_LAST_INCLUDESUBS))
  797. {
  798. // No
  799. bRet = PSNRET_NOERROR;
  800. }
  801. else
  802. {
  803. // Yes; commit the changes
  804. Info_CommitChange(this);
  805. // Sync up the current/previous state
  806. if (IsFlagSet(this->uState, IS_INCLUDESUBS))
  807. SetFlag(this->uState, IS_LAST_INCLUDESUBS);
  808. else
  809. ClearFlag(this->uState, IS_LAST_INCLUDESUBS);
  810. bRet = PSNRET_NOERROR;
  811. }
  812. GFree(pszExtList);
  813. ClearFlag(this->uState, IS_CHANGED);
  814. return bRet;
  815. }
  816. /*----------------------------------------------------------
  817. Purpose: PSN_SETACTIVE handler
  818. Returns: --
  819. Cond: --
  820. */
  821. void PRIVATE Info_OnSetActive(
  822. PINFO this)
  823. {
  824. HWND hwnd = this->hwnd;
  825. // Cause the page to be painted right away
  826. SetWindowRedraw(hwnd, TRUE);
  827. InvalidateRect(hwnd, NULL, TRUE);
  828. UpdateWindow(hwnd);
  829. if (this->bInit)
  830. {
  831. PageData_Init(this->ppagedata, GetParent(hwnd));
  832. this->bInit = FALSE;
  833. Info_SetSelections(this);
  834. Info_GetSelections(this, &this->pszExtListPrev);
  835. }
  836. // Is this data still valid?
  837. else if (S_FALSE == PageData_Query(this->ppagedata, this->hwnd, NULL, NULL))
  838. {
  839. // No; the folder has become an orphan
  840. Info_DisableAll(this);
  841. }
  842. }
  843. /*----------------------------------------------------------
  844. Purpose: WM_NOTIFY handler
  845. Returns: varies
  846. Cond: --
  847. */
  848. LRESULT PRIVATE Info_OnNotify(
  849. PINFO this,
  850. int idFrom,
  851. NMHDR * lpnmhdr)
  852. {
  853. LRESULT lRet = 0;
  854. switch (lpnmhdr->code)
  855. {
  856. case PSN_SETACTIVE:
  857. Info_OnSetActive(this);
  858. break;
  859. case PSN_APPLY:
  860. lRet = Info_OnApply(this);
  861. break;
  862. default:
  863. break;
  864. }
  865. return lRet;
  866. }
  867. /*----------------------------------------------------------
  868. Purpose: Determines whether to keep from leaving this sheet.
  869. For the stand-alone ('Add Folder') dialog, this
  870. function enables or disables the OK button.
  871. Returns: --
  872. Cond: --
  873. */
  874. void PRIVATE Info_DenyKill(
  875. PINFO this,
  876. BOOL bDeny)
  877. {
  878. if (Info_StandAlone(this))
  879. {
  880. Button_Enable(GetDlgItem(this->hwnd, IDOK), !bDeny);
  881. }
  882. else
  883. {
  884. if (bDeny)
  885. SetFlag(this->uState, IS_DENYAPPLY);
  886. else
  887. ClearFlag(this->uState, IS_DENYAPPLY);
  888. }
  889. }
  890. /*----------------------------------------------------------
  891. Purpose: Enable the Apply button
  892. Returns: --
  893. Cond: --
  894. */
  895. void PRIVATE Info_HandleChange(
  896. PINFO this)
  897. {
  898. if (IsFlagClear(this->uState, IS_CHANGED) && !Info_StandAlone(this))
  899. {
  900. SetFlag(this->uState, IS_CHANGED);
  901. PropSheet_Changed(GetParent(this->hwnd), this->hwnd);
  902. }
  903. }
  904. /*----------------------------------------------------------
  905. Purpose: Info WM_COMMAND Handler
  906. Returns: --
  907. Cond: --
  908. */
  909. VOID PRIVATE Info_OnCommand(
  910. PINFO this,
  911. int id,
  912. HWND hwndCtl,
  913. UINT uNotifyCode)
  914. {
  915. HWND hwnd = this->hwnd;
  916. switch (id)
  917. {
  918. case IDC_RBINALL:
  919. Info_DenyKill(this, FALSE);
  920. // fall thru
  921. case IDC_RBINSELECTED:
  922. // Disable/enable listbox depending on which radio button
  923. // is marked.
  924. //
  925. if (IDC_RBINALL == id)
  926. SetFlag(this->uState, IS_ALLTYPES);
  927. else
  928. ClearFlag(this->uState, IS_ALLTYPES);
  929. ListBox_Enable(GetDlgItem(hwnd, IDC_LBINTYPES), IsFlagClear(this->uState, IS_ALLTYPES));
  930. if (IDC_RBINSELECTED == id &&
  931. 0 == ListBox_GetSelCount(GetDlgItem(hwnd, IDC_LBINTYPES)))
  932. {
  933. Info_DenyKill(this, TRUE);
  934. }
  935. Info_HandleChange(this);
  936. break;
  937. case IDC_LBINTYPES:
  938. if (uNotifyCode == LBN_SELCHANGE)
  939. {
  940. // Disable/enable OK button based on number of selections
  941. // in listbox.
  942. //
  943. int csel = ListBox_GetSelCount(GetDlgItem(hwnd, IDC_LBINTYPES));
  944. if (csel == 0)
  945. Info_DenyKill(this, TRUE);
  946. else if (csel != this->cselPrev && this->cselPrev == 0)
  947. Info_DenyKill(this, FALSE);
  948. this->cselPrev = csel;
  949. Info_HandleChange(this);
  950. }
  951. break;
  952. case IDC_CHININCLUDE:
  953. if (FALSE != Button_GetCheck(GetDlgItem(hwnd, IDC_CHININCLUDE)))
  954. SetFlag(this->uState, IS_INCLUDESUBS);
  955. else
  956. ClearFlag(this->uState, IS_INCLUDESUBS);
  957. Info_HandleChange(this);
  958. break;
  959. case IDOK:
  960. if (FAILED(Info_CommitStandAlone(this)))
  961. EndDialog(hwnd, -1);
  962. // Fall thru
  963. // | |
  964. // v v
  965. case IDCANCEL:
  966. if (Info_StandAlone(this))
  967. EndDialog(hwnd, id);
  968. break;
  969. }
  970. }
  971. /*----------------------------------------------------------
  972. Purpose: Handle WM_DESTROY
  973. Returns: --
  974. Cond: --
  975. */
  976. void PRIVATE Info_OnDestroy(
  977. PINFO this)
  978. {
  979. GFree(this->pszExtListPrev);
  980. }
  981. ///////////////////////////////////////////////////// PRIVATE FUNCTIONS
  982. static BOOL s_bInfoRecurse = FALSE;
  983. LRESULT INLINE Info_DefProc(
  984. HWND hDlg,
  985. UINT msg,
  986. WPARAM wParam,
  987. LPARAM lParam)
  988. {
  989. ENTEREXCLUSIVE();
  990. {
  991. s_bInfoRecurse = TRUE;
  992. }
  993. LEAVEEXCLUSIVE();
  994. return DefDlgProc(hDlg, msg, wParam, lParam);
  995. }
  996. /*----------------------------------------------------------
  997. Purpose: Real Create Folder Twin dialog proc
  998. Returns: varies
  999. Cond: --
  1000. */
  1001. LRESULT Info_DlgProc(
  1002. PINFO this,
  1003. UINT message,
  1004. WPARAM wParam,
  1005. LPARAM lParam)
  1006. {
  1007. const static DWORD rgHelpIDs[] = {
  1008. IDC_RBINALL, IDH_BFC_FILTER_TYPE,
  1009. IDC_RBINSELECTED, IDH_BFC_FILTER_TYPE,
  1010. IDC_LBINTYPES, IDH_BFC_FILTER_TYPE,
  1011. IDC_CHININCLUDE, IDH_BFC_FILTER_INCLUDE,
  1012. IDC_GBIN, IDH_BFC_FILTER_TYPE,
  1013. 0, 0 };
  1014. switch (message)
  1015. {
  1016. HANDLE_MSG(this, WM_INITDIALOG, Info_OnInitDialog);
  1017. HANDLE_MSG(this, WM_COMMAND, Info_OnCommand);
  1018. HANDLE_MSG(this, WM_NOTIFY, Info_OnNotify);
  1019. HANDLE_MSG(this, WM_DESTROY, Info_OnDestroy);
  1020. case WM_HELP:
  1021. WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPVOID)rgHelpIDs);
  1022. return 0;
  1023. case WM_CONTEXTMENU:
  1024. WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID)rgHelpIDs);
  1025. return 0;
  1026. default:
  1027. return Info_DefProc(this->hwnd, message, wParam, lParam);
  1028. }
  1029. }
  1030. /*----------------------------------------------------------
  1031. Purpose: Create Folder Twin Dialog Wrapper
  1032. Returns: varies
  1033. Cond: --
  1034. */
  1035. INT_PTR _export CALLBACK Info_WrapperProc(
  1036. HWND hDlg, // std params
  1037. UINT message,
  1038. WPARAM wParam,
  1039. LPARAM lParam)
  1040. {
  1041. PINFO this;
  1042. // Cool windowsx.h dialog technique. For full explanation, see
  1043. // WINDOWSX.TXT. This supports multiple-instancing of dialogs.
  1044. //
  1045. ENTEREXCLUSIVE();
  1046. {
  1047. if (s_bInfoRecurse)
  1048. {
  1049. s_bInfoRecurse = FALSE;
  1050. LEAVEEXCLUSIVE();
  1051. return FALSE;
  1052. }
  1053. }
  1054. LEAVEEXCLUSIVE();
  1055. this = Info_GetPtr(hDlg);
  1056. if (this == NULL)
  1057. {
  1058. if (message == WM_INITDIALOG)
  1059. {
  1060. this = GAlloc(sizeof(*this));
  1061. if (!this)
  1062. {
  1063. MsgBox(hDlg, MAKEINTRESOURCE(IDS_OOM_INFO), MAKEINTRESOURCE(IDS_CAP_INFO),
  1064. NULL, MB_ERROR);
  1065. EndDialog(hDlg, IDCANCEL);
  1066. return Info_DefProc(hDlg, message, wParam, lParam);
  1067. }
  1068. this->hwnd = hDlg;
  1069. Info_SetPtr(hDlg, this);
  1070. }
  1071. else
  1072. {
  1073. return Info_DefProc(hDlg, message, wParam, lParam);
  1074. }
  1075. }
  1076. if (message == WM_DESTROY)
  1077. {
  1078. Info_DlgProc(this, message, wParam, lParam);
  1079. GFree(this);
  1080. Info_SetPtr(hDlg, NULL);
  1081. return 0;
  1082. }
  1083. return SetDlgMsgResult(hDlg, message, Info_DlgProc(this, message, wParam, lParam));
  1084. }
  1085. ///////////////////////////////////////////////////// PUBLIC FUNCTIONS
  1086. /*----------------------------------------------------------
  1087. Purpose: Entry point to invoke dialog
  1088. Returns: standard hresult
  1089. Cond: --
  1090. */
  1091. HRESULT PUBLIC Info_DoModal(
  1092. HWND hwndOwner,
  1093. LPCTSTR pszPathFrom, // Source path
  1094. LPCTSTR pszPathTo, // Target path
  1095. HDPA hdpaTwin,
  1096. PCBS pcbs)
  1097. {
  1098. HRESULT hres;
  1099. PROPSHEETPAGE psp;
  1100. PAGEDATA pagedata;
  1101. INFODATA infodata;
  1102. // (Use the source path for the atomPath because the target path
  1103. // does not exist yet.)
  1104. pagedata.atomPath = Atom_Add(pszPathFrom);
  1105. if (ATOM_ERR != pagedata.atomPath)
  1106. {
  1107. infodata.atomTo = Atom_Add(pszPathTo);
  1108. if (ATOM_ERR != infodata.atomTo)
  1109. {
  1110. INT_PTR nRet;
  1111. pagedata.pcbs = pcbs;
  1112. pagedata.lParam = (LPARAM)&infodata;
  1113. infodata.hdpaTwins = hdpaTwin;
  1114. infodata.bStandAlone = TRUE;
  1115. // Fake up a propsheetinfo struct for the dialog box
  1116. psp.lParam = (LPARAM)&pagedata; // this is all we care about
  1117. nRet = DoModal(hwndOwner, Info_WrapperProc, IDD_INFOCREATE, (LPARAM)(LPVOID)&psp);
  1118. Atom_Delete(infodata.atomTo);
  1119. switch (nRet)
  1120. {
  1121. case IDOK: hres = NOERROR; break;
  1122. case IDCANCEL: hres = E_ABORT; break;
  1123. default: hres = E_OUTOFMEMORY; break;
  1124. }
  1125. }
  1126. else
  1127. {
  1128. hres = E_OUTOFMEMORY;
  1129. }
  1130. Atom_Delete(pagedata.atomPath);
  1131. }
  1132. else
  1133. {
  1134. hres = E_OUTOFMEMORY;
  1135. }
  1136. return hres;
  1137. }