//--------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation 1993-1994 // // File: status.c // // This file contains the dialog code for the Status property sheet // // History: // 08-06-93 ScottH Transferred from twin code // //--------------------------------------------------------------------------- #include "brfprv.h" // common headers #include #include "res.h" #include "recact.h" #include typedef struct tagSTAT { HWND hwnd; // dialog handle PPAGEDATA ppagedata; FileInfo * pfi; TCHAR szFolder[MAX_PATH]; BOOL bInit; } STAT, * PSTAT; #define Stat_Pcbs(this) ((this)->ppagedata->pcbs) #define Stat_AtomBrf(this) ((this)->ppagedata->pcbs->atomBrf) #define Stat_GetPtr(hwnd) (PSTAT)GetWindowLongPtr(hwnd, DWLP_USER) #define Stat_SetPtr(hwnd, lp) (PSTAT)SetWindowLongPtr(hwnd, DWLP_USER, (LRESULT)(lp)) #define LNKM_ACTIVATEOTHER (WM_USER + 0) /*---------------------------------------------------------- Purpose: Disable all the controls in the property page Returns: -- Cond: -- */ void PRIVATE Stat_DisableAll( PSTAT this) { HWND hwnd = this->hwnd; HWND hwndFocus = GetFocus(); RecAct_DeleteAllItems(GetDlgItem(hwnd, IDC_UPDATEACTIONS)); RecAct_Enable(GetDlgItem(hwnd, IDC_UPDATEACTIONS), FALSE); Button_Enable(GetDlgItem(hwnd, IDC_PBTSRECON), FALSE); Button_Enable(GetDlgItem(hwnd, IDC_PBTSFIND), FALSE); Button_Enable(GetDlgItem(hwnd, IDC_PBTSSPLIT), FALSE); if ( !hwndFocus || !IsWindowEnabled(hwndFocus) ) { SetFocus(GetDlgItem(GetParent(hwnd), IDOK)); SendMessage(GetParent(hwnd), DM_SETDEFID, IDOK, 0); } } /*---------------------------------------------------------- Purpose: Set the directions static text Returns: -- Cond: -- */ void PRIVATE Stat_SetDirections( PSTAT this) { HWND hwnd = this->hwnd; HWND hwndRA = GetDlgItem(this->hwnd, IDC_UPDATEACTIONS); RA_ITEM item; TCHAR sz[MAXBUFLEN]; *sz = 0; // This function shouldn't be called if this is an orphan ASSERT(S_OK == PageData_Query(this->ppagedata, hwnd, NULL, NULL)); item.mask = RAIF_INSIDE | RAIF_OUTSIDE | RAIF_ACTION; item.iItem = 0; ASSERT(RecAct_GetItemCount(hwndRA) == 1); if (RecAct_GetItem(hwndRA, &item)) { UINT ids; ASSERT(IsFlagSet(item.mask, RAIF_INSIDE | RAIF_OUTSIDE)); switch (item.uAction) { case RAIA_TOIN: case RAIA_TOOUT: case RAIA_DELETEOUT: case RAIA_DELETEIN: case RAIA_MERGE: case RAIA_SOMETHING: // Instructions to update if (this->ppagedata->bFolder) ids = IDS_STATPROP_PressButton; else ids = IDS_STATPROP_Update; break; case RAIA_CONFLICT: ids = IDS_STATPROP_Conflict; break; default: if (SI_UNAVAILABLE == item.siOutside.uState) { // The original file is unavailable. We don't know if // everything is up-to-date. ids = IDS_STATPROP_Unavailable; } else { // They are up-to-date ids = IDS_STATPROP_Uptodate; } break; } SzFromIDS(ids, sz, ARRAYSIZE(sz)); } Static_SetText(GetDlgItem(hwnd, IDC_STTSDIRECT), sz); } /*---------------------------------------------------------- Purpose: Sets the reconciliation action control Returns: standard result S_OK if the item is still a twin S_FALSE if the item is an orphan Cond: -- */ HRESULT PRIVATE Stat_SetRecAct( PSTAT this, PRECLIST prl, PFOLDERTWINLIST pftl) { HRESULT hres; HWND hwnd = this->hwnd; HWND hwndRA = GetDlgItem(hwnd, IDC_UPDATEACTIONS); LPCTSTR pszPath = Atom_GetName(this->ppagedata->atomPath); RA_ITEM * pitem; // This function shouldn't be called if this is an orphan ASSERT(S_OK == PageData_Query(this->ppagedata, hwnd, NULL, NULL)); hres = RAI_Create(&pitem, Atom_GetName(Stat_AtomBrf(this)), pszPath, prl, pftl); if (SUCCEEDED(hres)) { if (RAIA_ORPHAN == pitem->uAction) { // This is a pending orphan PageData_Orphanize(this->ppagedata); hres = S_FALSE; } else if ( !this->ppagedata->bFolder ) { pitem->mask |= RAIF_LPARAM; pitem->lParam = (LPARAM)prl->priFirst; } if (S_OK == hres) { BOOL bEnable; HWND hwndFocus = GetFocus(); // Add the item to the recact control. RecAct_InsertItem(hwndRA, pitem); // Determine the state of the buttons bEnable = !(pitem->uAction == RAIA_SKIP || pitem->uAction == RAIA_CONFLICT || pitem->uAction == RAIA_NOTHING); Button_Enable(GetDlgItem(hwnd, IDC_PBTSRECON), bEnable); bEnable = (SI_UNAVAILABLE != pitem->siOutside.uState); Button_Enable(GetDlgItem(hwnd, IDC_PBTSFIND), bEnable); if ( !hwndFocus || !IsWindowEnabled(hwndFocus) ) { SetFocus(GetDlgItem(hwnd, IDC_PBTSSPLIT)); SendMessage(hwnd, DM_SETDEFID, IDC_PBTSSPLIT, 0); } } RAI_Free(pitem); } return hres; } /*---------------------------------------------------------- Purpose: Sets the controls in the status property page. Returns: -- Cond: -- */ void PRIVATE Stat_SetControls( PSTAT this) { HWND hwnd = this->hwnd; HRESULT hres; PRECLIST prl; PFOLDERTWINLIST pftl; // Is this a twin? hres = PageData_Query(this->ppagedata, hwnd, &prl, &pftl); if (S_OK == hres) { // Yes RecAct_DeleteAllItems(GetDlgItem(hwnd, IDC_UPDATEACTIONS)); // Is it still a twin? hres = Stat_SetRecAct(this, prl, pftl); if (S_OK == hres) { // Yes Stat_SetDirections(this); } else if (S_FALSE == hres) { // No goto WipeOut; } } else if (S_FALSE == hres) { // No; disable the controls TCHAR sz[MAXBUFLEN]; WipeOut: Stat_DisableAll(this); // Is this a subfolder twin? if (IsSubfolderTwin(PageData_GetHbrf(this->ppagedata), Atom_GetName(this->ppagedata->atomPath))) { // Yes; use subfolder twin message. SzFromIDS(IDS_STATPROP_SubfolderTwin, sz, ARRAYSIZE(sz)); } else { // No; use orphan message. if (this->ppagedata->bFolder) SzFromIDS(IDS_STATPROP_OrphanFolder, sz, ARRAYSIZE(sz)); else SzFromIDS(IDS_STATPROP_OrphanFile, sz, ARRAYSIZE(sz)); } Static_SetText(GetDlgItem(hwnd, IDC_STTSDIRECT), sz); } } /*---------------------------------------------------------- Purpose: Gets the icon of the file Returns: HICON Cond: -- */ HICON PRIVATE GetIconHelper( LPCTSTR pszPath) { SHFILEINFO sfi; if (SHGetFileInfo(pszPath, 0, &sfi, sizeof(sfi), SHGFI_ICON)) { return sfi.hIcon; } return NULL; } /*---------------------------------------------------------- Purpose: Stat WM_INITDIALOG Handler Returns: FALSE when we assign the control focus Cond: -- */ BOOL PRIVATE Stat_OnInitDialog( PSTAT this, HWND hwndFocus, LPARAM lParam) // expected to be LPPROPSHEETPAGE { HWND hwnd = this->hwnd; LPCTSTR pszPath; this->ppagedata = (PPAGEDATA)((LPPROPSHEETPAGE)lParam)->lParam; // Set up the display of the dialog pszPath = Atom_GetName(this->ppagedata->atomPath); if (SUCCEEDED(FICreate(pszPath, &this->pfi, FIF_ICON))) { Static_SetIcon(GetDlgItem(hwnd, IDC_ICTSMAIN), this->pfi->hicon); Static_SetText(GetDlgItem(hwnd, IDC_NAME), FIGetDisplayName(this->pfi)); } // Save the folder of the twin away. if (pszPath) { lstrcpy(this->szFolder, pszPath); } else { this->szFolder[0] = 0; } if (!this->ppagedata->bFolder) PathRemoveFileSpec(this->szFolder); this->bInit = TRUE; return FALSE; // we set the initial focus } /*---------------------------------------------------------- Purpose: PSN_SETACTIVE handler Returns: -- Cond: -- */ void PRIVATE Stat_OnSetActive( PSTAT this) { HWND hwnd = this->hwnd; // Cause the page to be painted right away HideCaret(NULL); SetWindowRedraw(hwnd, TRUE); InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd); if (this->bInit) { PageData_Init(this->ppagedata, GetParent(this->hwnd)); this->bInit = FALSE; } ShowCaret(NULL); Stat_SetControls(this); } /*---------------------------------------------------------- Purpose: Reconcile the twins in this property sheet. For folder twins, we invoke the Update dialog. For object twins, we reconcile from here. Returns: -- Cond: -- */ void PRIVATE Stat_OnUpdate( PSTAT this, PRECLIST prl) { HWND hwnd = this->hwnd; HWND hwndRA = GetDlgItem(hwnd, IDC_UPDATEACTIONS); LPCTSTR pszPath = Atom_GetName(this->ppagedata->atomPath); if (0 == RecAct_GetItemCount(hwndRA)) return; ASSERT(S_OK == PageData_Query(this->ppagedata, hwnd, NULL, NULL)); // Is this a folder? if (this->ppagedata->bFolder) { // Yes; let the Update dialog do the work Upd_DoModal(hwnd, Stat_Pcbs(this), pszPath, 1, UF_SELECTION); } else { // No; we do the work. HWND hwndProgress; hwndProgress = UpdBar_Show(hwnd, UB_UPDATING, 0); Sync_ReconcileRecList(prl, Atom_GetName(Stat_AtomBrf(this)), hwndProgress, RF_DEFAULT); UpdBar_Kill(hwndProgress); } this->ppagedata->bRecalc = TRUE; PropSheet_CancelToClose(GetParent(hwnd)); } /*---------------------------------------------------------- Purpose: Separate the twins. Returns: -- Cond: -- */ void PRIVATE Stat_OnSplit(PSTAT this) { HWND hwnd = this->hwnd; HWND hwndRA = GetDlgItem(hwnd, IDC_UPDATEACTIONS); LPCTSTR pszPath = Atom_GetName(this->ppagedata->atomPath); if (0 == RecAct_GetItemCount(hwndRA)) return; ASSERT(S_OK == PageData_Query(this->ppagedata, hwnd, NULL, NULL)); // Was the twin successfully deleted? if (S_OK == Sync_Split(PageData_GetHbrf(this->ppagedata), pszPath, 1, hwnd, 0)) { // Yes; remove the cache references CRL_Nuke(this->ppagedata->atomPath); Stat_DisableAll(this); PropSheet_CancelToClose(GetParent(hwnd)); // Notify the shell of the change PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE); } } /*---------------------------------------------------------- Purpose: Attempt to bind to an object to see if it exists. Returns: TRUE if the object exists Cond: -- */ BOOL PRIVATE VerifyExists(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl) { BOOL bRet = FALSE; IShellFolder *psfDesktop; ASSERT(pidlParent); ASSERT(pidl); if (SUCCEEDED(SHGetDesktopFolder(&psfDesktop))) { IShellFolder *psf; HRESULT hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlParent, NULL, &IID_IShellFolder, &psf); if (SUCCEEDED(hres)) { ULONG rgfAttr = SFGAO_VALIDATE; bRet = SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &rgfAttr)); psf->lpVtbl->Release(psf); } psfDesktop->lpVtbl->Release(psfDesktop); } return bRet; } /*---------------------------------------------------------- Purpose: Open a file (taken from ShellExecFile) Returns: value of ShellExecuteEx Cond: -- */ BOOL PUBLIC ExecFile(HWND hwnd, LPCTSTR pszVerb, LPCTSTR pszFile, // Fully qualified and fully resolved path to the file LPCTSTR pszParams, LPCTSTR pszDir, // If NULL then working dir is derived from lpszFile (except for UNC's) LPCITEMIDLIST pidl, int nShow) { SHELLEXECUTEINFO execinfo = {0}; execinfo.cbSize = sizeof(execinfo); execinfo.hwnd = hwnd; execinfo.lpVerb = pszVerb; execinfo.lpFile = pszFile; execinfo.lpParameters = pszParams; execinfo.lpDirectory = pszDir; execinfo.nShow = nShow; execinfo.lpIDList = (LPITEMIDLIST)pidl; if (pidl) { execinfo.fMask |= SEE_MASK_IDLIST; } return ShellExecuteEx(&execinfo); } /*---------------------------------------------------------- Purpose: Selects an item in the given cabinet window. Optionally sets it to be renamed. This function does not verify if the window is really a cabinet window. Returns: -- Cond: -- */ void PUBLIC SelectItemInCabinet( HWND hwndCabinet, LPCITEMIDLIST pidl, BOOL bEdit) { if (IsWindow(hwndCabinet)) { if (pidl) { LPITEMIDLIST pidlItem; // we need to global clone this because hwndCabinet might be // in a different process... could happen with common dialog pidlItem = ILGlobalClone(pidl); if (pidlItem) { UINT uFlagsEx; if (bEdit) uFlagsEx = SVSI_EDIT; else uFlagsEx = 0; SendMessage(hwndCabinet, CWM_SELECTITEM, uFlagsEx | SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_DESELECTOTHERS, (LPARAM)pidlItem); ILGlobalFree(pidlItem); } } } } /*---------------------------------------------------------- Purpose: Open a cabinet window and set the focus on the object. Returns: -- Cond: -- */ void PUBLIC OpenCabinet( HWND hwnd, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidl, BOOL bEdit) // TRUE: set the focus to edit the label { if (!VerifyExists(pidlFolder, pidl)) { MsgBox(hwnd, MAKEINTRESOURCE(IDS_MSG_CantFindOriginal), MAKEINTRESOURCE(IDS_CAP_STATUS), NULL, MB_INFO); } else { HWND hwndCabinet; SHWaitForFileToOpen(pidlFolder, WFFO_ADD, 0L); if (ExecFile(hwnd, c_szOpen, NULL, NULL, NULL, pidlFolder, SW_NORMAL)) { // This will wait for the window to open or time out // We need to disable the dialog box while we are waiting. DECLAREHOURGLASS; SetHourglass(); EnableWindow(hwnd, FALSE); SHWaitForFileToOpen(pidlFolder, WFFO_REMOVE | WFFO_WAIT, WFFO_WAITTIME); EnableWindow(hwnd, TRUE); ResetHourglass(); hwndCabinet = FindWindow(c_szCabinetClass, NULL); } else { // If it failed clear out our wait hwndCabinet = NULL; SHWaitForFileToOpen(pidlFolder, WFFO_REMOVE, 0L); } if (hwndCabinet) { SelectItemInCabinet(hwndCabinet, pidl, bEdit); // we need to post to the other because we can't activate another // thread from within a button's callback PostMessage(hwnd, LNKM_ACTIVATEOTHER, 0, (LPARAM)hwndCabinet); } } } /*---------------------------------------------------------- Purpose: Opens the cabinet with the item pointed to by the twin. (copied and modified from link.c in shelldll) Returns: -- Cond: -- */ void PRIVATE Stat_OnFind( PSTAT this) { HWND hwnd = this->hwnd; HWND hwndRA = GetDlgItem(hwnd, IDC_UPDATEACTIONS); RA_ITEM item; if (0 == RecAct_GetItemCount(hwndRA)) return; ASSERT(S_OK == PageData_Query(this->ppagedata, hwnd, NULL, NULL)); item.mask = RAIF_OUTSIDE | RAIF_NAME; item.iItem = 0; if (RecAct_GetItem(hwndRA, &item)) { TCHAR szCanon[MAX_PATH]; LPITEMIDLIST pidlFolder; LPITEMIDLIST pidl; // Use UNC name to find it on the net BrfPathCanonicalize(item.siOutside.pszDir, szCanon); pidlFolder = ILCreateFromPath(szCanon); if (pidlFolder) { pidl = ILCreateFromPath(item.pszName); if (pidl) { OpenCabinet(hwnd, pidlFolder, ILFindLastID(pidl), FALSE); ILFree(pidl); } ILFree(pidlFolder); } } } /*---------------------------------------------------------- Purpose: Stat WM_COMMAND Handler Returns: -- Cond: -- */ void PRIVATE Stat_OnCommand( PSTAT this, int id, HWND hwndCtl, UINT uNotifyCode) { PRECLIST prl; HRESULT hres; switch (id) { case IDC_PBTSRECON: case IDC_PBTSFIND: case IDC_PBTSSPLIT: RETRY_BEGIN(FALSE) { hres = PageData_Query(this->ppagedata, this->hwnd, &prl, NULL); if (FAILED(hres)) { // Error // Unavailable disk? if (E_TR_UNAVAILABLE_VOLUME == hres) { // Yes; ask user to retry/cancel int id = MsgBox(this->hwnd, MAKEINTRESOURCE(IDS_ERR_UNAVAIL_VOL), MAKEINTRESOURCE(IDS_CAP_STATUS), NULL, MB_RETRYCANCEL | MB_ICONWARNING); if (IDRETRY == id) RETRY_SET(); // Try again } } } RETRY_END() // Is this a twin? if (S_OK == hres) { // Yes; do the operation switch (id) { case IDC_PBTSRECON: Stat_OnUpdate(this, prl); break; case IDC_PBTSFIND: Stat_OnFind(this); break; case IDC_PBTSSPLIT: Stat_OnSplit(this); break; } Stat_SetControls(this); } else if (S_FALSE == hres) { Stat_SetControls(this); } break; } } /*---------------------------------------------------------- Purpose: Handle RN_ITEMCHANGED Returns: -- Cond: -- */ void PRIVATE Stat_HandleItemChange( PSTAT this, NM_RECACT * lpnm) { PRECITEM pri; ASSERT((lpnm->mask & RAIF_LPARAM) != 0); pri = (PRECITEM)lpnm->lParam; // The action has changed, update the recnode accordingly // if (lpnm->mask & RAIF_ACTION) { BOOL bEnable; HWND hwndFocus = GetFocus(); Sync_ChangeRecItemAction(pri, Atom_GetName(Stat_AtomBrf(this)), this->szFolder, lpnm->uAction); bEnable = (RAIA_SKIP != lpnm->uAction && RAIA_CONFLICT != lpnm->uAction); Button_Enable(GetDlgItem(this->hwnd, IDC_PBTSRECON), bEnable); if ( !hwndFocus || !IsWindowEnabled(hwndFocus) ) { SetFocus(GetDlgItem(this->hwnd, IDC_PBTSSPLIT)); SendMessage(this->hwnd, DM_SETDEFID, IDC_PBTSSPLIT, 0); } } } /*---------------------------------------------------------- Purpose: WM_NOTIFY handler Returns: varies Cond: -- */ LRESULT PRIVATE Stat_OnNotify( PSTAT this, int idFrom, NMHDR * lpnmhdr) { LRESULT lRet = PSNRET_NOERROR; switch (lpnmhdr->code) { case RN_ITEMCHANGED: Stat_HandleItemChange(this, (NM_RECACT *)lpnmhdr); break; case PSN_SETACTIVE: Stat_OnSetActive(this); break; case PSN_KILLACTIVE: // N.b. This message is not sent if user clicks Cancel! // N.b. This message is sent prior to PSN_APPLY // break; case PSN_APPLY: break; default: break; } return lRet; } /*---------------------------------------------------------- Purpose: WM_DESTROY handler Returns: -- Cond: -- */ void PRIVATE Stat_OnDestroy( PSTAT this) { FIFree(this->pfi); } ///////////////////////////////////////////////////// EXPORTED FUNCTIONS static BOOL s_bStatRecurse = FALSE; LRESULT INLINE Stat_DefProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { ENTEREXCLUSIVE(); { s_bStatRecurse = TRUE; } LEAVEEXCLUSIVE(); return DefDlgProc(hDlg, msg, wParam, lParam); } /*---------------------------------------------------------- Purpose: Real Create Folder Twin dialog proc Returns: varies Cond: -- */ LRESULT Stat_DlgProc( PSTAT this, UINT message, WPARAM wParam, LPARAM lParam) { const static DWORD rgHelpIDs[] = { IDC_ICTSMAIN, IDH_BFC_PROP_FILEICON, IDC_NAME, IDH_BFC_PROP_FILEICON, IDC_STTSDIRECT, IDH_BFC_UPDATE_SCREEN, IDC_UPDATEACTIONS, IDH_BFC_UPDATE_SCREEN, // different IDC_PBTSRECON, IDH_BFC_UPDATE_BUTTON, IDC_PBTSSPLIT, IDH_BFC_PROP_SPLIT_BUTTON, IDC_PBTSFIND, IDH_BFC_PROP_FINDORIG_BUTTON, 0, 0 }; DWORD_PTR dw; switch (message) { HANDLE_MSG(this, WM_INITDIALOG, Stat_OnInitDialog); HANDLE_MSG(this, WM_COMMAND, Stat_OnCommand); HANDLE_MSG(this, WM_NOTIFY, Stat_OnNotify); HANDLE_MSG(this, WM_DESTROY, Stat_OnDestroy); case WM_HELP: dw = (DWORD_PTR)rgHelpIDs; if ( IDC_STATIC != ((LPHELPINFO)lParam)->iCtrlId ) WinHelp(((LPHELPINFO)lParam)->hItemHandle, c_szWinHelpFile, HELP_WM_HELP, dw); return 0; case WM_CONTEXTMENU: dw = (DWORD_PTR)rgHelpIDs; WinHelp((HWND)wParam, c_szWinHelpFile, HELP_CONTEXTMENU, dw); return 0; case LNKM_ACTIVATEOTHER: SwitchToThisWindow(GetLastActivePopup((HWND)lParam), TRUE); SetForegroundWindow((HWND)lParam); return 0; default: return Stat_DefProc(this->hwnd, message, wParam, lParam); } return 0; } /*---------------------------------------------------------- Purpose: Create Folder Twin Dialog Wrapper Returns: varies Cond: -- */ INT_PTR CALLBACK Stat_WrapperProc( HWND hDlg, // std params UINT message, WPARAM wParam, LPARAM lParam) { PSTAT this; // Cool windowsx.h dialog technique. For full explanation, see // WINDOWSX.TXT. This supports multiple-instancing of dialogs. // ENTEREXCLUSIVE(); { if (s_bStatRecurse) { s_bStatRecurse = FALSE; LEAVEEXCLUSIVE(); return FALSE; } } LEAVEEXCLUSIVE(); this = Stat_GetPtr(hDlg); if (this == NULL) { if (message == WM_INITDIALOG) { this = GAlloc(sizeof(*this)); if (!this) { MsgBox(hDlg, MAKEINTRESOURCE(IDS_OOM_STATUS), MAKEINTRESOURCE(IDS_CAP_STATUS), NULL, MB_ERROR); EndDialog(hDlg, IDCANCEL); return Stat_DefProc(hDlg, message, wParam, lParam); } this->hwnd = hDlg; Stat_SetPtr(hDlg, this); } else { return Stat_DefProc(hDlg, message, wParam, lParam); } } if (message == WM_DESTROY) { Stat_DlgProc(this, message, wParam, lParam); GFree(this); Stat_SetPtr(hDlg, NULL); return 0; } return SetDlgMsgResult(hDlg, message, Stat_DlgProc(this, message, wParam, lParam)); }