windows-nt/Source/XPSP1/NT/shell/ext/brfcase/filesync/syncui/update.c
2020-09-26 16:20:57 +08:00

1248 lines
36 KiB
C

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: update.c
//
// This files contains code for the Update UI and dialog.
//
// History:
// 08-17-93 ScottH Created.
//
//---------------------------------------------------------------------------
#include "brfprv.h" // common headers
#include "res.h"
#include "recact.h"
#include <help.h>
// This structure contains all the important counts
// that determine the specific course of action when
// the user wants to update something.
typedef struct
{
// These are 1 to 1
UINT cFiles;
UINT cOrphans;
UINT cSubfolders;
// These are 1 to 1
UINT cUnavailable;
UINT cDoSomething;
UINT cConflict;
UINT cTombstone;
} UPDCOUNT;
// This is the structure passed to the dialog at WM_INITDIALOG
typedef struct
{
PRECLIST lprl; // Supplied reclist
CBS * pcbs;
UINT uFlags; // UF_ Flags
HDPA hdpa; // List of RA_ITEMs
UINT cDoSomething;
} XUPDSTRUCT, * LPXUPDSTRUCT;
typedef struct tagUPD
{
HWND hwnd; // dialog handle
LPXUPDSTRUCT pxupd;
} UPD, * PUPD;
#define Upd_Prl(this) ((this)->pxupd->lprl)
#define Upd_AtomBrf(this) ((this)->pxupd->pcbs->atomBrf)
#define Upd_GetBrfPtr(this) Atom_GetName(Upd_AtomBrf(this))
#define Upd_GetPtr(hwnd) (PUPD)GetWindowLongPtr(hwnd, DWLP_USER)
#define Upd_SetPtr(hwnd, lp) (PUPD)SetWindowLongPtr(hwnd, DWLP_USER, (LRESULT)(lp))
// These flags are used for DoUpdateMsg
#define DUM_ALL 0x0001
#define DUM_SELECTION 0x0002
#define DUM_ORPHAN 0x0004
#define DUM_UPTODATE 0x0008
#define DUM_UNAVAILABLE 0x0010
#define DUM_SUBFOLDER_TWIN 0x0020
// These flags are returned by PassedSpecialCases
#define PSC_SHOWDIALOG 0x0001
#define PSC_POSTMSGBOX 0x0002
//---------------------------------------------------------------------------
// Some comments
//---------------------------------------------------------------------------
// There are several special cases and conditions which must be
// handled. Lets break them down now. The case numbers on on the far
// right, and are referenced in comments thru out this file.
//
// There are two user actions: Update All and Update Selection.
//
// Update All:
//
// Case What to do
// ----------------- --------------
// A1. * No files in the briefcase ---> MB (messagebox)
// A2. * All files are orphans ---> MB
// * Some files are twins and...
// * they are all available and...
// A3. * they are all up-to-date ---> MB
// A4. * some of them need updating ---> Update dialog
// * some are unavailable and...
// A5. * the available ones are up-to-date ---> MB then Update
// A6. * some need updating ---> MB then Update
//
//
// Update Selection:
//
// Case What to do
// ----------------- --------------
// * Single selection and...
// S1. * is an orphan ---> MB
// * is available and...
// S2. * up-to-date ---> MB
// S3. * needs updating ---> Update
// S4. * is unavailable ---> MB then Update
// * Multi selection and...
// S5. * all are orphans ---> MB
// * some (non-orphans) are unavailable and...
// * some need updating and...
// S6. * none are orphans ---> MB then Update
// S7. * some are orphans ---> MB then Update then MB
// * the available ones are up-to-date and...
// S8. * none are orphans ---> MB then Update
// S9. * some are orphans ---> MB then Update then MB
// * all (non-orphans) are available and...
// * some need updating and...
// S10. * none are orphans ---> Update
// S11. * some are orphans ---> Update then MB
// * all up-to-date and...
// S12. * none are orphans ---> MB
// S13. * some are orphans ---> MB
//---------------------------------------------------------------------------
// Dialog code
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Fill the reconciliation action listbox
Returns: TRUE on success
Cond: --
*/
BOOL PRIVATE Upd_FillList(
PUPD this)
{
HWND hwndCtl = GetDlgItem(this->hwnd, IDC_UPDATEACTIONS);
HDPA hdpa = this->pxupd->hdpa;
int cItems;
int i;
cItems = DPA_GetPtrCount(hdpa);
for (i = 0; i < cItems; i++)
{
RA_ITEM * pitem = DPA_FastGetPtr(hdpa, i);
RecAct_InsertItem(hwndCtl, pitem);
RAI_Free(pitem);
}
return TRUE;
}
/*----------------------------------------------------------
Purpose: Sets the Update and Cancel buttons according to the
bDisableUpdate parameter.
Returns: --
Cond: --
*/
void PRIVATE Upd_SetExitButtons(
PUPD this,
BOOL bDisableUpdate)
{
HWND hwndOK = GetDlgItem(this->hwnd, IDOK);
// Disable the update button?
if (bDisableUpdate)
{
// Yes
if (GetFocus() == hwndOK)
{
SetFocus(GetDlgItem(this->hwnd, IDCANCEL));
}
Button_Enable(hwndOK, FALSE);
}
else
{
// No
Button_Enable(hwndOK, TRUE);
}
}
/*----------------------------------------------------------
Purpose: WM_INITDIALOG Handler
Returns:
Cond: --
*/
BOOL PRIVATE Upd_OnInitDialog(
PUPD this,
HWND hwndFocus,
LPARAM lParam)
{
HWND hwnd = this->hwnd;
TCHAR szFmt[MAXBUFLEN];
TCHAR sz[MAXMSGLEN];
ASSERT(lParam != 0L);
this->pxupd = (LPXUPDSTRUCT)lParam;
if (Upd_FillList(this))
{
// Set the title caption
wsprintf(sz, SzFromIDS(IDS_CAP_UpdateFmt, szFmt, ARRAYSIZE(szFmt)),
PathFindFileName(Upd_GetBrfPtr(this)));
SetWindowText(hwnd, sz);
// Do any files need updating?
if (0 == this->pxupd->cDoSomething)
{
// No
Upd_SetExitButtons(this, TRUE);
}
}
else
{
// Failed
EndDialog(hwnd, -1);
}
return(TRUE);
}
/*----------------------------------------------------------
Purpose: Handle RN_ITEMCHANGED
Returns: --
Cond: --
*/
void PRIVATE Upd_HandleItemChange(
PUPD this,
NM_RECACT * lpnm)
{
PRECITEM lpri;
ASSERT((lpnm->mask & RAIF_LPARAM) != 0);
lpri = (PRECITEM)lpnm->lParam;
// The action has changed, update the recnode accordingly
if (lpnm->mask & RAIF_ACTION)
{
LPCTSTR pszDir = Upd_GetBrfPtr(this);
Sync_ChangeRecItemAction(lpri, pszDir, pszDir, lpnm->uAction);
switch (lpnm->uActionOld)
{
case RAIA_TOOUT:
case RAIA_TOIN:
case RAIA_MERGE:
// Is this a change from "do something" to "skip"?
if (RAIA_SKIP == lpnm->uAction)
{
// Yes
ASSERT(0 < this->pxupd->cDoSomething);
this->pxupd->cDoSomething--;
}
break;
case RAIA_SKIP:
case RAIA_CONFLICT:
// Is this a change from "skip"/"conflict" to "do something"?
if (RAIA_TOOUT == lpnm->uAction ||
RAIA_TOIN == lpnm->uAction ||
RAIA_MERGE == lpnm->uAction)
{
// Yes
this->pxupd->cDoSomething++;
}
break;
}
Upd_SetExitButtons(this, 0 == this->pxupd->cDoSomething);
}
}
/*----------------------------------------------------------
Purpose: WM_NOTIFY handler
Returns: varies
Cond: --
*/
LRESULT PRIVATE Upd_OnNotify(
PUPD this,
int idFrom,
NMHDR * lpnmhdr)
{
LRESULT lRet = 0;
switch (lpnmhdr->code)
{
case RN_ITEMCHANGED:
Upd_HandleItemChange(this, (NM_RECACT *)lpnmhdr);
break;
default:
break;
}
return lRet;
}
/*----------------------------------------------------------
Purpose: Info WM_COMMAND Handler
Returns: --
Cond: --
*/
VOID PRIVATE Upd_OnCommand(
PUPD this,
int id,
HWND hwndCtl,
UINT uNotifyCode)
{
HWND hwnd = this->hwnd;
switch (id)
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, id);
break;
}
}
/*----------------------------------------------------------
Purpose: WM_DESTROY handler
Returns: --
Cond: --
*/
void PRIVATE Upd_OnDestroy(
PUPD this)
{
}
static BOOL s_bUpdRecurse = FALSE;
LRESULT INLINE Upd_DefProc(
HWND hDlg,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
ENTEREXCLUSIVE();
{
s_bUpdRecurse = TRUE;
}
LEAVEEXCLUSIVE();
return DefDlgProc(hDlg, msg, wParam, lParam);
}
/*----------------------------------------------------------
Purpose: Real Create Folder Twin dialog proc
Returns: varies
Cond: --
*/
LRESULT Upd_DlgProc(
PUPD this,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
const static DWORD rgHelpIDs[] = {
IDC_UPDATEACTIONS, IDH_BFC_UPDATE_SCREEN, // different
IDOK, IDH_BFC_UPDATE_BUTTON,
0, 0 };
switch (message)
{
HANDLE_MSG(this, WM_INITDIALOG, Upd_OnInitDialog);
HANDLE_MSG(this, WM_COMMAND, Upd_OnCommand);
HANDLE_MSG(this, WM_NOTIFY, Upd_OnNotify);
HANDLE_MSG(this, WM_DESTROY, Upd_OnDestroy);
case WM_HELP:
WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPVOID)rgHelpIDs);
return 0;
case WM_CONTEXTMENU:
WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPVOID)rgHelpIDs);
return 0;
default:
return Upd_DefProc(this->hwnd, message, wParam, lParam);
}
}
/*----------------------------------------------------------
Purpose: Create Folder Twin Dialog Wrapper
Returns: varies
Cond: --
*/
INT_PTR _export CALLBACK Upd_WrapperProc(
HWND hDlg, // std params
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PUPD this;
// Cool windowsx.h dialog technique. For full explanation, see
// WINDOWSX.TXT. This supports multiple-instancing of dialogs.
//
ENTEREXCLUSIVE();
{
if (s_bUpdRecurse)
{
s_bUpdRecurse = FALSE;
LEAVEEXCLUSIVE();
return FALSE;
}
}
LEAVEEXCLUSIVE();
this = Upd_GetPtr(hDlg);
if (this == NULL)
{
if (message == WM_INITDIALOG)
{
this = GAlloc(sizeof(*this));
if (!this)
{
MsgBox(hDlg, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
NULL, MB_ERROR);
EndDialog(hDlg, IDCANCEL);
return Upd_DefProc(hDlg, message, wParam, lParam);
}
this->hwnd = hDlg;
Upd_SetPtr(hDlg, this);
}
else
{
return Upd_DefProc(hDlg, message, wParam, lParam);
}
}
if (message == WM_DESTROY)
{
Upd_DlgProc(this, message, wParam, lParam);
GFree(this);
Upd_SetPtr(hDlg, NULL);
return 0;
}
return SetDlgMsgResult(hDlg, message, Upd_DlgProc(this, message, wParam, lParam));
}
//---------------------------------------------------------------------------
// Update detection code
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Checks if the briefcase is empty. This function skips the
"desktop.ini" and "Briefcase Database" files.
Returns: TRUE if the briefcase is empty
Cond: --
*/
BOOL PRIVATE IsBriefcaseEmpty(
LPCTSTR pszPath)
{
BOOL bRet = FALSE;
ASSERT(pszPath);
if (pszPath)
{
// Enumerate thru folder
TCHAR szSearch[MAXPATHLEN];
WIN32_FIND_DATA fd;
HANDLE hfile;
// This must be per instance, else it will cause a fixup in
// shared data segment.
const static LPCTSTR s_rgszIgnore[] = { TEXT("."), TEXT(".."), g_szDBName, g_szDBNameShort, c_szDesktopIni };
PathCombine(szSearch, pszPath, TEXT("*.*"));
hfile = FindFirstFile(szSearch, &fd);
if (INVALID_HANDLE_VALUE != hfile)
{
BOOL bCont = TRUE;
bRet = TRUE; // Default to empty folder
while (bCont)
{
int bIgnore = FALSE;
int i;
// Is this file one of the files to ignore?
for (i = 0; i < ARRAYSIZE(s_rgszIgnore); i++)
{
if (IsSzEqual(fd.cFileName, s_rgszIgnore[i]))
{
// Yes
bIgnore = TRUE;
break;
}
}
// Is this a valid file/folder?
if (FALSE == bIgnore)
{
// Yes; return the briefcase is not empty
bRet = FALSE;
bCont = FALSE; // stop the enumeration
}
else
{
bCont = FindNextFile(hfile, &fd);
}
}
FindClose(hfile);
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: Create a DSA of RA_ITEMs
Sets the cDoSomething, cUnavailable, cConflict and
cTombstone fields of pupdcount.
Returns: TRUE on success
Cond: --
*/
HDPA PRIVATE ComposeUpdateList(
PCBS pcbs,
PRECLIST prl,
UPDCOUNT * pupdcount,
HWND hwndOwner)
{
HRESULT hres;
HDPA hdpa;
ASSERT(prl);
hdpa = DPA_Create(20);
if (NULL != hdpa)
{
LPRA_ITEM pitem;
PRECITEM pri;
LPCTSTR pszBrf = Atom_GetName(pcbs->atomBrf);
if (pszBrf)
{
DEBUG_CODE( Sync_DumpRecList(TR_SUCCESS, prl, TEXT("ComposeUpdateList")); )
pupdcount->cUnavailable = 0;
pupdcount->cDoSomething = 0;
pupdcount->cTombstone = 0;
pupdcount->cConflict = 0;
for (pri = prl->priFirst; pri; pri = pri->priNext)
{
hres = RAI_CreateFromRecItem(&pitem, pszBrf, pri);
if (SUCCEEDED(hres))
{
// Is this a NOP?
if (RAIA_NOTHING == pitem->uAction ||
RAIA_ORPHAN == pitem->uAction)
{
// Yes; skip these guys altogether
}
else
{
pitem->mask |= RAIF_LPARAM;
pitem->lParam = (LPARAM)pri;
#ifndef NEW_REC
// Has the file inside or outside the briefcase been deleted?
if (SI_DELETED == pitem->siInside.uState ||
SI_DELETED == pitem->siOutside.uState)
{
// Yes
pupdcount->cTombstone++;
}
else
#endif
// Is this a file entry?
if (IsFileRecItem(pri))
{
// Yes; add the item to the list.
pitem->iItem = 0x7fff;
DPA_InsertPtr(hdpa, DPA_APPEND, pitem);
// Is this unavailable?
if (RAIA_SKIP == pitem->uAction)
{
// Yes
ASSERT(SI_UNAVAILABLE == pitem->siInside.uState ||
SI_UNAVAILABLE == pitem->siOutside.uState ||
SI_NOEXIST == pitem->siInside.uState ||
SI_NOEXIST == pitem->siOutside.uState);
pupdcount->cUnavailable++;
}
else if (RAIA_CONFLICT == pitem->uAction)
{
pupdcount->cConflict++;
}
else
{
pupdcount->cDoSomething++;
}
// (prevent pitem from being freed until
// the dialog fills its list in Upd_FillList)
pitem = NULL;
}
}
RAI_Free(pitem);
}
}
}
}
return hdpa;
}
/*----------------------------------------------------------
Purpose: Displays a messagebox error specific to updating files
Returns: id of button
Cond: --
*/
int PRIVATE DoUpdateMsg(
HWND hwndOwner,
LPCTSTR pszPath,
UINT cFiles,
UINT uFlags) // DUM_ flags
{
UINT ids;
UINT idi;
int idRet;
// Is this for Update All?
if (IsFlagSet(uFlags, DUM_ALL))
{
// Yes
idi = IDI_UPDATE_MULT;
if (IsFlagSet(uFlags, DUM_ORPHAN))
{
// In this case, pszPath should be the briefcase root
ASSERT(pszPath);
if (IsBriefcaseEmpty(pszPath))
ids = IDS_MSG_NoFiles;
else
ids = IDS_MSG_AllOrphans;
}
else if (IsFlagSet(uFlags, DUM_UPTODATE))
ids = IDS_MSG_AllUptodate;
else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
ids = IDS_MSG_AllSomeUnavailable;
else
{
ASSERT(0); // should never get here
ids = (UINT)-1;
}
idRet = MsgBox(hwndOwner,
MAKEINTRESOURCE(ids),
MAKEINTRESOURCE(IDS_CAP_UPDATE),
LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
MB_INFO);
}
else
{
// No
TCHAR sz[MAX_PATH];
ASSERT(0 != cFiles);
ASSERT(pszPath);
// Is this a single selection?
if (1 == cFiles)
{
// Yes; assume it is a folder, then decrement the count
// of the ids it is a file
if (IsFlagSet(uFlags, DUM_ORPHAN))
ids = IDS_MSG_FolderOrphan;
else if (IsFlagSet(uFlags, DUM_UPTODATE))
ids = IDS_MSG_FolderUptodate;
else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
ids = IDS_MSG_FolderUnavailable;
else if (IsFlagSet(uFlags, DUM_SUBFOLDER_TWIN))
ids = IDS_MSG_FolderSubfolder;
else
{
ASSERT(0); // should never get here
ids = (UINT)-1;
}
if (FALSE == PathIsDirectory(pszPath))
{
ASSERT(IsFlagClear(uFlags, DUM_SUBFOLDER_TWIN));
ids--; // use file-oriented messages
idi = IDI_UPDATE_FILE;
}
else
{
idi = IDI_UPDATE_FOLDER;
}
idRet = MsgBox(hwndOwner,
MAKEINTRESOURCE(ids),
MAKEINTRESOURCE(IDS_CAP_UPDATE),
LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
MB_INFO,
PathGetDisplayName(pszPath, sz));
}
else
{
// No; multi selection
idi = IDI_UPDATE_MULT;
if (IsFlagSet(uFlags, DUM_UPTODATE))
{
if (IsFlagSet(uFlags, DUM_ORPHAN))
ids = IDS_MSG_MultiUptodateOrphan;
else
ids = IDS_MSG_MultiUptodate;
}
else if (IsFlagSet(uFlags, DUM_ORPHAN))
ids = IDS_MSG_MultiOrphans;
else if (IsFlagSet(uFlags, DUM_UNAVAILABLE))
ids = IDS_MSG_MultiUnavailable;
else if (IsFlagSet(uFlags, DUM_SUBFOLDER_TWIN))
ids = IDS_MSG_MultiSubfolder;
else
{
ASSERT(0); // should never get here
ids = (UINT)-1;
}
idRet = MsgBox(hwndOwner,
MAKEINTRESOURCE(ids),
MAKEINTRESOURCE(IDS_CAP_UPDATE),
LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
MB_INFO,
cFiles);
}
}
return idRet;
}
/*----------------------------------------------------------
Purpose: This function does some preliminary checks to determine
whether the dialog box needs to be invoked at all.
Sets the cOrphans and cSubfolders fields of pupdcount.
Returns: standard result
Cond: --
*/
HRESULT PRIVATE PrepForUpdateAll(
PCBS pcbs,
PRECLIST * pprl,
UPDCOUNT * pupdcount,
HWND hwndProgress)
{
HRESULT hres = E_FAIL;
TWINRESULT tr;
HWND hwndOwner = GetParent(hwndProgress);
BOOL bAnyTwins;
pupdcount->cSubfolders = 0;
// Are there any twins in the database?
tr = Sync_AnyTwins(pcbs->hbrf, &bAnyTwins);
if (TR_SUCCESS == tr)
{
if (FALSE == bAnyTwins)
{
// No
DoUpdateMsg(hwndOwner, Atom_GetName(pcbs->atomBrf), 1, DUM_ALL | DUM_ORPHAN);
hres = S_FALSE;
}
// Can we get a fresh reclist?
else
{
pupdcount->cOrphans = 0;
hres = Sync_CreateCompleteRecList(pcbs->hbrf, UpdBar_GetAbortEvt(hwndProgress), pprl);
if (FAILED(hres))
{
// No
if (E_TR_ABORT != hres)
{
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG),
MAKEINTRESOURCE(IDS_CAP_UPDATE), NULL, MB_ERROR);
}
}
else
{
// Yes
if (*pprl)
{
hres = S_OK;
}
else
{
hres = E_UNEXPECTED;
}
// (reclist is freed inFinishUpdate())
}
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: This function does some preliminary checks to determine
whether the dialog box needs to be invoked at all.
Sets the cOrphans and cSubfolders fields of pupdcount.
Returns: standard result
Cond: --
*/
HRESULT PRIVATE PrepForUpdateSelection(
PCBS pcbs,
PRECLIST * pprl,
LPCTSTR pszList,
UINT cFiles,
UPDCOUNT * pupdcount,
HWND hwndProgress)
{
HRESULT hres;
TWINRESULT tr;
HTWINLIST htl;
HWND hwndOwner = GetParent(hwndProgress);
pupdcount->cSubfolders = 0;
// Create a twin list
tr = Sync_CreateTwinList(pcbs->hbrf, &htl);
if (TR_SUCCESS != tr)
{
// Failure
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
NULL, MB_ERROR);
hres = E_OUTOFMEMORY;
}
else
{
LPCTSTR psz;
UINT cOrphans = 0;
UINT cSubfolders = 0;
UINT i;
for (i = 0, psz = pszList; i < cFiles; i++)
{
// Is this object really a twin?
if (S_FALSE == Sync_IsTwin(pcbs->hbrf, psz, 0) )
{
// No; is this a subfolder twin?
if (IsSubfolderTwin(pcbs->hbrf, psz))
{
// Yes
cSubfolders++;
}
else
{
// No
cOrphans++;
}
}
else
{
// Yes; add it to the twin list
Sync_AddPathToTwinList(pcbs->hbrf, htl, psz, NULL);
}
DataObj_NextFile(psz); // Set psz to next file in list
}
// Are all the selected objects orphans?
if (cOrphans < cFiles)
{
// No; create the reclist
hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), pprl);
}
else
{
// Yes
DoUpdateMsg(hwndOwner, pszList, cFiles, DUM_SELECTION | DUM_ORPHAN);
hres = S_FALSE;
}
pupdcount->cOrphans = cOrphans;
pupdcount->cSubfolders = cSubfolders;
Sync_DestroyTwinList(htl); // Don't need this anymore
}
return hres;
}
/*----------------------------------------------------------
Purpose: Checks for the special cases that are listed at the top
of this file.
Returns: PSC_ flags
Cond: --
*/
UINT PRIVATE PassedSpecialCases(
HWND hwndOwner,
LPCTSTR pszList,
UPDCOUNT * pupdcount,
UINT uFlags) // UF_ flags
{
UINT uRet = 0;
UINT dum = 0;
UINT cSomeAction = pupdcount->cDoSomething + pupdcount->cConflict;
// Is this Update All?
if (IsFlagSet(uFlags, UF_ALL))
{
// Yes
if (0 < pupdcount->cOrphans)
{
// Case A2
dum = DUM_ALL | DUM_ORPHAN;
}
else if (0 == pupdcount->cUnavailable)
{
if (0 == cSomeAction)
{
// Case A3
dum = DUM_ALL | DUM_UPTODATE;
}
else
{
// Case A4
uRet = PSC_SHOWDIALOG;
}
}
else
{
// Cases A5 and A6
dum = DUM_ALL | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG;
}
#ifdef DEBUG
if (IsFlagSet(g_uDumpFlags, DF_UPDATECOUNT))
{
TRACE_MSG(TF_ALWAYS, TEXT("Update All counts: files = %u, orphans = %u, unavailable = %u, dosomething = %u, conflict = %u, subfolders = %u"),
pupdcount->cFiles, pupdcount->cOrphans,
pupdcount->cUnavailable, pupdcount->cDoSomething,
pupdcount->cConflict, pupdcount->cSubfolders);
}
#endif
}
else
{
// No; single selection?
// Take caution in the comparisons below. The counts do not
// have a 1-to-1 correspondence. They are split into two
// groups: cFiles <---> cOrphans <---> cSubfolders
// cUnavailable <---> cDoSomething
//
// This means comparing cFiles with cDoSomething or cUnavailable
// will produce bogus results in the case when folders are
// selected.
//
// As long as the comparisons below do not break these limits,
// everything is okay.
if (1 == pupdcount->cFiles)
{
// Yes
ASSERT(2 > pupdcount->cOrphans);
ASSERT(2 > pupdcount->cSubfolders);
if (1 == pupdcount->cOrphans)
{
// Case S1
dum = DUM_SELECTION | DUM_ORPHAN;
}
else if (0 == pupdcount->cUnavailable)
{
if (0 == cSomeAction)
{
if (0 == pupdcount->cSubfolders)
{
// Case S2
dum = DUM_SELECTION | DUM_UPTODATE;
}
else
{
dum = DUM_SELECTION | DUM_SUBFOLDER_TWIN;
}
}
else
{
// Case S3
uRet = PSC_SHOWDIALOG;
}
}
else
{
// Case S4
dum = DUM_SELECTION | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG;
}
}
else
{
// No; this is a multi selection
if (0 < pupdcount->cSubfolders)
{
DoUpdateMsg(hwndOwner, pszList, pupdcount->cSubfolders, DUM_SELECTION | DUM_SUBFOLDER_TWIN);
goto Leave; // HACK
}
if (pupdcount->cFiles == pupdcount->cOrphans)
{
// Case S5
dum = DUM_SELECTION | DUM_ORPHAN;
}
else if (0 < pupdcount->cUnavailable)
{
if (0 < cSomeAction)
{
if (0 == pupdcount->cOrphans)
{
// Case S6
dum = DUM_SELECTION | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG;
}
else
{
// Case S7
dum = DUM_SELECTION | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
}
}
else
{
if (0 == pupdcount->cOrphans)
{
// Case S8
dum = DUM_SELECTION | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG;
}
else
{
// Case S9
dum = DUM_SELECTION | DUM_UNAVAILABLE;
uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
}
}
}
else
{
if (0 < cSomeAction)
{
if (0 == pupdcount->cOrphans)
{
// Case S10
uRet = PSC_SHOWDIALOG;
}
else
{
// Case S11
uRet = PSC_SHOWDIALOG | PSC_POSTMSGBOX;
}
}
else
{
if (0 == pupdcount->cOrphans)
{
// Case S12
dum = DUM_SELECTION | DUM_UPTODATE;
}
else
{
// Case S13
dum = DUM_SELECTION | DUM_UPTODATE | DUM_ORPHAN;
}
}
}
}
Leave:
;
#ifdef DEBUG
if (IsFlagSet(g_uDumpFlags, DF_UPDATECOUNT))
{
TRACE_MSG(TF_ALWAYS, TEXT("Update selection counts: files = %u, orphans = %u, unavailable = %u, dosomething = %u, conflict = %u, subfolders = %u"),
pupdcount->cFiles, pupdcount->cOrphans,
pupdcount->cUnavailable, pupdcount->cDoSomething,
pupdcount->cConflict, pupdcount->cSubfolders);
}
#endif
}
if (0 != dum)
{
DoUpdateMsg(hwndOwner, pszList, pupdcount->cFiles, dum);
}
return uRet;
}
/*----------------------------------------------------------
Purpose: Show the update dialog and perform the reconcilation
if the user chooses OK
Returns: standard result
Cond: --
*/
HRESULT PUBLIC Upd_DoModal(
HWND hwndOwner,
CBS * pcbs,
LPCTSTR pszList, // May be NULL if uFlags == UF_ALL
UINT cFiles,
UINT uFlags)
{
INT_PTR nRet;
HRESULT hres;
PRECLIST prl;
UPDCOUNT updcount;
HWND hwndProgress;
hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING, DELAY_UPDBAR);
// Get a reclist and other useful information
updcount.cFiles = cFiles;
if (IsFlagSet(uFlags, UF_ALL))
{
hres = PrepForUpdateAll(pcbs, &prl, &updcount, hwndProgress);
}
else
{
hres = PrepForUpdateSelection(pcbs, &prl, pszList, cFiles, &updcount, hwndProgress);
}
UpdBar_Kill(hwndProgress);
if (S_OK == GetScode(hres))
{
XUPDSTRUCT xupd;
xupd.lprl = prl;
xupd.pcbs = pcbs;
xupd.uFlags = uFlags;
xupd.hdpa = ComposeUpdateList(pcbs, prl, &updcount, hwndOwner);
xupd.cDoSomething = updcount.cDoSomething;
if (NULL == xupd.hdpa)
{
hres = E_OUTOFMEMORY;
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
NULL, MB_ERROR);
}
else
{
// Check for some of those special cases listed at top of file
UINT uVal = PassedSpecialCases(hwndOwner, pszList, &updcount, uFlags);
// Show the update dialog?
if (IsFlagSet(uVal, PSC_SHOWDIALOG))
{
// Yes
nRet = DoModal(hwndOwner, Upd_WrapperProc, IDD_UPDATE, (LPARAM)&xupd);
switch (nRet)
{
case IDOK:
// Reconcile!
hwndProgress = UpdBar_Show(hwndOwner, UB_UPDATING, 0);
Sync_ReconcileRecList(prl, Atom_GetName(pcbs->atomBrf),
hwndProgress, RF_DEFAULT);
UpdBar_Kill(hwndProgress);
// Show a summary messagebox?
if (IsFlagSet(uVal, PSC_POSTMSGBOX))
{
// Yes
DoUpdateMsg(hwndOwner, pszList, updcount.cOrphans, DUM_SELECTION | DUM_ORPHAN);
}
// Fall thru
// | |
// v v
case IDCANCEL:
hres = NOERROR;
break;
case -1:
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_OOM_UPDATEDIALOG), MAKEINTRESOURCE(IDS_CAP_UPDATE),
NULL, MB_ERROR);
hres = E_OUTOFMEMORY;
break;
default:
ASSERT(0);
break;
}
}
DPA_Destroy(xupd.hdpa);
}
Sync_DestroyRecList(prl);
}
return hres;
}