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.

1247 lines
36 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: update.c
  6. //
  7. // This files contains code for the Update UI and dialog.
  8. //
  9. // History:
  10. // 08-17-93 ScottH Created.
  11. //
  12. //---------------------------------------------------------------------------
  13. #include "brfprv.h" // common headers
  14. #include "res.h"
  15. #include "recact.h"
  16. #include <help.h>
  17. // This structure contains all the important counts
  18. // that determine the specific course of action when
  19. // the user wants to update something.
  20. typedef struct
  21. {
  22. // These are 1 to 1
  23. UINT cFiles;
  24. UINT cOrphans;
  25. UINT cSubfolders;
  26. // These are 1 to 1
  27. UINT cUnavailable;
  28. UINT cDoSomething;
  29. UINT cConflict;
  30. UINT cTombstone;
  31. } UPDCOUNT;
  32. // This is the structure passed to the dialog at WM_INITDIALOG
  33. typedef struct
  34. {
  35. PRECLIST lprl; // Supplied reclist
  36. CBS * pcbs;
  37. UINT uFlags; // UF_ Flags
  38. HDPA hdpa; // List of RA_ITEMs
  39. UINT cDoSomething;
  40. } XUPDSTRUCT, * LPXUPDSTRUCT;
  41. typedef struct tagUPD
  42. {
  43. HWND hwnd; // dialog handle
  44. LPXUPDSTRUCT pxupd;
  45. } UPD, * PUPD;
  46. #define Upd_Prl(this) ((this)->pxupd->lprl)
  47. #define Upd_AtomBrf(this) ((this)->pxupd->pcbs->atomBrf)
  48. #define Upd_GetBrfPtr(this) Atom_GetName(Upd_AtomBrf(this))
  49. #define Upd_GetPtr(hwnd) (PUPD)GetWindowLongPtr(hwnd, DWLP_USER)
  50. #define Upd_SetPtr(hwnd, lp) (PUPD)SetWindowLongPtr(hwnd, DWLP_USER, (LRESULT)(lp))
  51. // These flags are used for DoUpdateMsg
  52. #define DUM_ALL 0x0001
  53. #define DUM_SELECTION 0x0002
  54. #define DUM_ORPHAN 0x0004
  55. #define DUM_UPTODATE 0x0008
  56. #define DUM_UNAVAILABLE 0x0010
  57. #define DUM_SUBFOLDER_TWIN 0x0020
  58. // These flags are returned by PassedSpecialCases
  59. #define PSC_SHOWDIALOG 0x0001
  60. #define PSC_POSTMSGBOX 0x0002
  61. //---------------------------------------------------------------------------
  62. // Some comments
  63. //---------------------------------------------------------------------------
  64. // There are several special cases and conditions which must be
  65. // handled. Lets break them down now. The case numbers on on the far
  66. // right, and are referenced in comments thru out this file.
  67. //
  68. // There are two user actions: Update All and Update Selection.
  69. //
  70. // Update All:
  71. //
  72. // Case What to do
  73. // ----------------- --------------
  74. // A1. * No files in the briefcase ---> MB (messagebox)
  75. // A2. * All files are orphans ---> MB
  76. // * Some files are twins and...
  77. // * they are all available and...
  78. // A3. * they are all up-to-date ---> MB
  79. // A4. * some of them need updating ---> Update dialog
  80. // * some are unavailable and...
  81. // A5. * the available ones are up-to-date ---> MB then Update
  82. // A6. * some need updating ---> MB then Update
  83. //
  84. //
  85. // Update Selection:
  86. //
  87. // Case What to do
  88. // ----------------- --------------
  89. // * Single selection and...
  90. // S1. * is an orphan ---> MB
  91. // * is available and...
  92. // S2. * up-to-date ---> MB
  93. // S3. * needs updating ---> Update
  94. // S4. * is unavailable ---> MB then Update
  95. // * Multi selection and...
  96. // S5. * all are orphans ---> MB
  97. // * some (non-orphans) are unavailable and...
  98. // * some need updating and...
  99. // S6. * none are orphans ---> MB then Update
  100. // S7. * some are orphans ---> MB then Update then MB
  101. // * the available ones are up-to-date and...
  102. // S8. * none are orphans ---> MB then Update
  103. // S9. * some are orphans ---> MB then Update then MB
  104. // * all (non-orphans) are available and...
  105. // * some need updating and...
  106. // S10. * none are orphans ---> Update
  107. // S11. * some are orphans ---> Update then MB
  108. // * all up-to-date and...
  109. // S12. * none are orphans ---> MB
  110. // S13. * some are orphans ---> MB
  111. //---------------------------------------------------------------------------
  112. // Dialog code
  113. //---------------------------------------------------------------------------
  114. /*----------------------------------------------------------
  115. Purpose: Fill the reconciliation action listbox
  116. Returns: TRUE on success
  117. Cond: --
  118. */
  119. BOOL PRIVATE Upd_FillList(
  120. PUPD this)
  121. {
  122. HWND hwndCtl = GetDlgItem(this->hwnd, IDC_UPDATEACTIONS);
  123. HDPA hdpa = this->pxupd->hdpa;
  124. int cItems;
  125. int i;
  126. cItems = DPA_GetPtrCount(hdpa);
  127. for (i = 0; i < cItems; i++)
  128. {
  129. RA_ITEM * pitem = DPA_FastGetPtr(hdpa, i);
  130. RecAct_InsertItem(hwndCtl, pitem);
  131. RAI_Free(pitem);
  132. }
  133. return TRUE;
  134. }
  135. /*----------------------------------------------------------
  136. Purpose: Sets the Update and Cancel buttons according to the
  137. bDisableUpdate parameter.
  138. Returns: --
  139. Cond: --
  140. */
  141. void PRIVATE Upd_SetExitButtons(
  142. PUPD this,
  143. BOOL bDisableUpdate)
  144. {
  145. HWND hwndOK = GetDlgItem(this->hwnd, IDOK);
  146. // Disable the update button?
  147. if (bDisableUpdate)
  148. {
  149. // Yes
  150. if (GetFocus() == hwndOK)
  151. {
  152. SetFocus(GetDlgItem(this->hwnd, IDCANCEL));
  153. }
  154. Button_Enable(hwndOK, FALSE);
  155. }
  156. else
  157. {
  158. // No
  159. Button_Enable(hwndOK, TRUE);
  160. }
  161. }
  162. /*----------------------------------------------------------
  163. Purpose: WM_INITDIALOG Handler
  164. Returns:
  165. Cond: --
  166. */
  167. BOOL PRIVATE Upd_OnInitDialog(
  168. PUPD this,
  169. HWND hwndFocus,
  170. LPARAM lParam)
  171. {
  172. HWND hwnd = this->hwnd;
  173. TCHAR szFmt[MAXBUFLEN];
  174. TCHAR sz[MAXMSGLEN];
  175. ASSERT(lParam != 0L);
  176. this->pxupd = (LPXUPDSTRUCT)lParam;
  177. if (Upd_FillList(this))
  178. {
  179. // Set the title caption
  180. wsprintf(sz, SzFromIDS(IDS_CAP_UpdateFmt, szFmt, ARRAYSIZE(szFmt)),
  181. PathFindFileName(Upd_GetBrfPtr(this)));
  182. SetWindowText(hwnd, sz);
  183. // Do any files need updating?
  184. if (0 == this->pxupd->cDoSomething)
  185. {
  186. // No
  187. Upd_SetExitButtons(this, TRUE);
  188. }
  189. }
  190. else
  191. {
  192. // Failed
  193. EndDialog(hwnd, -1);
  194. }
  195. return(TRUE);
  196. }
  197. /*----------------------------------------------------------
  198. Purpose: Handle RN_ITEMCHANGED
  199. Returns: --
  200. Cond: --
  201. */
  202. void PRIVATE Upd_HandleItemChange(
  203. PUPD this,
  204. NM_RECACT * lpnm)
  205. {
  206. PRECITEM lpri;
  207. ASSERT((lpnm->mask & RAIF_LPARAM) != 0);
  208. lpri = (PRECITEM)lpnm->lParam;
  209. // The action has changed, update the recnode accordingly
  210. if (lpnm->mask & RAIF_ACTION)
  211. {
  212. LPCTSTR pszDir = Upd_GetBrfPtr(this);
  213. Sync_ChangeRecItemAction(lpri, pszDir, pszDir, lpnm->uAction);
  214. switch (lpnm->uActionOld)
  215. {
  216. case RAIA_TOOUT:
  217. case RAIA_TOIN:
  218. case RAIA_MERGE:
  219. // Is this a change from "do something" to "skip"?
  220. if (RAIA_SKIP == lpnm->uAction)
  221. {
  222. // Yes
  223. ASSERT(0 < this->pxupd->cDoSomething);
  224. this->pxupd->cDoSomething--;
  225. }
  226. break;
  227. case RAIA_SKIP:
  228. case RAIA_CONFLICT:
  229. // Is this a change from "skip"/"conflict" to "do something"?
  230. if (RAIA_TOOUT == lpnm->uAction ||
  231. RAIA_TOIN == lpnm->uAction ||
  232. RAIA_MERGE == lpnm->uAction)
  233. {
  234. // Yes
  235. this->pxupd->cDoSomething++;
  236. }
  237. break;
  238. }
  239. Upd_SetExitButtons(this, 0 == this->pxupd->cDoSomething);
  240. }
  241. }
  242. /*----------------------------------------------------------
  243. Purpose: WM_NOTIFY handler
  244. Returns: varies
  245. Cond: --
  246. */
  247. LRESULT PRIVATE Upd_OnNotify(
  248. PUPD this,
  249. int idFrom,
  250. NMHDR * lpnmhdr)
  251. {
  252. LRESULT lRet = 0;
  253. switch (lpnmhdr->code)
  254. {
  255. case RN_ITEMCHANGED:
  256. Upd_HandleItemChange(this, (NM_RECACT *)lpnmhdr);
  257. break;
  258. default:
  259. break;
  260. }
  261. return lRet;
  262. }
  263. /*----------------------------------------------------------
  264. Purpose: Info WM_COMMAND Handler
  265. Returns: --
  266. Cond: --
  267. */
  268. VOID PRIVATE Upd_OnCommand(
  269. PUPD this,
  270. int id,
  271. HWND hwndCtl,
  272. UINT uNotifyCode)
  273. {
  274. HWND hwnd = this->hwnd;
  275. switch (id)
  276. {
  277. case IDOK:
  278. case IDCANCEL:
  279. EndDialog(hwnd, id);
  280. break;
  281. }
  282. }
  283. /*----------------------------------------------------------
  284. Purpose: WM_DESTROY handler
  285. Returns: --
  286. Cond: --
  287. */
  288. void PRIVATE Upd_OnDestroy(
  289. PUPD this)
  290. {
  291. }
  292. static BOOL s_bUpdRecurse = FALSE;
  293. LRESULT INLINE Upd_DefProc(
  294. HWND hDlg,
  295. UINT msg,
  296. WPARAM wParam,
  297. LPARAM lParam)
  298. {
  299. ENTEREXCLUSIVE();
  300. {
  301. s_bUpdRecurse = TRUE;
  302. }
  303. LEAVEEXCLUSIVE();
  304. return DefDlgProc(hDlg, msg, wParam, lParam);
  305. }
  306. /*----------------------------------------------------------
  307. Purpose: Real Create Folder Twin dialog proc
  308. Returns: varies
  309. Cond: --
  310. */
  311. LRESULT Upd_DlgProc(
  312. PUPD this,
  313. UINT message,
  314. WPARAM wParam,
  315. LPARAM lParam)
  316. {
  317. const static DWORD rgHelpIDs[] = {
  318. IDC_UPDATEACTIONS, IDH_BFC_UPDATE_SCREEN, // different
  319. IDOK, IDH_BFC_UPDATE_BUTTON,
  320. 0, 0 };
  321. switch (message)
  322. {
  323. HANDLE_MSG(this, WM_INITDIALOG, Upd_OnInitDialog);
  324. HANDLE_MSG(this, WM_COMMAND, Upd_OnCommand);
  325. HANDLE_MSG(this, WM_NOTIFY, Upd_OnNotify);
  326. HANDLE_MSG(this, WM_DESTROY, Upd_OnDestroy);
  327. case WM_HELP:
  328. WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPVOID)rgHelpIDs);
  329. return 0;
  330. case WM_CONTEXTMENU:
  331. WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID)rgHelpIDs);
  332. return 0;
  333. default:
  334. return Upd_DefProc(this->hwnd, message, wParam, lParam);
  335. }
  336. }
  337. /*----------------------------------------------------------
  338. Purpose: Create Folder Twin Dialog Wrapper
  339. Returns: varies
  340. Cond: --
  341. */
  342. INT_PTR _export CALLBACK Upd_WrapperProc(
  343. HWND hDlg, // std params
  344. UINT message,
  345. WPARAM wParam,
  346. LPARAM lParam)
  347. {
  348. PUPD this;
  349. // Cool windowsx.h dialog technique. For full explanation, see
  350. // WINDOWSX.TXT. This supports multiple-instancing of dialogs.
  351. //
  352. ENTEREXCLUSIVE();
  353. {
  354. if (s_bUpdRecurse)
  355. {
  356. s_bUpdRecurse = FALSE;
  357. LEAVEEXCLUSIVE();
  358. return FALSE;
  359. }
  360. }
  361. LEAVEEXCLUSIVE();
  362. this = Upd_GetPtr(hDlg);
  363. if (this == NULL)
  364. {
  365. if (message == WM_INITDIALOG)
  366. {
  367. this = GAlloc(sizeof(*this));
  368. if (!this)
  369. {
  370. MsgBox(hDlg, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
  371. NULL, MB_ERROR);
  372. EndDialog(hDlg, IDCANCEL);
  373. return Upd_DefProc(hDlg, message, wParam, lParam);
  374. }
  375. this->hwnd = hDlg;
  376. Upd_SetPtr(hDlg, this);
  377. }
  378. else
  379. {
  380. return Upd_DefProc(hDlg, message, wParam, lParam);
  381. }
  382. }
  383. if (message == WM_DESTROY)
  384. {
  385. Upd_DlgProc(this, message, wParam, lParam);
  386. GFree(this);
  387. Upd_SetPtr(hDlg, NULL);
  388. return 0;
  389. }
  390. return SetDlgMsgResult(hDlg, message, Upd_DlgProc(this, message, wParam, lParam));
  391. }
  392. //---------------------------------------------------------------------------
  393. // Update detection code
  394. //---------------------------------------------------------------------------
  395. /*----------------------------------------------------------
  396. Purpose: Checks if the briefcase is empty. This function skips the
  397. "desktop.ini" and "Briefcase Database" files.
  398. Returns: TRUE if the briefcase is empty
  399. Cond: --
  400. */
  401. BOOL PRIVATE IsBriefcaseEmpty(
  402. LPCTSTR pszPath)
  403. {
  404. BOOL bRet = FALSE;
  405. ASSERT(pszPath);
  406. if (pszPath)
  407. {
  408. // Enumerate thru folder
  409. TCHAR szSearch[MAXPATHLEN];
  410. WIN32_FIND_DATA fd;
  411. HANDLE hfile;
  412. // This must be per instance, else it will cause a fixup in
  413. // shared data segment.
  414. const static LPCTSTR s_rgszIgnore[] = { TEXT("."), TEXT(".."), g_szDBName, g_szDBNameShort, c_szDesktopIni };
  415. PathCombine(szSearch, pszPath, TEXT("*.*"));
  416. hfile = FindFirstFile(szSearch, &fd);
  417. if (INVALID_HANDLE_VALUE != hfile)
  418. {
  419. BOOL bCont = TRUE;
  420. bRet = TRUE; // Default to empty folder
  421. while (bCont)
  422. {
  423. int bIgnore = FALSE;
  424. int i;
  425. // Is this file one of the files to ignore?
  426. for (i = 0; i < ARRAYSIZE(s_rgszIgnore); i++)
  427. {
  428. if (IsSzEqual(fd.cFileName, s_rgszIgnore[i]))
  429. {
  430. // Yes
  431. bIgnore = TRUE;
  432. break;
  433. }
  434. }
  435. // Is this a valid file/folder?
  436. if (FALSE == bIgnore)
  437. {
  438. // Yes; return the briefcase is not empty
  439. bRet = FALSE;
  440. bCont = FALSE; // stop the enumeration
  441. }
  442. else
  443. {
  444. bCont = FindNextFile(hfile, &fd);
  445. }
  446. }
  447. FindClose(hfile);
  448. }
  449. }
  450. return bRet;
  451. }
  452. /*----------------------------------------------------------
  453. Purpose: Create a DSA of RA_ITEMs
  454. Sets the cDoSomething, cUnavailable, cConflict and
  455. cTombstone fields of pupdcount.
  456. Returns: TRUE on success
  457. Cond: --
  458. */
  459. HDPA PRIVATE ComposeUpdateList(
  460. PCBS pcbs,
  461. PRECLIST prl,
  462. UPDCOUNT * pupdcount,
  463. HWND hwndOwner)
  464. {
  465. HRESULT hres;
  466. HDPA hdpa;
  467. ASSERT(prl);
  468. hdpa = DPA_Create(20);
  469. if (NULL != hdpa)
  470. {
  471. LPRA_ITEM pitem;
  472. PRECITEM pri;
  473. LPCTSTR pszBrf = Atom_GetName(pcbs->atomBrf);
  474. if (pszBrf)
  475. {
  476. DEBUG_CODE( Sync_DumpRecList(TR_SUCCESS, prl, TEXT("ComposeUpdateList")); )
  477. pupdcount->cUnavailable = 0;
  478. pupdcount->cDoSomething = 0;
  479. pupdcount->cTombstone = 0;
  480. pupdcount->cConflict = 0;
  481. for (pri = prl->priFirst; pri; pri = pri->priNext)
  482. {
  483. hres = RAI_CreateFromRecItem(&pitem, pszBrf, pri);
  484. if (SUCCEEDED(hres))
  485. {
  486. // Is this a NOP?
  487. if (RAIA_NOTHING == pitem->uAction ||
  488. RAIA_ORPHAN == pitem->uAction)
  489. {
  490. // Yes; skip these guys altogether
  491. }
  492. else
  493. {
  494. pitem->mask |= RAIF_LPARAM;
  495. pitem->lParam = (LPARAM)pri;
  496. #ifndef NEW_REC
  497. // Has the file inside or outside the briefcase been deleted?
  498. if (SI_DELETED == pitem->siInside.uState ||
  499. SI_DELETED == pitem->siOutside.uState)
  500. {
  501. // Yes
  502. pupdcount->cTombstone++;
  503. }
  504. else
  505. #endif
  506. // Is this a file entry?
  507. if (IsFileRecItem(pri))
  508. {
  509. // Yes; add the item to the list.
  510. pitem->iItem = 0x7fff;
  511. DPA_InsertPtr(hdpa, DPA_APPEND, pitem);
  512. // Is this unavailable?
  513. if (RAIA_SKIP == pitem->uAction)
  514. {
  515. // Yes
  516. ASSERT(SI_UNAVAILABLE == pitem->siInside.uState ||
  517. SI_UNAVAILABLE == pitem->siOutside.uState ||
  518. SI_NOEXIST == pitem->siInside.uState ||
  519. SI_NOEXIST == pitem->siOutside.uState);
  520. pupdcount->cUnavailable++;
  521. }
  522. else if (RAIA_CONFLICT == pitem->uAction)
  523. {
  524. pupdcount->cConflict++;
  525. }
  526. else
  527. {
  528. pupdcount->cDoSomething++;
  529. }
  530. // (prevent pitem from being freed until
  531. // the dialog fills its list in Upd_FillList)
  532. pitem = NULL;
  533. }
  534. }
  535. RAI_Free(pitem);
  536. }
  537. }
  538. }
  539. }
  540. return hdpa;
  541. }
  542. /*----------------------------------------------------------
  543. Purpose: Displays a messagebox error specific to updating files
  544. Returns: id of button
  545. Cond: --
  546. */
  547. int PRIVATE DoUpdateMsg(
  548. HWND hwndOwner,
  549. LPCTSTR pszPath,
  550. UINT cFiles,
  551. UINT uFlags) // DUM_ flags
  552. {
  553. UINT ids;
  554. UINT idi;
  555. int idRet;
  556. // Is this for Update All?
  557. if (IsFlagSet(uFlags, DUM_ALL))
  558. {
  559. // Yes
  560. idi = IDI_UPDATE_MULT;
  561. if (IsFlagSet(uFlags, DUM_ORPHAN))
  562. {
  563. // In this case, pszPath should be the briefcase root
  564. ASSERT(pszPath);
  565. if (IsBriefcaseEmpty(pszPath))
  566. ids = IDS_MSG_NoFiles;
  567. else
  568. ids = IDS_MSG_AllOrphans;
  569. }
  570. else if (IsFlagSet(uFlags, DUM_UPTODATE))
  571. ids = IDS_MSG_AllUptodate;
  572. else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
  573. ids = IDS_MSG_AllSomeUnavailable;
  574. else
  575. {
  576. ASSERT(0); // should never get here
  577. ids = (UINT)-1;
  578. }
  579. idRet = MsgBox(hwndOwner,
  580. MAKEINTRESOURCE(ids),
  581. MAKEINTRESOURCE(IDS_CAP_UPDATE),
  582. LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
  583. MB_INFO);
  584. }
  585. else
  586. {
  587. // No
  588. TCHAR sz[MAX_PATH];
  589. ASSERT(0 != cFiles);
  590. ASSERT(pszPath);
  591. // Is this a single selection?
  592. if (1 == cFiles)
  593. {
  594. // Yes; assume it is a folder, then decrement the count
  595. // of the ids it is a file
  596. if (IsFlagSet(uFlags, DUM_ORPHAN))
  597. ids = IDS_MSG_FolderOrphan;
  598. else if (IsFlagSet(uFlags, DUM_UPTODATE))
  599. ids = IDS_MSG_FolderUptodate;
  600. else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
  601. ids = IDS_MSG_FolderUnavailable;
  602. else if (IsFlagSet(uFlags, DUM_SUBFOLDER_TWIN))
  603. ids = IDS_MSG_FolderSubfolder;
  604. else
  605. {
  606. ASSERT(0); // should never get here
  607. ids = (UINT)-1;
  608. }
  609. if (FALSE == PathIsDirectory(pszPath))
  610. {
  611. ASSERT(IsFlagClear(uFlags, DUM_SUBFOLDER_TWIN));
  612. ids--; // use file-oriented messages
  613. idi = IDI_UPDATE_FILE;
  614. }
  615. else
  616. {
  617. idi = IDI_UPDATE_FOLDER;
  618. }
  619. idRet = MsgBox(hwndOwner,
  620. MAKEINTRESOURCE(ids),
  621. MAKEINTRESOURCE(IDS_CAP_UPDATE),
  622. LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
  623. MB_INFO,
  624. PathGetDisplayName(pszPath, sz));
  625. }
  626. else
  627. {
  628. // No; multi selection
  629. idi = IDI_UPDATE_MULT;
  630. if (IsFlagSet(uFlags, DUM_UPTODATE))
  631. {
  632. if (IsFlagSet(uFlags, DUM_ORPHAN))
  633. ids = IDS_MSG_MultiUptodateOrphan;
  634. else
  635. ids = IDS_MSG_MultiUptodate;
  636. }
  637. else if (IsFlagSet(uFlags, DUM_ORPHAN))
  638. ids = IDS_MSG_MultiOrphans;
  639. else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
  640. ids = IDS_MSG_MultiUnavailable;
  641. else if (IsFlagSet(uFlags, DUM_SUBFOLDER_TWIN))
  642. ids = IDS_MSG_MultiSubfolder;
  643. else
  644. {
  645. ASSERT(0); // should never get here
  646. ids = (UINT)-1;
  647. }
  648. idRet = MsgBox(hwndOwner,
  649. MAKEINTRESOURCE(ids),
  650. MAKEINTRESOURCE(IDS_CAP_UPDATE),
  651. LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
  652. MB_INFO,
  653. cFiles);
  654. }
  655. }
  656. return idRet;
  657. }
  658. /*----------------------------------------------------------
  659. Purpose: This function does some preliminary checks to determine
  660. whether the dialog box needs to be invoked at all.
  661. Sets the cOrphans and cSubfolders fields of pupdcount.
  662. Returns: standard result
  663. Cond: --
  664. */
  665. HRESULT PRIVATE PrepForUpdateAll(
  666. PCBS pcbs,
  667. PRECLIST * pprl,
  668. UPDCOUNT * pupdcount,
  669. HWND hwndProgress)
  670. {
  671. HRESULT hres = E_FAIL;
  672. TWINRESULT tr;
  673. HWND hwndOwner = GetParent(hwndProgress);
  674. BOOL bAnyTwins;
  675. pupdcount->cSubfolders = 0;
  676. // Are there any twins in the database?
  677. tr = Sync_AnyTwins(pcbs->hbrf, &bAnyTwins);
  678. if (TR_SUCCESS == tr)
  679. {
  680. if (FALSE == bAnyTwins)
  681. {
  682. // No
  683. DoUpdateMsg(hwndOwner, Atom_GetName(pcbs->atomBrf), 1, DUM_ALL | DUM_ORPHAN);
  684. hres = S_FALSE;
  685. }
  686. // Can we get a fresh reclist?
  687. else
  688. {
  689. pupdcount->cOrphans = 0;
  690. hres = Sync_CreateCompleteRecList(pcbs->hbrf, UpdBar_GetAbortEvt(hwndProgress), pprl);
  691. if (FAILED(hres))
  692. {
  693. // No
  694. if (E_TR_ABORT != hres)
  695. {
  696. MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG),
  697. MAKEINTRESOURCE(IDS_CAP_UPDATE), NULL, MB_ERROR);
  698. }
  699. }
  700. else
  701. {
  702. // Yes
  703. if (*pprl)
  704. {
  705. hres = S_OK;
  706. }
  707. else
  708. {
  709. hres = E_UNEXPECTED;
  710. }
  711. // (reclist is freed inFinishUpdate())
  712. }
  713. }
  714. }
  715. return hres;
  716. }
  717. /*----------------------------------------------------------
  718. Purpose: This function does some preliminary checks to determine
  719. whether the dialog box needs to be invoked at all.
  720. Sets the cOrphans and cSubfolders fields of pupdcount.
  721. Returns: standard result
  722. Cond: --
  723. */
  724. HRESULT PRIVATE PrepForUpdateSelection(
  725. PCBS pcbs,
  726. PRECLIST * pprl,
  727. LPCTSTR pszList,
  728. UINT cFiles,
  729. UPDCOUNT * pupdcount,
  730. HWND hwndProgress)
  731. {
  732. HRESULT hres;
  733. TWINRESULT tr;
  734. HTWINLIST htl;
  735. HWND hwndOwner = GetParent(hwndProgress);
  736. pupdcount->cSubfolders = 0;
  737. // Create a twin list
  738. tr = Sync_CreateTwinList(pcbs->hbrf, &htl);
  739. if (TR_SUCCESS != tr)
  740. {
  741. // Failure
  742. MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
  743. NULL, MB_ERROR);
  744. hres = E_OUTOFMEMORY;
  745. }
  746. else
  747. {
  748. LPCTSTR psz;
  749. UINT cOrphans = 0;
  750. UINT cSubfolders = 0;
  751. UINT i;
  752. for (i = 0, psz = pszList; i < cFiles; i++)
  753. {
  754. // Is this object really a twin?
  755. if (S_FALSE == Sync_IsTwin(pcbs->hbrf, psz, 0) )
  756. {
  757. // No; is this a subfolder twin?
  758. if (IsSubfolderTwin(pcbs->hbrf, psz))
  759. {
  760. // Yes
  761. cSubfolders++;
  762. }
  763. else
  764. {
  765. // No
  766. cOrphans++;
  767. }
  768. }
  769. else
  770. {
  771. // Yes; add it to the twin list
  772. Sync_AddPathToTwinList(pcbs->hbrf, htl, psz, NULL);
  773. }
  774. DataObj_NextFile(psz); // Set psz to next file in list
  775. }
  776. // Are all the selected objects orphans?
  777. if (cOrphans < cFiles)
  778. {
  779. // No; create the reclist
  780. hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), pprl);
  781. }
  782. else
  783. {
  784. // Yes
  785. DoUpdateMsg(hwndOwner, pszList, cFiles, DUM_SELECTION | DUM_ORPHAN);
  786. hres = S_FALSE;
  787. }
  788. pupdcount->cOrphans = cOrphans;
  789. pupdcount->cSubfolders = cSubfolders;
  790. Sync_DestroyTwinList(htl); // Don't need this anymore
  791. }
  792. return hres;
  793. }
  794. /*----------------------------------------------------------
  795. Purpose: Checks for the special cases that are listed at the top
  796. of this file.
  797. Returns: PSC_ flags
  798. Cond: --
  799. */
  800. UINT PRIVATE PassedSpecialCases(
  801. HWND hwndOwner,
  802. LPCTSTR pszList,
  803. UPDCOUNT * pupdcount,
  804. UINT uFlags) // UF_ flags
  805. {
  806. UINT uRet = 0;
  807. UINT dum = 0;
  808. UINT cSomeAction = pupdcount->cDoSomething + pupdcount->cConflict;
  809. // Is this Update All?
  810. if (IsFlagSet(uFlags, UF_ALL))
  811. {
  812. // Yes
  813. if (0 < pupdcount->cOrphans)
  814. {
  815. // Case A2
  816. dum = DUM_ALL | DUM_ORPHAN;
  817. }
  818. else if (0 == pupdcount->cUnavailable)
  819. {
  820. if (0 == cSomeAction)
  821. {
  822. // Case A3
  823. dum = DUM_ALL | DUM_UPTODATE;
  824. }
  825. else
  826. {
  827. // Case A4
  828. uRet = PSC_SHOWDIALOG;
  829. }
  830. }
  831. else
  832. {
  833. // Cases A5 and A6
  834. dum = DUM_ALL | DUM_UNAVAILABLE;
  835. uRet = PSC_SHOWDIALOG;
  836. }
  837. #ifdef DEBUG
  838. if (IsFlagSet(g_uDumpFlags, DF_UPDATECOUNT))
  839. {
  840. TRACE_MSG(TF_ALWAYS, TEXT("Update All counts: files = %u, orphans = %u, unavailable = %u, dosomething = %u, conflict = %u, subfolders = %u"),
  841. pupdcount->cFiles, pupdcount->cOrphans,
  842. pupdcount->cUnavailable, pupdcount->cDoSomething,
  843. pupdcount->cConflict, pupdcount->cSubfolders);
  844. }
  845. #endif
  846. }
  847. else
  848. {
  849. // No; single selection?
  850. // Take caution in the comparisons below. The counts do not
  851. // have a 1-to-1 correspondence. They are split into two
  852. // groups: cFiles <---> cOrphans <---> cSubfolders
  853. // cUnavailable <---> cDoSomething
  854. //
  855. // This means comparing cFiles with cDoSomething or cUnavailable
  856. // will produce bogus results in the case when folders are
  857. // selected.
  858. //
  859. // As long as the comparisons below do not break these limits,
  860. // everything is okay.
  861. if (1 == pupdcount->cFiles)
  862. {
  863. // Yes
  864. ASSERT(2 > pupdcount->cOrphans);
  865. ASSERT(2 > pupdcount->cSubfolders);
  866. if (1 == pupdcount->cOrphans)
  867. {
  868. // Case S1
  869. dum = DUM_SELECTION | DUM_ORPHAN;
  870. }
  871. else if (0 == pupdcount->cUnavailable)
  872. {
  873. if (0 == cSomeAction)
  874. {
  875. if (0 == pupdcount->cSubfolders)
  876. {
  877. // Case S2
  878. dum = DUM_SELECTION | DUM_UPTODATE;
  879. }
  880. else
  881. {
  882. dum = DUM_SELECTION | DUM_SUBFOLDER_TWIN;
  883. }
  884. }
  885. else
  886. {
  887. // Case S3
  888. uRet = PSC_SHOWDIALOG;
  889. }
  890. }
  891. else
  892. {
  893. // Case S4
  894. dum = DUM_SELECTION | DUM_UNAVAILABLE;
  895. uRet = PSC_SHOWDIALOG;
  896. }
  897. }
  898. else
  899. {
  900. // No; this is a multi selection
  901. if (0 < pupdcount->cSubfolders)
  902. {
  903. DoUpdateMsg(hwndOwner, pszList, pupdcount->cSubfolders, DUM_SELECTION | DUM_SUBFOLDER_TWIN);
  904. goto Leave; // HACK
  905. }
  906. if (pupdcount->cFiles == pupdcount->cOrphans)
  907. {
  908. // Case S5
  909. dum = DUM_SELECTION | DUM_ORPHAN;
  910. }
  911. else if (0 < pupdcount->cUnavailable)
  912. {
  913. if (0 < cSomeAction)
  914. {
  915. if (0 == pupdcount->cOrphans)
  916. {
  917. // Case S6
  918. dum = DUM_SELECTION | DUM_UNAVAILABLE;
  919. uRet = PSC_SHOWDIALOG;
  920. }
  921. else
  922. {
  923. // Case S7
  924. dum = DUM_SELECTION | DUM_UNAVAILABLE;
  925. uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
  926. }
  927. }
  928. else
  929. {
  930. if (0 == pupdcount->cOrphans)
  931. {
  932. // Case S8
  933. dum = DUM_SELECTION | DUM_UNAVAILABLE;
  934. uRet = PSC_SHOWDIALOG;
  935. }
  936. else
  937. {
  938. // Case S9
  939. dum = DUM_SELECTION | DUM_UNAVAILABLE;
  940. uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
  941. }
  942. }
  943. }
  944. else
  945. {
  946. if (0 < cSomeAction)
  947. {
  948. if (0 == pupdcount->cOrphans)
  949. {
  950. // Case S10
  951. uRet = PSC_SHOWDIALOG;
  952. }
  953. else
  954. {
  955. // Case S11
  956. uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
  957. }
  958. }
  959. else
  960. {
  961. if (0 == pupdcount->cOrphans)
  962. {
  963. // Case S12
  964. dum = DUM_SELECTION | DUM_UPTODATE;
  965. }
  966. else
  967. {
  968. // Case S13
  969. dum = DUM_SELECTION | DUM_UPTODATE | DUM_ORPHAN;
  970. }
  971. }
  972. }
  973. }
  974. Leave:
  975. ;
  976. #ifdef DEBUG
  977. if (IsFlagSet(g_uDumpFlags, DF_UPDATECOUNT))
  978. {
  979. TRACE_MSG(TF_ALWAYS, TEXT("Update selection counts: files = %u, orphans = %u, unavailable = %u, dosomething = %u, conflict = %u, subfolders = %u"),
  980. pupdcount->cFiles, pupdcount->cOrphans,
  981. pupdcount->cUnavailable, pupdcount->cDoSomething,
  982. pupdcount->cConflict, pupdcount->cSubfolders);
  983. }
  984. #endif
  985. }
  986. if (0 != dum)
  987. {
  988. DoUpdateMsg(hwndOwner, pszList, pupdcount->cFiles, dum);
  989. }
  990. return uRet;
  991. }
  992. /*----------------------------------------------------------
  993. Purpose: Show the update dialog and perform the reconcilation
  994. if the user chooses OK
  995. Returns: standard result
  996. Cond: --
  997. */
  998. HRESULT PUBLIC Upd_DoModal(
  999. HWND hwndOwner,
  1000. CBS * pcbs,
  1001. LPCTSTR pszList, // May be NULL if uFlags == UF_ALL
  1002. UINT cFiles,
  1003. UINT uFlags)
  1004. {
  1005. INT_PTR nRet;
  1006. HRESULT hres;
  1007. PRECLIST prl;
  1008. UPDCOUNT updcount;
  1009. HWND hwndProgress;
  1010. hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING, DELAY_UPDBAR);
  1011. // Get a reclist and other useful information
  1012. updcount.cFiles = cFiles;
  1013. if (IsFlagSet(uFlags, UF_ALL))
  1014. {
  1015. hres = PrepForUpdateAll(pcbs, &prl, &updcount, hwndProgress);
  1016. }
  1017. else
  1018. {
  1019. hres = PrepForUpdateSelection(pcbs, &prl, pszList, cFiles, &updcount, hwndProgress);
  1020. }
  1021. UpdBar_Kill(hwndProgress);
  1022. if (S_OK == GetScode(hres))
  1023. {
  1024. XUPDSTRUCT xupd;
  1025. xupd.lprl = prl;
  1026. xupd.pcbs = pcbs;
  1027. xupd.uFlags = uFlags;
  1028. xupd.hdpa = ComposeUpdateList(pcbs, prl, &updcount, hwndOwner);
  1029. xupd.cDoSomething = updcount.cDoSomething;
  1030. if (NULL == xupd.hdpa)
  1031. {
  1032. hres = E_OUTOFMEMORY;
  1033. MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
  1034. NULL, MB_ERROR);
  1035. }
  1036. else
  1037. {
  1038. // Check for some of those special cases listed at top of file
  1039. UINT uVal = PassedSpecialCases(hwndOwner, pszList, &updcount, uFlags);
  1040. // Show the update dialog?
  1041. if (IsFlagSet(uVal, PSC_SHOWDIALOG))
  1042. {
  1043. // Yes
  1044. nRet = DoModal(hwndOwner, Upd_WrapperProc, IDD_UPDATE, (LPARAM)&xupd);
  1045. switch (nRet)
  1046. {
  1047. case IDOK:
  1048. // Reconcile!
  1049. hwndProgress = UpdBar_Show(hwndOwner, UB_UPDATING, 0);
  1050. Sync_ReconcileRecList(prl, Atom_GetName(pcbs->atomBrf),
  1051. hwndProgress, RF_DEFAULT);
  1052. UpdBar_Kill(hwndProgress);
  1053. // Show a summary messagebox?
  1054. if (IsFlagSet(uVal, PSC_POSTMSGBOX))
  1055. {
  1056. // Yes
  1057. DoUpdateMsg(hwndOwner, pszList, updcount.cOrphans, DUM_SELECTION | DUM_ORPHAN);
  1058. }
  1059. // Fall thru
  1060. // | |
  1061. // v v
  1062. case IDCANCEL:
  1063. hres = NOERROR;
  1064. break;
  1065. case -1:
  1066. MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
  1067. NULL, MB_ERROR);
  1068. hres = E_OUTOFMEMORY;
  1069. break;
  1070. default:
  1071. ASSERT(0);
  1072. break;
  1073. }
  1074. }
  1075. DPA_Destroy(xupd.hdpa);
  1076. }
  1077. Sync_DestroyRecList(prl);
  1078. }
  1079. return hres;
  1080. }