Code/Resource
Windows Develop
Linux-Unix program
Internet-Socket-Network
Web Server
Browser Client
Ftp Server
Ftp Client
Browser Plugins
Proxy Server
Email Server
Email Client
WEB Mail
Firewall-Security
Telnet Server
Telnet Client
ICQ-IM-Chat
Search Engine
Sniffer Package capture
Remote Control
xml-soap-webservice
P2P
WEB(ASP,PHP,...)
TCP/IP Stack
SNMP
Grid Computing
SilverLight
DNS
Cluster Service
Network Security
Communication-Mobile
Game Program
Editor
Multimedia program
Graph program
Compiler program
Compress-Decompress algrithms
Crypt_Decrypt algrithms
Mathimatics-Numerical algorithms
MultiLanguage
Disk/Storage
Java Develop
assembly language
Applications
Other systems
Database system
Embeded-SCM Develop
FlashMX/Flex
source in ebook
Delphi VCL
OS Develop
MiddleWare
MPI
MacOS develop
LabView
ELanguage
Software/Tools
E-Books
Artical/Document
copy.c
Package: shell.rar [view]
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 273k
Category:
Windows Kernel
Development Platform:
Visual C++
- // Copyright (c) <1995-1999> Microsoft Corporation
- #include "shellprv.h"
- #include "mmhelper.h"
- #pragma hdrstop
- #include "shlwapip.h" // for SHGlobalCounterDecrement
- #define INTERNAL_COPY_ENGINE
- #include "copy.h"
- #include "shell32p.h"
- #include "control.h"
- //--------------------//
- // Helper macros
- #ifndef RECTWIDTH
- #define RECTWIDTH(rc) ((rc).right - (rc).left)
- #endif//RECTWIDTH
- #ifndef RECTHEIGHT
- #define RECTHEIGHT(rc) ((rc).bottom - (rc).top)
- #endif//RECTHEIGHT
- #ifdef WINNT
- #include <iofs.h>
- #endif
- #define REG_VAL_GENERAL_RENAMEHTMLFILE TEXT("RenameHtmlFile")
- // BUGBUG - Might want this for Nashville too...
- #ifdef WINNT
- #define COPY_USE_COPYFILEEX
- #endif
- #define TF_DEBUGCOPY 0x00800000
- #define VERBOSE_STATUS
- // REVIEW, we should tune this size down as small as we can
- // to get smoother multitasking (without effecting performance)
- #define COPYMAXBUFFERSIZE 0x10000 // 0xFFFF this is 32-bit code!
- #define MIN_MINTIME4FEEDBACK 5 // is it worth showing estimated time to completion feedback?
- #define MS_RUNAVG 10000 // ms, window for running average time to completion estimate
- #define MS_TIMESLICE 2000 // ms, (MUST be > 1000!) first average time to completion estimate
- #define MAXDIRDEPTH 128 // # of directories we will deal with recursivly
- #define SHOW_PROGRESS_TIMEOUT 1000 // 1 second
- #define MINSHOWTIME 1000 // 1 sec
- // progress dialog message
- #define PDM_SHUTDOWN WM_APP
- #define PDM_NOOP (WM_APP + 1)
- #define PDM_UPDATE (WM_APP + 2)
- #define OPER_MASK 0x0F00
- #define OPER_ENTERDIR 0x0100
- #define OPER_LEAVEDIR 0x0200
- #define OPER_DOFILE 0x0300
- #define OPER_ERROR 0x0400
- #define FOFuncToStringID(wFunc) (IDS_UNDO_FILEOP + wFunc)
- // The following are the file and folder suffixes.
- // BUGBUG: These are hard-coded now; See if we need to read these from the registry.
- static const TCHAR c_szDotHtm[] = TEXT(".htm"); // The extension for HTML files.
- static const TCHAR c_szDotHtml[] = TEXT(".html"); // A different extension for HTML files.
- static const TCHAR c_szDotHtmQuestion[]= TEXT(".htm?"); // Wild card extension for .htm and .html
- static const TCHAR c_szStart[] = TEXT("*"); // Wild card for all suffixes.
- //
- // The following is a list of folder suffixes in all international languages. This list is NOT
- // read from a resource because we do NOT want the strings in this list to be mistakenly localized.
- // This list will allow NT5 shell to operate on files created by any international version of
- // office 9.
- // This list is taken from "http://officeweb/specs/webclient/files.htm"
- //
- // WARNING: Do not localize the strings in this table. Do not make any changes to this table
- // without consulting AlanRa (Office9 PM)
- //
- static const LPCTSTR c_apszSuffixes[] =
- {
- TEXT(".files"),
- TEXT("_files"),
- TEXT("-Dateien"),
- TEXT("_fichiers"),
- TEXT("_bestanden"),
- TEXT("_file"),
- TEXT("_archivos"),
- TEXT("-filer"),
- TEXT("_tiedostot"),
- TEXT("_pliki"),
- TEXT("_soubory"),
- TEXT("_elemei"),
- TEXT("_ficheiros"),
- TEXT("_arquivos"),
- TEXT("_dosyalar"),
- TEXT("_datoteke"),
- TEXT("_fitxers"),
- TEXT("_failid"),
- TEXT("_fails"),
- TEXT("_bylos"),
- TEXT("_fajlovi"),
- TEXT("_fitxategiak"),
- };
- // The reg value under HKCUREGSTR_PATH_EXPLORER that specifies Connection ON/OFF switch
- #define REG_VALUE_NO_FILEFOLDER_CONNECTION TEXT("NoFileFolderConnection")
- ////////////////////////////////////////////////////////////////////////////
- ///// directory tree cache.
- // this is set if pdtnChild has not been traversed (as opposed to NULL which means
- // there are no children
- #define DTN_DELAYED ((PDIRTREENODE)-1)
- // DIRTREENODE is a node in a linked list/tree cache of the directory structure.
- // except for the top level (which is specified by the caller of the api), the order
- // are all files first, then all directories.
- typedef struct _dirtreenode {
- struct _dirtreenode *pdtnNext; // sibling
- struct _dirtreenode *pdtnChild; // head of children linked list
- struct _dirtreenode *pdtnParent;
- DWORD dwFileAttributes;
- FILETIME ftCreationTime;
- FILETIME ftLastWriteTime;
- DWORD nFileSizeLow;
- DWORD nFileSizeHigh;
- DWORD nFileSizeCopied;
- BOOL fNewRoot : 1;
- BOOL fDummy : 1; // this marks the node as a dummy node (a wildcard that didn't match anything)
- BOOL fConnectedElement : 1; // this marks the node as an element that was implicitly added
- // to the Move/Copy source list because of an office9 type of
- // connection established in the registry.
- //The following is a union because not all nodes need all the fields.
- union {
- // The following is valid only if fConnectedElement is FALSE.
- struct _dirtreenode *pdtnConnected;
- // The following structure is valid only if fConnectedElemet is TRUE.
- struct {
- LPTSTR pFromConnected; // if fNewRoot && fConnectedElement, then these two elements
- LPTSTR pToConnected; // have the pFrom and pTo.
- DWORD dwConfirmation; // The result of confirnation given by end-user
- } ConnectedInfo;
- };
- TCHAR szShortName[14];
- TCHAR szName[1]; // this struct is dynamic
- } DIRTREENODE, *PDIRTREENODE;
- typedef struct {
- BOOL fChanged;
- DWORD dwFiles; // number of files
- DWORD dwFolders; // number of folders
- DWORD dwSize; // total size of all files
- } DIRTOTALS, *PDIRTOTALS;
- typedef struct {
- UINT oper;
- DIRTOTALS dtAll; // totals for all files
- DIRTOTALS dtDone; // totals of what's done
- BOOL fChangePosted;
- PDIRTREENODE pdtn; // first directory tree node
- PDIRTREENODE pdtnCurrent;
- PDIRTREENODE pdtnConnectedItems; //Pointer to the begining of connected elements node.
- TCHAR bDiskCheck[26];
- // how much does each operation cost in the progress...
- int iFilePoints;
- int iFolderPoints;
- int iSizePoints;
- LPTSTR pTo; // this holds the top level target list
- LPTSTR pFrom; // this holds the top level source list
- BOOL fMultiDest;
- TCHAR szSrcPath[MAX_PATH];
- TCHAR szDestPath[MAX_PATH]; // this is the current destination for pdtn and all it's children (not siblings)
- // lpszDestPath includes pdtn's first path component
- HDSA hdsaRenamePairs;
- } DIRTREEHEADER, *PDIRTREEHEADER;
- typedef struct {
- int nSourceFiles;
- LPTSTR lpCopyBuffer; // global file copy buffer
- UINT uSize; // size of this buffer
- FILEOP_FLAGS fFlags; // from SHFILEOPSTRUCT
- HWND hwndProgress; // dialog/progress window
- HWND hwndDlgParent; // parent window for message boxes
- CONFIRM_DATA cd; // confirmation stuff
- LPUNDOATOM lpua; // the undo atom that this file operation will make
- BOOL fNoConfirmRecycle;
- BOOL bAbort;
- BOOL fMerge; // are we doing a merge of folders
- BOOL fDone;
- BOOL fProgressOk;
- BOOL fDTBuilt;
- // folowing fields are used for giving estimated time for completion
- // feedback to the user during longer than MINTIME4FEEDBACK operations
- BOOL fFlushWrites; // Should we flush writes for destinations on slow links
- DWORD dwPreviousTime; // calculate transfer rate
- int iLastProgressPoints; // how many progress points we had the last time we updated the time est
- DWORD dwPointsPerSec;
- LPCTSTR lpszProgressTitle;
- LPSHFILEOPSTRUCT lpfo;
- DIRTREEHEADER dth;
- #ifdef COPY_USE_COPYFILEEX
- BOOL fInitialize;
- const WIN32_FIND_DATA* pfd;
- #endif
- BOOL bStreamLossPossible; // Could stream loss happen in this directory?
- } COPY_STATE, *LPCOPY_STATE;
- // function declarations
- void _ProcessNameMappings(LPTSTR pszTarget, HDSA hdsaRenamePairs);
- int GetNameDialog(HWND hwnd, COPY_STATE *pcs, BOOL fMultiple,UINT wOp, LPTSTR pFrom, LPTSTR pTo);
- void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs);
- BOOL FOQueryAbort(COPY_STATE *pcs);
- UINT DTAllocConnectedItemNodes(PDIRTREEHEADER pdth, COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR pszPath, BOOL fRecurse, PDIRTREENODE *ppdtnConnectedItems);
- void CALLBACK FOUndo_Invoke(LPUNDOATOM lpua);
- BOOL DTDiskCheck(PDIRTREEHEADER pdth, COPY_STATE *pcs, LPTSTR pszPath)
- {
- int iDrive = PathGetDriveNumber(pszPath);
- if (iDrive != -1)
- {
- if (!pdth->bDiskCheck[iDrive])
- {
- HWND hwnd = pcs->hwndDlgParent;
- TCHAR szDrive[] = TEXT("A:\");
- szDrive[0] += (CHAR)iDrive;
- // Sometimes pszPath is a dir and sometimes it's a file. All we really care about is if the
- // drive is ready (inserted, formated, net path mapped, etc). We know that we don't have a
- // UNC path because PathGetDriveNumber would have failed and we are already busted in terms
- // of mounted volumes, again because we use PathGetDriveNumber, so we don't have to worry about
- // these two cases. As such we build the root path and use that instead.
- pdth->bDiskCheck[iDrive] = SUCCEEDED(SHPathPrepareForWrite(((pcs->fFlags & FOF_NOERRORUI) ? NULL : hwnd), NULL, szDrive, 0));
- }
- return pdth->bDiskCheck[iDrive];
- }
- return TRUE; // always succeed for net drives
- }
- //--------------------------------------------------------------------------------------------
- // ConvertToConnectedItemname:
- // Given a file/folder name, this function checks to see if it has any connection and if
- // there is a connection, then it will convert the given name to that of the connected element
- // and return length of the prefix. If no connection exists, it returns zero.
- // The fDirectory parameter specifies if the given filename is a FOLDER or not!
- //
- // dwBuffSize: The size of pszFileName buffer in CHARACTERS.
- //
- // Examples:
- // "foo.htm" => "foo*" (returns 3 because the prefix("foo") length is 3)
- // "foobar files" => "foobar.htm?" (returns 6 as the prefix length)
- //
- //--------------------------------------------------------------------------------------------
- int ConvertToConnectedItemName(LPTSTR pszFileName, DWORD dwBuffSize, BOOL fDirectory)
- {
- LPTSTR pszDest, pszConnectedElemSuffix;
- int iPrefixLength;
- if (fDirectory)
- {
- // Look for a suffix which is one of the standard suffixes.
- if (!(pszDest = (LPTSTR)PathFindSuffixArray(pszFileName, c_apszSuffixes, ARRAYSIZE(c_apszSuffixes))))
- return FALSE;
- // " files" suffix is found. Replace it with ".htm?"
- pszConnectedElemSuffix = (LPTSTR)c_szDotHtmQuestion;
- }
- else
- {
- // Look for the extension ".htm" or ".html" and replace it with "*".
- if (!(pszDest = PathFindExtension(pszFileName)))
- return FALSE;
- if (lstrcmpi(pszDest, c_szDotHtm) && (lstrcmpi(pszDest, c_szDotHtml)))
- return(FALSE);
- // Extension ".htm" or ".html" is found. Replace it with "*"
- pszConnectedElemSuffix = (LPTSTR)c_szStar;
- }
- iPrefixLength = (int)(pszDest - pszFileName);
- //Check if the input buffer is big enough to over-write the suffix in-place
- if ((((int)dwBuffSize - iPrefixLength) - 1) < lstrlen(pszConnectedElemSuffix))
- return 0;
- //Replace the source suffix with the connected element's suffix.
- lstrcpy(pszDest, pszConnectedElemSuffix);
- return(iPrefixLength);
- }
- PDIRTREENODE DTAllocNode(PDIRTREEHEADER pdth, WIN32_FIND_DATA* pfd, PDIRTREENODE pdtnParent, PDIRTREENODE pdtnNext, BOOL fConnectedElement)
- {
- // BOBDAY: does this lstrlen need to be something else for unicode?
- int iLen = pfd ? lstrlen(pfd->cFileName) * sizeof(TCHAR) : 0;
- PDIRTREENODE pdtn = (PDIRTREENODE)LocalAlloc(LPTR, sizeof(DIRTREENODE) + iLen);
- if (pdtn)
- {
- pdtn->fConnectedElement = fConnectedElement;
- // Initializing the following to NULL is not needed because of the LPTR (zero init) done
- // above.
- // if (fConnectedElement)
- //{
- // pdtn->ConnectedInfo.pFromConnected = pdtn->ConnectedInfo.pToConnected = NULL;
- // pdtn->ConnectedInfo.dwConfirmation = 0;
- //}
- //else
- // pdtn->pdtnConnected = NULL;
- pdtn->pdtnParent = pdtnParent;
- pdtn->pdtnNext = pdtnNext;
- if (pfd)
- {
- pdtn->dwFileAttributes = pfd->dwFileAttributes;
- pdtn->ftCreationTime = pfd->ftCreationTime;
- pdtn->ftLastWriteTime = pfd->ftLastWriteTime;
- pdtn->nFileSizeLow = pfd->nFileSizeLow;
- pdtn->nFileSizeHigh = pfd->nFileSizeHigh;
- // only the stuff we care about
- lstrcpy(pdtn->szShortName, pfd->cAlternateFileName);
- lstrcpy(pdtn->szName, pfd->cFileName);
- if (ISDIRFINDDATA(*pfd))
- {
- pdth->dtAll.dwFolders++;
- pdtn->pdtnChild = DTN_DELAYED;
- }
- else
- {
- pdth->dtAll.dwSize += pfd->nFileSizeLow;
- pdth->dtAll.dwFiles++;
- }
- // increment the header stats
- pdth->dtAll.fChanged = TRUE;
- }
- }
- return pdtn;
- }
- #if defined(DEBUG) /// && defined(DEBUGCOPY)
- void DebugDumpPDTN(PDIRTREENODE pdtn, LPTSTR ptext)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("***** PDTN %x (%s)"), pdtn, ptext);
- //Safe-guard against pdtn being NULL!
- if (pdtn)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("** %s %s"), pdtn->szShortName, pdtn->szName);
- DebugMsg(TF_DEBUGCOPY, TEXT("** %x %d"), pdtn->dwFileAttributes, pdtn->nFileSizeLow);
- DebugMsg(TF_DEBUGCOPY, TEXT("** %x %x %x"), pdtn->pdtnParent, pdtn->pdtnNext, pdtn->pdtnChild);
- DebugMsg(TF_DEBUGCOPY, TEXT("** NewRoot:%x, Connected:%x, Dummy:%x"), pdtn->fNewRoot, pdtn->fConnectedElement, pdtn->fDummy);
- if (pdtn->fConnectedElement)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("**** Connected: pFromConnected:%s, pToConnected:%s, dwConfirmation:%x"), pdtn->ConnectedInfo.pFromConnected,
- pdtn->ConnectedInfo.pToConnected, pdtn->ConnectedInfo.dwConfirmation);
- }
- else
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("**** Origin: pdtnConnected:%x"), pdtn->pdtnConnected);
- }
- }
- else
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("** NULL pointer(PDTN)"));
- }
- }
- #else
- #define DebugDumpPDTN(p, x) 0
- #endif
- BOOL DoesSuffixMatch(LPTSTR lpSuffix, const LPCTSTR *apSuffixes, int iArraySize)
- {
- while(iArraySize--)
- {
- // Note: This must be a case sensitive compare, because we don't want to pickup
- // "Program Files".
- if (!lstrcmp(lpSuffix, *apSuffixes++))
- return(TRUE);
- }
- return(FALSE);
- }
- //--------------------------------------------------------------------------------------------
- //
- // DTPathToDTNode:
- // This function is used to build a list of nodes that correspond to the given pszPath.
- // This list is built under "ppdtn". If ppdtnConnectedItems is given, another list of nodes that
- // correspond to the connected elements(files/folders) of the nodes in the first list is also built
- // under "ppdtnConnectedItems".
- //
- // WARNING: This parties directly on pszPath and pfd so that it doesn't need to allocate
- // on the stack. This recurses, so we want to use as little stack as possible
- //
- // this will wack off one component from pszPath
- //
- //
- // ppdtn: Points to where the header of the list being built will be stored.
- // ppdtnConnectedItems: If this is NULL, then we are not interested in finding and building the
- // connected elements. If this is NOT null, it points to where the header of
- // the connected items list will be stored.
- // fConnectedElement: Each node being built under ppdtn needs to be marked with this bit.
- // iPrefixLength: This parameter is zero if fConnectedElement is FALSE. Otherwise, it contains the
- // Length of the prefix part of the file or foldername (path is NOT included).
- // For example, if "c:windowsfoo*" is passed in, iPrefixLength is 3 (length of "foo")
- //
- // dwFilesOrFolders parameter can specify if we need to look for only FILES or FOLDERs or BOTH.
- #define DTF_FILES_ONLY 0x00000001 //Operate only on Files.
- #define DTF_FOLDERS_ONLY 0x00000002 //Operate only on Folders.
- #define DTF_FILES_AND_FOLDERS (DTF_FILES_ONLY | DTF_FOLDERS_ONLY) //Operate on files AND folders.
- UINT DTPathToDTNode(PDIRTREEHEADER pdth, COPY_STATE *pcs, LPTSTR pszPath, BOOL fRecurse,
- DWORD dwFilesOrFolders, PDIRTREENODE* ppdtn, WIN32_FIND_DATA *pfd,
- PDIRTREENODE pdtnParent, PDIRTREENODE* ppdtnConnectedItems, BOOL fConnectedElement,
- int iPrefixLength)
- {
- int iError = 0;
- // this points to the var where all items are inserted.
- // folders are placed after it, files are placed before
- // keep the stack vars to a minimum because this is recursive
- PDIRTREENODE *ppdtnMiddle = ppdtn;
- HANDLE hfind;
- BOOL fNeedToFindNext;
- DebugMsg(TF_DEBUGCOPY, TEXT("DTPathToDTNode Entering %s"), pszPath);
- *ppdtnMiddle = NULL; // in case there are no children
- hfind = FindFirstFile(pszPath, pfd);
- if (hfind == INVALID_HANDLE_VALUE)
- {
- // this is allowable only if the path is wild...
- // and the parent exists
- if (PathIsWild(pszPath))
- {
- PathRemoveFileSpec(pszPath);
- if (PathFileExists(pszPath))
- {
- return 0;
- }
- }
- return OPER_ERROR | DE_FILENOTFOUND;
- }
- //Remove the filespec before passing it onto DTAllocConnectedItemNodes.
- PathRemoveFileSpec(pszPath);
- fNeedToFindNext = TRUE;
- do
- {
- // We skip the following files:
- // "." and ".." filenames
- // Folders when DTF_FILES_ONLY is set
- // Files when DTF_FOLDERS_ONLY is set
- if (!PathIsDotOrDotDot(pfd->cFileName) &&
- (((dwFilesOrFolders & DTF_FILES_ONLY) && !ISDIRFINDDATA(*pfd)) ||
- ((dwFilesOrFolders & DTF_FOLDERS_ONLY) && ISDIRFINDDATA(*pfd))))
- {
- //Check if we are looking for connected elements
- if ((!pdtnParent) && fConnectedElement)
- {
- // We found what we are looking for. If we are looking for a top-level connected item and
- // if it is a folder, then we need to make sure that the suffix exactly matches one of the
- // suffixes in the array c_apszSuffixes[].
- LPTSTR lpSuffix = (LPTSTR)(pfd->cFileName + iPrefixLength);
- if (ISDIRFINDDATA(*pfd))
- {
- // What we found is a directory!
- // See if it has one of the standard suffixes for connected folders.
- if (!DoesSuffixMatch(lpSuffix, c_apszSuffixes, ARRAYSIZE(c_apszSuffixes)))
- continue; //This is not what we look for. So, find next.
- }
- else
- {
- // What we found is a file (i.e Not a directory)
- // See if it has one of the standard suffixes for html files.
- if (lstrcmpi(lpSuffix, c_szDotHtm) && lstrcmpi(lpSuffix, c_szDotHtml))
- continue; //This is not what we look for. So, find next.
- }
- // Now we know that we found the connected element that we looked for.
- // So, no need to FindNext again. We can get out of the loop after processing
- // it once.
- fNeedToFindNext = FALSE;
- }
- *ppdtnMiddle = DTAllocNode(pdth, pfd, pdtnParent, *ppdtnMiddle, fConnectedElement);
- if (!*ppdtnMiddle)
- return OPER_ERROR | DE_INSMEM;
- // make sure that the parent's pointer always points to the head of
- // this linked list
- if (*ppdtn == (*ppdtnMiddle)->pdtnNext)
- *ppdtn = (*ppdtnMiddle);
- DebugDumpPDTN(*ppdtnMiddle, TEXT("DTPathToDTNode, DTAllocNode"));
- //We need to check for Connected elements only for the top level items
- if ((!(pcs->fFlags & FOF_NO_CONNECTED_ELEMENTS)) && ppdtnConnectedItems)
- {
- //Make sure this is a top level item
- ASSERT(!pdtnParent);
- //Create a list of connected items and attach it to the head of the list.
- iError = DTAllocConnectedItemNodes(pdth, pcs, pfd, pszPath, fRecurse, ppdtnConnectedItems);
- DebugDumpPDTN(*ppdtnConnectedItems, TEXT("DTPathToDTNode, DTAllocConnectedNodes"));
- // It is possible that the connected files do not exist. That condition is not really
- // an error. So, we check for insufficient memory error condition alone here.
- if (iError == (OPER_ERROR | DE_INSMEM))
- return(iError);
- //If a connected item exists, then make the origin item point to this connected item.
- if (*ppdtnConnectedItems)
- {
- (*ppdtnMiddle)->pdtnConnected = *ppdtnConnectedItems;
- // Also by default, set the Confirmation result to NO so that the connected element
- // will not be copied/moved etc., in case of a conflict. However, if the origin had
- // a conflict, we would put up a confirmation dlg and the result of that dlg will
- // over-write this value.
- (*ppdtnConnectedItems)->ConnectedInfo.dwConfirmation = IDNO;
- }
- //Move to the last node in the connected items list.
- while(*ppdtnConnectedItems)
- ppdtnConnectedItems = &((*ppdtnConnectedItems)->pdtnNext);
- }
- else
- {
- // This should have been initialized to zero during allocation, but lets be paranoid
- ASSERT( NULL == (*ppdtnMiddle)->pdtnConnected );
- }
- // if this is not a directory, move the ppdtnMiddle up one
- if (!ISDIRFINDDATA(*pfd))
- {
- ppdtnMiddle = &(*ppdtnMiddle)->pdtnNext;
- }
- }
- } while (fNeedToFindNext && !FOQueryAbort(pcs) && FindNextFile(hfind, pfd));
- iError = 0; //It is possible that iError contains other errors value now! So, reset it!
- FindClose(hfind);
- // now go and recurse into folders (if desired)
- // we don't have to check to see if these pdtn's are dirs, because the
- // way we inserted them above ensures that everything in from of
- // ppdtnMiddle are folders
- // we're going to tack on a specific child
- // then add the *.* after that
- while (!FOQueryAbort(pcs) && *ppdtnMiddle)
- {
- if (fRecurse)
- {
- if (PathAppend(pszPath, (*ppdtnMiddle)->szName))
- {
- if (PathAppend(pszPath, c_szStarDotStar))
- {
- // NULL indicates that we do not want to get the connected elements.
- // This is because we want the connected elements only for the top-level items.
- iError = DTPathToDTNode(pdth, pcs, pszPath, TRUE, DTF_FILES_AND_FOLDERS,
- &((*ppdtnMiddle)->pdtnChild), pfd, *ppdtnMiddle, NULL, fConnectedElement, 0);
- }
- else
- {
- iError = OPER_ERROR | DE_INVALIDFILES;
- }
- PathRemoveFileSpec(pszPath);
- }
- else
- {
- iError = OPER_ERROR | DE_INVALIDFILES;
- }
- }
- else
- {
- // of we don't want to recurse, just mark them all as having no children
- (*ppdtnMiddle)->pdtnChild = NULL;
- }
- if (iError)
- {
- return iError;
- }
- ppdtnMiddle = &(*ppdtnMiddle)->pdtnNext;
- }
- return 0;
- }
- UINT DTAllocConnectedItemNodes(PDIRTREEHEADER pdth, COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR pszPath, BOOL fRecurse, PDIRTREENODE *ppdtnConnectedItems)
- {
- // Since DTAllocConnectedItemNodes() gets called only for the top-level items in the src list,
- // there is no danger of this function getting called recursively. Hence, I didn't worry about
- // allocating the following on the stack.
- // If "too-much-stack-is-used" problem arises, we can optimize the stack usage by splitting
- // the following function into two such that the most common case (of no connection)
- // doesn't use much stack.
- DWORD dwFileOrFolder;
- TCHAR szFullPath[MAX_PATH];
- TCHAR szFileName[MAX_PATH];
- WIN32_FIND_DATA fd;
- int iPrefixLength; //This is the length of "foo" if the filename is "foo.htm" or "foo files"
- //Make a copy of the filename; This copy will get munged by ConvertToConnectedItemName().
- lstrcpy(szFileName, pfd->cFileName);
- // Convert the given file/foder name into the connected item's name with wild card characters.
- if (!(iPrefixLength = ConvertToConnectedItemName(szFileName, ARRAYSIZE(szFileName), ISDIRFINDDATA(*pfd))))
- return 0; //No connections exist for the given folder/file.
- // Now szFileName has the name of connected element with wildcard character.
- // If the given element is a directory, we want to look for connected FILES only and
- // if the given element is a file, we want to look for connected FOLDERS only.
- dwFileOrFolder = ISDIRFINDDATA(*pfd) ? DTF_FILES_ONLY : DTF_FOLDERS_ONLY;
- // Form the file/folder name with the complete path!
- lstrcpy(szFullPath, pszPath);
- PathAppend(szFullPath, szFileName);
- // The file-element has some "connected" items.
- DebugMsg(TF_DEBUGCOPY, TEXT("DTAllocConnectedItemNodes Looking for %s"), szFullPath);
- return(DTPathToDTNode(pdth, pcs, szFullPath, fRecurse, dwFileOrFolder, ppdtnConnectedItems, &fd, NULL, NULL, TRUE, iPrefixLength));
- }
- void DTInitProgressPoints(PDIRTREEHEADER pdth, COPY_STATE *pcs)
- {
- pdth->iFilePoints = 1;
- pdth->iFolderPoints = 1;
- switch (pcs->lpfo->wFunc) {
- case FO_RENAME:
- case FO_DELETE:
- pdth->iSizePoints = 0;
- break;
- case FO_COPY:
- pdth->iSizePoints = 1;
- break;
- case FO_MOVE:
- if (PathIsSameRoot(pcs->lpfo->pFrom, pcs->lpfo->pTo))
- {
- pdth->iSizePoints = 0;
- }
- else
- {
- // if it's across volumes, these points increase
- // because we need to nuke the source as well as
- // create the target...
- // whereas we don't need to nuke the "size" of the source
- pdth->iFilePoints = 2;
- pdth->iFolderPoints = 2;
- pdth->iSizePoints = 1;
- }
- break;
- }
- }
- UINT DTBuild(COPY_STATE* pcs)
- {
- PDIRTREEHEADER pdth = &pcs->dth;
- WIN32_FIND_DATA fd;
- TCHAR szPath[MAX_PATH];
- PDIRTREENODE *ppdtn;
- PDIRTREENODE *ppdtnConnectedItems;
- int iError = 0;
- pcs->dth.pFrom = (LPTSTR)pcs->lpfo->pFrom;
- pcs->dth.pTo = (LPTSTR)pcs->lpfo->pTo;
- // A tree of original items will be built under ppdtn.
- ppdtn = &pdth->pdtn;
- // A tree of items "connected" to the orginal items will be built under ppdtnConnectedItems.
- ppdtnConnectedItems = &pdth->pdtnConnectedItems;
- DTInitProgressPoints(pdth, pcs);
- while (!FOQueryAbort(pcs) && *pdth->pFrom)
- {
- BOOL fRecurse = TRUE;
- switch (pcs->lpfo->wFunc)
- {
- case FO_MOVE:
- // The move operation doesn't need to recurse if we are moving from and to the same
- // volume. In this case we know that we don't need to display any warnings for
- // things like LFN to 8.3 filename conversion or stream loss. Instead, we can do
- // the operation with one single win32 file operation that just does a rename.
- // MAJOR BUGBUG (toddb): This is only true if we don't cross a mount point! If we cross
- // a mount point then we might have to warn about these things.
- if ((pcs->fFlags & FOF_NORECURSION) || PathIsSameRoot(pdth->pFrom, pdth->pTo))
- {
- fRecurse = FALSE;
- }
- break;
- case FO_COPY:
- // For a copy we always recurse unless we're told not to.
- if (pcs->fFlags & FOF_NORECURSION)
- {
- fRecurse = FALSE;
- }
- break;
- case FO_RENAME:
- // for a rename we never recurse
- fRecurse = FALSE;
- break;
- case FO_DELETE:
- // for a delete we don't need to recurse IF the recycle bin will be able to handle
- // the given item. If the recycle bin handles the delete then we can undo from
- // the recycle bin if we need to.
- if ((pcs->fFlags & FOF_ALLOWUNDO) && BBWillRecycle(pdth->pFrom, NULL))
- {
- fRecurse = FALSE;
- }
- break;
- }
- lstrcpy(szPath, pdth->pFrom);
- DebugMsg(TF_DEBUGCOPY, TEXT("DTBuild: %s"), szPath);
- // If the file is on removable media, we need to check for media in the drive.
- // Prompt the user to insert the media if it's missing.
- if (!DTDiskCheck(pdth, pcs, szPath))
- {
- iError = ERROR_CANCELLED;
- break;
- }
- iError = DTPathToDTNode(pdth, pcs, szPath, fRecurse,
- ((PathIsWild(pdth->pFrom) && (pcs->lpfo->fFlags & FOF_FILESONLY)) ? DTF_FILES_ONLY : DTF_FILES_AND_FOLDERS),
- ppdtn,&fd, NULL, ppdtnConnectedItems, FALSE, 0);
- DebugMsg(TF_DEBUGCOPY, TEXT("DTBuild: returned %d"), iError);
- // BUGBUG: If an error occured we should allow the user to skip the file that caused the error. That way
- // if one of the source files doesn't exists the rest will still get copied. Do this only in the multi-
- // source case, blah blah blah. This helps in the case where one of the source files cannot be moved or
- // copied (usually due to Access Denied, could be insuffecent permissions or file is in use, etc).
- if (iError)
- break;
- if (!(*ppdtn) && PathIsWild(pdth->pFrom))
- {
- // no files are associated with this path... this
- // can happen when we have wildcards...
- // alloc a dummy node
- *ppdtn = DTAllocNode(pdth, NULL, NULL, NULL, FALSE);
- if (*ppdtn)
- {
- (*ppdtn)->fDummy = TRUE;
- }
- }
- if (*ppdtn)
- {
- // mark this as the start of a root spec... this is
- // necessary in case we have several wild specs
- (*ppdtn)->fNewRoot = TRUE;
- }
- if (*ppdtnConnectedItems)
- {
- // Mark this as the start of a root spec.
- (*ppdtnConnectedItems)->fNewRoot = TRUE;
- // For connected items, we need to remember the path.
- (*ppdtnConnectedItems)->ConnectedInfo.pFromConnected = pdth->pFrom;
- (*ppdtnConnectedItems)->ConnectedInfo.pToConnected = pdth->pTo;
- }
- while (*ppdtn)
- {
- ppdtn = &(*ppdtn)->pdtnNext;
- }
- while (*ppdtnConnectedItems)
- {
- ppdtnConnectedItems = &(*ppdtnConnectedItems)->pdtnNext;
- }
- pdth->pFrom += lstrlen(pdth->pFrom) + 1;
- if (pcs->lpfo->wFunc != FO_DELETE && (pcs->lpfo->fFlags & FOF_MULTIDESTFILES))
- {
- pdth->pTo += lstrlen(pdth->pTo) + 1;
- }
- }
- //Attach the "ConnectedElements" Tree to the end of the source element tree.
- *ppdtn = pcs->dth.pdtnConnectedItems;
- pcs->dth.pFrom = (LPTSTR)pcs->lpfo->pFrom;
- pcs->dth.pTo = (LPTSTR)pcs->lpfo->pTo;
- pcs->fDTBuilt = TRUE;
- // set up the initial time information
- pcs->dwPreviousTime = GetTickCount();
- pcs->dwPointsPerSec = 0;
- pcs->iLastProgressPoints = 0;
- return iError;
- }
- #define DTNIsRootNode(pdtn) ((pdtn)->pdtnParent == NULL)
- #define DTNIsDirectory(pdtn) (pdtn->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- // This macro determines if the given node is an "Origin" of a connection. i.e. Does this node
- // point to a connected element that needs to be moved/copied etc., along with it?
- // For example, if "foo.htm" is moved, "foo files" is also moved.
- // Here, "foo.htm" is the "Connect origin" (fConnectedElement = FALSE; pdtnConnected is valid)
- // and "foo files" is the "connected element". (fConnectedElement = TRUE; )
- #define DTNIsConnectOrigin(pdtn) ((!pdtn->fConnectedElement) && (pdtn->pdtnConnected != NULL))
- #define DTNIsConnected(pdtn) (pdtn && (pdtn->fConnectedElement))
- //
- UINT DTEnumChildren(PDIRTREEHEADER pdth, COPY_STATE *pcs, BOOL fRecurse, DWORD dwFileOrFolder)
- {
- int iError = 0;
- if (pdth->pdtnCurrent->pdtnChild == DTN_DELAYED)
- {
- WIN32_FIND_DATA fd;
- // fill in all the children and update the stats in pdth
- if (PathAppend(pdth->szSrcPath, c_szStarDotStar))
- {
- iError = DTPathToDTNode(pdth, pcs, pdth->szSrcPath, fRecurse, dwFileOrFolder,
- &pdth->pdtnCurrent->pdtnChild, &fd, pdth->pdtnCurrent, NULL, pdth->pdtnCurrent->fConnectedElement, 0);
- }
- else
- {
- iError = OPER_ERROR | DE_INVALIDFILES;
- }
- // If we get "File Not Found" Error now and if it is a connected item, then this item
- // must have already been moved/renamed/deleted etc., So, this is not really an error.
- // All this means is that this connected item was also explicitly selected and hence appeared
- // as or "Origin" item earlier in the list and it had already been operated upon.
- // So, reset the error here.
- // (Example: If end-user selects "foo.htm" AND "foo files" folder and moves them, then we
- // will get a file-not-found error when we attempt to move the connected items. To avoid
- // this error dialog, we reset the error here.)
- if (DTNIsConnected(pdth->pdtnCurrent) && (iError == (OPER_ERROR | DE_FILENOTFOUND)))
- iError = 0;
- }
- return iError;
- }
- //
- // DTNGetConfirmationResult:
- // When a file("foo.htm") is moved/copied, we may put up a confirmation dialog in case
- // of a conflict and the end-user might have responded saying "Yes", "no" etc., When the
- // corresponding connected element ("foo files") is also moved/copied etc., we should NOT put up
- // a confirmation dialog again. We must simply store the answer to the original confirmation and
- // use it later.
- // This function retries the result of the original confirmation from the top-level connected
- // element.
- int DTNGetConfirmationResult(PDIRTREENODE pdtn)
- {
- //Confirmation results are saved only for Connected items; Not for Connection Origins.
- if (!pdtn || !DTNIsConnected(pdtn))
- return 0;
- //Confirmation results are stored only at the top-level node. So, go there.
- while(pdtn->pdtnParent)
- pdtn = pdtn->pdtnParent;
- return(pdtn->ConnectedInfo.dwConfirmation);
- }
- void DTGetWin32FindData(PDIRTREENODE pdtn, WIN32_FIND_DATA* pfd)
- {
- // only the stuff we care about
- lstrcpy(pfd->cAlternateFileName, pdtn->szShortName);
- lstrcpy(pfd->cFileName, pdtn->szName);
- pfd->dwFileAttributes = pdtn->dwFileAttributes;
- pfd->ftCreationTime = pdtn->ftCreationTime;
- pfd->ftLastWriteTime = pdtn->ftLastWriteTime;
- pfd->nFileSizeLow = pdtn->nFileSizeLow;
- pfd->nFileSizeHigh = pdtn->nFileSizeHigh;
- }
- void DTSetFileCopyProgress(PDIRTREEHEADER pdth, DWORD dwRead)
- {
- DWORD dwDelta;
- dwDelta = (dwRead - pdth->pdtnCurrent->nFileSizeCopied);
- DebugMsg(TF_DEBUGCOPY, TEXT("DTSetFileCopyProgress %d %d %d"), dwDelta, dwRead, pdth->dtDone.dwSize);
- pdth->pdtnCurrent->nFileSizeCopied += dwDelta;
- pdth->dtDone.dwSize += dwDelta;
- DebugMsg(TF_DEBUGCOPY, TEXT("DTSetFileCopyProgress %d %d"), dwDelta, pdth->dtDone.dwSize);
- pdth->dtDone.fChanged = TRUE;
- }
- void DTFreeNode(PDIRTREEHEADER pdth, PDIRTREENODE pdtn)
- {
- if (pdth)
- {
- ASSERT(pdtn->pdtnChild == NULL || pdtn->pdtnChild == DTN_DELAYED);
- // we're done with this node.. update the header totals
- if (DTNIsDirectory(pdtn)) {
- pdth->dtDone.dwFolders++;
- } else {
- pdth->dtDone.dwFiles++;
- pdth->dtDone.dwSize += (pdtn->nFileSizeLow - pdtn->nFileSizeCopied);
- }
- pdth->dtDone.fChanged = TRUE;
- // repoint parent pointer
- if (!pdtn->pdtnParent) {
- // no parent... must be a root type thing
- ASSERT(pdth->pdtn == pdtn);
- pdth->pdtn = pdtn->pdtnNext;
- } else {
- ASSERT(pdtn->pdtnParent->pdtnChild == pdtn);
- if (pdtn->pdtnParent->pdtnChild == pdtn) {
- // if my parent was pointing to me, point him to my sib
- pdtn->pdtnParent->pdtnChild = pdtn->pdtnNext;
- }
- }
- }
- LocalFree(pdtn);
- }
- // this frees all children of (but NOT including) the current node.
- // it doesn' free the current node because it's assumed that
- // DTGoToNextNode will be called right afterwards, and that will
- // free the current node
- void DTFreeChildrenNodes(PDIRTREEHEADER pdth, PDIRTREENODE pdtn)
- {
- PDIRTREENODE pdtnChild = pdtn->pdtnChild;
- while (pdtnChild && pdtnChild != DTN_DELAYED)
- {
- PDIRTREENODE pdtnNext = pdtnChild->pdtnNext;
- // recurse and free these children
- if (DTNIsDirectory(pdtnChild))
- {
- DTFreeChildrenNodes(pdth, pdtnChild);
- }
- DTFreeNode(pdth, pdtnChild);
- pdtnChild = pdtnNext;
- }
- pdtn->pdtnChild = NULL;
- }
- void DTForceEnumChildren(PDIRTREEHEADER pdth)
- {
- if (!pdth->pdtnCurrent->pdtnChild)
- pdth->pdtnCurrent->pdtnChild = DTN_DELAYED;
- }
- void DTAbortCurrentNode(PDIRTREEHEADER pdth)
- {
- DTFreeChildrenNodes((pdth), (pdth)->pdtnCurrent);
- if (pdth->oper == OPER_ENTERDIR)
- pdth->oper = OPER_LEAVEDIR;
- }
- void DTCleanup(PDIRTREEHEADER pdth)
- {
- PDIRTREENODE pdtn;
- while (pdth->pdtnCurrent && pdth->pdtnCurrent->pdtnParent)
- {
- // in case we bailed deep in a tree
- pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnParent;
- }
- while (pdth->pdtnCurrent)
- {
- pdtn = pdth->pdtnCurrent;
- pdth->pdtnCurrent = pdtn->pdtnNext;
- DTFreeChildrenNodes(NULL, pdtn);
- DTFreeNode(NULL, pdtn);
- }
- }
- BOOL DTInitializePaths(PDIRTREEHEADER pdth, COPY_STATE *pcs)
- {
- ASSERT( pdth->pdtnCurrent ); // If we have no current node then how can we Initialize its paths?
- lstrcpyn(pdth->szSrcPath, pdth->pFrom, ARRAYSIZE(pdth->szSrcPath));
- // For the "Origins" we need to do this only if a wild card exists. However, for connected elements,
- // we need to do this everytime because connected elements may not exist for every "Origins"
- if (PathIsWild(pdth->pFrom) || (pdth->pdtnCurrent->fNewRoot && DTNIsConnected(pdth->pdtnCurrent)))
- {
- PathRemoveFileSpec(pdth->szSrcPath);
- if (!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName))
- return FALSE;
- }
- if (!pdth->pTo)
- {
- // no dest, make it the same as the source and we're done
- lstrcpyn(pdth->szDestPath, pdth->szSrcPath, ARRAYSIZE(pdth->szSrcPath));
- return TRUE;
- }
- if (pdth->pTo)
- {
- lstrcpyn(pdth->szDestPath, pdth->pTo, ARRAYSIZE(pdth->szSrcPath));
- }
- if (!pdth->fMultiDest)
- {
- if (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
- return FALSE;
- }
- else
- {
- //When undo of a move operation is done, fMultiDest is set.
- // When fMultiDest is set, we need to strip out the filename given by pTo and
- // append the current filename.
- // For RENAME operations, the source and destination names are different. This is handled
- // seperately below. So, we handle only other operations here where source and dest names are the same.
- if ((pcs->lpfo->wFunc != FO_RENAME) && pdth->pdtnCurrent->fNewRoot && DTNIsConnected(pdth->pdtnCurrent))
- {
- PathRemoveFileSpec(pdth->szDestPath);
- if (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
- return FALSE;
- }
- }
- //We will never try to rename a connected element! Make sure we don't hit this!
- ASSERT(!((pcs->lpfo->wFunc == FO_RENAME) && DTNIsConnected(pdth->pdtnCurrent)));
- // BUGBUG: chee implement me
- return TRUE;
- }
- UINT DTValidatePathNames(PDIRTREEHEADER pdth, UINT operation, COPY_STATE * pcs)
- {
- if (pcs->lpfo->wFunc != FO_DELETE)
- {
- // Why process name mappings? Here's why. If we are asked to copy directory "c:foo" and
- // file "c:foofile" to another directory (say "d:") we might have a name confilct when
- // we copy "c:foo" so instead we create "d:Copy Of foo". Later, we walk to the second
- // dirtree node and we are asked to copy "c:foofile" to "d:foo", all of which is valid.
- // HOWEVER, it's not what we want to do. We use _ProccessNameMappings to convert
- // "d:foofile" into "d:Copy of foofile".
- _ProcessNameMappings(pdth->szDestPath, pdth->hdsaRenamePairs);
- // REVIEW, do we need to do the name mapping here or just let the
- // VFAT do it? if vfat does it we need to rip out all of the GetNameDialog() stuff.
- if ((operation != OPER_LEAVEDIR) &&
- !IsLFNDrive(pdth->szDestPath) &&
- PathIsLFNFileSpec(PathFindFileName(pdth->szSrcPath)) &&
- PathIsLFNFileSpec(PathFindFileName(pdth->szDestPath)))
- {
- int iRet;
- TCHAR szOldDest[MAX_PATH];
- lstrcpy(szOldDest, pdth->szDestPath);
- iRet = GetNameDialog(pcs->hwndDlgParent, pcs,
- (pcs->nSourceFiles != 1 ) || !DTNIsRootNode(pdth->pdtnCurrent), // if we're entering a dir, multiple spec, or not at root
- operation, pdth->szSrcPath, pdth->szDestPath);
- switch (iRet) {
- case IDNO:
- case IDCANCEL:
- return iRet;
- default:
- AddRenamePairToHDSA(szOldDest, pdth->szDestPath, &pcs->dth.hdsaRenamePairs);
- break;
- }
- }
- if (operation == OPER_ENTERDIR)
- {
- // Make sure the new directory is not a subdir of the original...
- int cchFrom = lstrlen(pdth->szSrcPath);
- // BUGBUG: Shouldn't we get the short names for both these directories and compair those?
- // Otherwise I can copy "C:Long Directory Name" to "C:LongDi~1foo" without error.
- if (!(pcs->fFlags & FOF_RENAMEONCOLLISION) &&
- !StrCmpNI(pdth->szSrcPath, pdth->szDestPath, cchFrom))
- {
- TCHAR chNext = pdth->szDestPath[cchFrom]; // Get the next char in the dest.
- if (!chNext)
- {
- return OPER_ERROR | DE_DESTSAMETREE;
- }
- else if (chNext == TEXT('\'))
- {
- // The two fully qualified strings are equal up to the end
- // of the source directory ==> the destination is a subdir.
- // Must return an error.
- // if, stripping the last file name and the backslash give the same length, they are the
- // same file/folder
- if ((PathFindFileName(pdth->szDestPath) - pdth->szDestPath - 1) ==
- lstrlen(pdth->szSrcPath))
- {
- return OPER_ERROR | DE_DESTSAMETREE;
- }
- else
- {
- return OPER_ERROR | DE_DESTSUBTREE;
- }
- }
- }
- }
- }
- return 0;
- }
- // this moves to the next node (child, sib, parent) and sets up the
- // directory path info and oper state
- UINT DTGoToNextNode(PDIRTREEHEADER pdth, COPY_STATE *pcs)
- {
- UINT oper = OPER_ENTERDIR; // the default
- int iError;
- if (!pdth->pdtnCurrent)
- {
- pdth->pdtnCurrent = pdth->pdtn;
- if (pdth->pdtnCurrent)
- {
- if (pdth->pdtnCurrent->fDummy)
- {
- // if this is just a placeholder... go on to the next one
- return DTGoToNextNode(pdth, pcs);
- }
- if (!DTInitializePaths(pdth, pcs))
- {
- return OPER_ERROR | DE_INVALIDFILES;
- }
- }
- else
- {
- // Our tree is completely empty.
- // REVIEW: What do we do here? If pdtnCurrent is still NULL then our list is completely empty.
- // Is that a bug or what? My hunch is that we should return an error code here, most likely
- // OPER_ERROR | DE_INVALIDFILES. If we do nothing here then we will fail silently.
- return OPER_ERROR | DE_INVALIDFILES;
- }
- }
- else
- {
- UINT iError;
- BOOL fFreeLastNode = TRUE;
- PDIRTREENODE pdtnLastCurrent = pdth->pdtnCurrent;
- if (iError = DTEnumChildren(pdth, pcs, FALSE, DTF_FILES_AND_FOLDERS))
- return iError;
- if (pdth->pdtnCurrent->pdtnChild)
- {
- fFreeLastNode = FALSE;
- pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnChild;
- // if the long name is too long, try the short name
- if ((!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName) &&
- !PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szShortName)) ||
- (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName) &&
- !PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szShortName)))
- return OPER_ERROR | DE_INVALIDFILES;
- }
- else if (pdth->oper == OPER_ENTERDIR)
- {
- // if the last operation was an enterdir and it has no children
- // (because it failed the above test
- // then we should do a leave dir on it now
- oper = OPER_LEAVEDIR;
- fFreeLastNode = FALSE;
- }
- else if (pdth->pdtnCurrent->pdtnNext)
- {
- pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnNext;
- if (!pdth->pdtnCurrent->pdtnParent)
- {
- // if this was the top, we need to build the next path info
- // from scratch
- if (pdth->pdtnCurrent->fNewRoot)
- {
- if (pdth->pdtnCurrent->fConnectedElement)
- {
- // Since this is a new root in a Connected list, the pFrom and pTo are
- // stored in the node itself. This is needed because Connected elements may
- // not exist for every item in the source list and we do not want to create dummy
- // nodes for each one of them. So, pFrom and pTo are stored for every NewRoot of
- // connected elements and we use these here.
- pdth->pFrom = pdth->pdtnCurrent->ConnectedInfo.pFromConnected;
- pdth->pTo = pdth->pdtnCurrent->ConnectedInfo.pToConnected;
- }
- else
- {
- // go to the next path pair
- pdth->pFrom += lstrlen(pdth->pFrom) + 1;
- if (pdth->pTo)
- {
- if (pdth->fMultiDest)
- {
- pdth->pTo += lstrlen(pdth->pTo) + 1;
- }
- }
- }
- }
- if (pdth->pdtnCurrent->fDummy)
- {
- // if this is just a placeholder... go on to the next one
- if (fFreeLastNode)
- {
- DTFreeNode(pdth, pdtnLastCurrent);
- }
- return DTGoToNextNode(pdth, pcs);
- }
- DTInitializePaths(pdth, pcs);
- }
- else
- {
- PathRemoveFileSpec(pdth->szSrcPath);
- PathRemoveFileSpec(pdth->szDestPath);
- if (!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName) ||
- !PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
- return OPER_ERROR | DE_INVALIDFILES;
- }
- }
- else
- {
- oper = OPER_LEAVEDIR;
- PathRemoveFileSpec(pdth->szSrcPath);
- PathRemoveFileSpec(pdth->szDestPath);
- pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnParent;
- }
- if (fFreeLastNode)
- {
- DTFreeNode(pdth, pdtnLastCurrent);
- }
- }
- if (!pdth->pdtnCurrent)
- {
- // no more! we're done!
- return 0;
- }
- DebugDumpPDTN(pdth->pdtnCurrent, TEXT("PDTNCurrent"));
- if (oper == OPER_ENTERDIR)
- {
- if (pcs->lpfo->wFunc == FO_RENAME || !DTNIsDirectory(pdth->pdtnCurrent))
- {
- oper = OPER_DOFILE;
- }
- }
- if (DTNIsRootNode(pdth->pdtnCurrent))
- {
- // we need to diskcheck the source and target because this might
- // be the first time we've seen this drive
- if (!DTDiskCheck(pdth, pcs, pdth->szSrcPath) ||
- !DTDiskCheck(pdth, pcs, pdth->szDestPath))
- {
- return 0;
- }
- }
- iError = DTValidatePathNames(pdth, oper, pcs);
- if (iError)
- {
- if (iError & OPER_ERROR)
- {
- //For connected nodes, ignore the error and silently abort the node!
- if (DTNIsConnected(pdth->pdtnCurrent))
- {
- DTAbortCurrentNode(pdth);
- return DTGoToNextNode(pdth, pcs);
- }
- else
- return iError;
- }
- else
- {
- switch (iError)
- {
- case IDNO:
- DTAbortCurrentNode(pdth);
- pcs->lpfo->fAnyOperationsAborted = TRUE;
- return DTGoToNextNode(pdth, pcs);
- case IDCANCEL:
- // User cancelled the operation
- pcs->bAbort = TRUE;
- return 0;
- }
- }
- }
- pdth->oper = oper;
- return oper;
- }
- int CopyMoveRetry(COPY_STATE *pcs, LPCTSTR pszDest, int error, DWORD dwFileSize);
- void CopyError(LPCOPY_STATE, LPCTSTR, LPCTSTR, int, UINT, int);
- void SetProgressTime(COPY_STATE *pcs);
- void SetProgressText(COPY_STATE *pcs, LPCTSTR pszFrom, LPCTSTR pszTo);
- void FOUndo_AddInfo(LPUNDOATOM lpua, LPTSTR lpszSrc, LPTSTR lpszDest, DWORD dwAttributes);
- void CALLBACK FOUndo_Release(LPUNDOATOM lpua);
- void FOUndo_FileReallyDeleted(LPTSTR lpszFile);
- void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs);
- BOOL_PTR CALLBACK FOFProgressDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
- typedef struct {
- LPTSTR lpszName;
- DWORD dwAttributes;
- } FOUNDO_DELETEDFILEINFO, *LPFOUNDO_DELETEDFILEINFO;
- typedef struct {
- HDPA hdpa;
- HDSA hdsa;
- } FOUNDODATA, *LPFOUNDODATA;
- DWORD CALLBACK FOUIThreadProc(COPY_STATE *pcs)
- {
- DWORD dwShowTime;
- HWND hwnd;
- int iShowTimeLeft;
- DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- Begin"));
- Sleep(SHOW_PROGRESS_TIMEOUT);
- if (pcs->fDone)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- End . Done before we started"));
- return 0;
- }
- hwnd = CreateDialogParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_MOVECOPYPROGRESS),
- pcs->lpfo->hwnd, FOFProgressDlgProc, (LPARAM)pcs);
- // crit section to sync with main thread termination
- ENTERCRITICAL;
- if (!pcs->fDone)
- {
- pcs->hwndProgress = hwnd;
- }
- LEAVECRITICAL;
- if (pcs->hwndProgress)
- {
- MSG msg;
- dwShowTime = GetTickCount();
- while(!pcs->fDone && GetMessage(&msg, NULL, 0, 0))
- {
- if (!pcs->fDone && !IsDialogMessage(pcs->hwndProgress, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- // if we've put it up, we need to keep it up for at least some minimal amount of time
- iShowTimeLeft = MINSHOWTIME - (GetTickCount() - dwShowTime);
- if (iShowTimeLeft > 0)
- {
- DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- doing an extra sleep"));
- Sleep(iShowTimeLeft);
- }
- }
- // Keep us from doing this while other thread processing...
- ENTERCRITICAL;
- pcs->hwndProgress = NULL;
- LEAVECRITICAL;
- DestroyWindow(hwnd);
- DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- End . Completed"));
- return 0;
- }
- // this queries the progress dialog for a cancel and yields.
- // it also will show the progress dialog if a certain amount of time has passed
- //
- // returns:
- // TRUE cacnel was pressed, abort the operation
- // FALSE continue
- BOOL FOQueryAbort(COPY_STATE *pcs)
- {
- if (!pcs->bAbort && pcs->hwndProgress)
- {
- if (pcs->hwndProgress != pcs->hwndDlgParent)
- {
- // do this here rather than on the FOUIThreadProc so that we don't have
- // synchronization problems with this thread popping up a dialog on
- // hwndDlgParent then the progress dialog coming up afterwards on top.
- pcs->hwndDlgParent = pcs->hwndProgress;
- ShowWindow(pcs->hwndProgress, SW_SHOW);
- SetForegroundWindow(pcs->hwndProgress);
- SetFocus(GetDlgItem(pcs->hwndProgress, IDCANCEL));
- SetProgressText(pcs, pcs->dth.szSrcPath,
- pcs->lpfo->wFunc == FO_DELETE ? NULL : pcs->dth.szDestPath);
- }
- else
- {
- MSG msg;
- // win95 handled messages in here.
- // we need to do the same in order to flush the input queue as well as
- // for backwards compatability.
- // we need to flush the input queue now because hwndProgress is
- // on a different thread... which means it has attached thread inputs
- // inorder to unlock the attached threads, we need to remove some
- // sort of message until there's none left... any type of message..
- while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- {
- if (!IsDialogMessage(pcs->hwndProgress, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- }
- if (pcs->dth.dtAll.fChanged || pcs->dth.dtDone.fChanged )
- {
- if (!pcs->dth.fChangePosted)
- {
- // set the flag first because with async threads
- // the progress window could handle it and clear the
- // bit before we set it.. then we'd lose further messages
- // thinking that one was still pending
- pcs->dth.fChangePosted = TRUE;
- if (!PostMessage(pcs->hwndProgress, PDM_UPDATE, 0, 0))
- pcs->dth.fChangePosted = FALSE;
- }
- }
- }
- return pcs->bAbort;
- }
- typedef struct _confdlg_data {
- LPCTSTR pFileDest;
- LPCTSTR pFileSource;
- LPCTSTR pStreamNames;
- const WIN32_FIND_DATA *pfdDest;
- const WIN32_FIND_DATA *pfdSource;
- BOOL bShowCancel; // allow cancel out of this operation
- BOOL bShowDates; // use date/size info in message
- UINT uDeleteWarning; // warn that the delete's not going to the wastebasket
- BOOL bFireIcon;
- BOOL bShrinkDialog; // should we move the buttons up to the text?
- int nSourceFiles; // if != 1 used to build "n files" string
- int idText; // if != 0 use to override string in dlg template
- CONFIRM_FLAG fConfirm; // we will confirm things set here
- CONFIRM_FLAG fYesMask; // these bits are cleared in fConfirm on "yes"
- // Only use fYesMask for things that should be confirmed once per operation
- CONFIRM_FLAG fYesToAllMask; // these bits are cleared in fConfirm on "yes to all"
- //COPY_STATE *pcs;
- CONFIRM_DATA *pcd;
- void (*InitConfirmDlg)(HWND hDlg, struct _confdlg_data *pcd); // routine to initialize dialog
- BOOL bARPWarning;
- } CONFDLG_DATA;
- BOOL BuildDateLine(LPTSTR pszDateLine, const WIN32_FIND_DATA *pFind, LPCTSTR pFileName)
- {
- TCHAR szTemplate[64];
- TCHAR szNum[32], szTmp[64];
- WIN32_FIND_DATA fd;
- ULARGE_INTEGER liFileSize;
- if (!pFind)
- {
- HANDLE hfind = FindFirstFile(pFileName, &fd);
- ASSERT(hfind != INVALID_HANDLE_VALUE);
- FindClose(hfind);
- pFind = &fd;
- }
- liFileSize.LowPart = pFind->nFileSizeLow;
- liFileSize.HighPart = pFind->nFileSizeHigh;
- // There are cases where the date is 0, this is especially true when the
- // source is from a file contents...
- if (pFind->ftLastWriteTime.dwLowDateTime || pFind->ftLastWriteTime.dwHighDateTime)
- {
- DWORD dwFlags = FDTF_LONGDATE | FDTF_RELATIVE | FDTF_LONGTIME;
- SHFormatDateTime(&pFind->ftLastWriteTime, &dwFlags, szTmp, SIZECHARS(szTmp));
- LoadString(HINST_THISDLL, IDS_DATESIZELINE, szTemplate, ARRAYSIZE(szTemplate));
- wsprintf(pszDateLine, szTemplate, StrFormatByteSize64(liFileSize.QuadPart, szNum, ARRAYSIZE(szNum)),
- szTmp);
- }
- else
- {
- // Simpy output the number to the string
- StrFormatByteSize64(liFileSize.QuadPart, pszDateLine, 64);
- if (liFileSize.QuadPart == 0)
- return FALSE;
- }
- return TRUE; // valid data in the strings
- }
- // hide the cancel button and move "Yes" and "No" over to the right positions.
- //
- // "Yes" is IDYES
- // "No" is IDNO
- //
- #define HideYesToAllAndCancel(hdlg) HideConfirmButtons(hdlg, IDCANCEL)
- #define HideYesToAllAndNo(hdlg) HideConfirmButtons(hdlg, IDNO)
- void HideConfirmButtons(HWND hdlg, int idHide)
- {
- HWND hwndCancel = GetDlgItem(hdlg, IDCANCEL);
- HWND hwndYesToAll = GetDlgItem(hdlg, IDD_YESTOALL);
- if (hwndCancel) {
- RECT rcCancel;
- HWND hwndNo;
- GetWindowRect(hwndCancel, &rcCancel);
- hwndNo = GetDlgItem(hdlg, IDNO);
- if (hwndNo) {
- RECT rcNo;
- HWND hwndYes;
- GetWindowRect(hwndNo, &rcNo);
- MapWindowRect(NULL, hdlg, &rcCancel);
- SetWindowPos(hwndNo, NULL, rcCancel.left, rcCancel.top,
- 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
- hwndYes = GetDlgItem(hdlg, IDYES);
- if (hwndYes) {
- MapWindowRect(NULL, hdlg, &rcNo);
- SetWindowPos(hwndYes, NULL, rcNo.left, rcNo.top,
- 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
- }
- }
- // Although the function is called "Hide", we actually destroy
- // the windows, because keyboard accelerators for hidden windows
- // are still active!
- if (hwndYesToAll)
- DestroyWindow(hwndYesToAll);
- DestroyWindow( GetDlgItem(hdlg, idHide));
- }
- }
- int MoveDlgItem(HWND hDlg, UINT id, int y)
- {
- RECT rc;
- HWND hwnd = GetDlgItem(hDlg, id);
- if (hwnd) {
- GetWindowRect(hwnd, &rc);
- MapWindowRect(NULL, hDlg, &rc);
- SetWindowPos(hwnd, NULL, rc.left, y, 0,0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
- return rc.top - y; // return how much it moved
- }
- return 0;
- }
- void ShrinkDialog(HWND hDlg, UINT idText)
- {
- RECT rc;
- int y;
- HWND hwnd;
- hwnd = GetDlgItem(hDlg, idText);
- ASSERT(hwnd);
- GetWindowRect(hwnd, &rc);
- MapWindowRect(NULL, hDlg, &rc);
- y = rc.bottom + 12;
- // move all the buttons
- MoveDlgItem(hDlg, IDNO, y);
- MoveDlgItem(hDlg, IDCANCEL, y);
- MoveDlgItem(hDlg, IDD_YESTOALL, y);
- y = MoveDlgItem(hDlg, IDYES, y);
- // now resize the entire dialog
- GetWindowRect(hDlg, &rc);
- SetWindowPos(hDlg, NULL, 0, 0, rc.right - rc.left, rc.bottom - y - rc.top, SWP_NOMOVE | SWP_NOZORDER |SWP_NOACTIVATE);
- }
- void InitConfirmDlg(HWND hDlg, CONFDLG_DATA *pcd)
- {
- TCHAR szMessage[255];
- TCHAR szDeleteWarning[80];
- TCHAR szSrc[32];
- TCHAR szFriendlyName[MAX_PATH];
- SHFILEINFO sfi;
- SHFILEINFO sfiDest;
- LPTSTR pszFileDest = NULL;
- LPTSTR pszMsg, pszSource;
- int i;
- int cxWidth;
- RECT rc;
- BOOL bIsARPWarning = pcd->bARPWarning;
- ASSERT((bIsARPWarning && (pcd->nSourceFiles == 1)) || (!bIsARPWarning));
- // get the size of the text boxes
- GetWindowRect(GetDlgItem(hDlg, pcd->idText), &rc);
- cxWidth = rc.right - rc.left;
- if (!bIsARPWarning && !pcd->bShowCancel)
- HideYesToAllAndCancel(hDlg);
- switch (pcd->nSourceFiles)
- {
- case -1:
- LoadString(HINST_THISDLL, IDS_SELECTEDFILES, szSrc, ARRAYSIZE(szSrc));
- pszSource = szSrc;
- break;
- case 1:
- if (bIsARPWarning)
- {
- TCHAR szTarget[MAX_PATH];
- DWORD cchFriendlyName = ARRAYSIZE(szFriendlyName);
- HRESULT hres = GetPathFromLinkFile(pcd->pFileSource, szTarget, ARRAYSIZE(szTarget));
- if (S_OK == hres)
- {
- if (SUCCEEDED(AssocQueryString(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME,
- szTarget, NULL, szFriendlyName, &cchFriendlyName)))
- {
- pszSource = szFriendlyName;
- }
- else
- {
- pszSource = PathFindFileName(szTarget);
- }
- }
- else if (S_FALSE == hres)
- {
- TCHAR szProductCode[MAX_PATH];
- szProductCode[0] = TEXT('');
- if ((ERROR_SUCCESS == MsiDecomposeDescriptor(szTarget, szProductCode, NULL, NULL, NULL)) &&
- (ERROR_SUCCESS == MsiGetProductInfo(szProductCode, INSTALLPROPERTY_PRODUCTNAME, szFriendlyName, &cchFriendlyName)))
- {
- pszSource = szFriendlyName;
- }
- else
- goto UNKNOWNAPP;
- }
- else
- {
- UNKNOWNAPP:
- LoadString(HINST_THISDLL, IDS_UNKNOWNAPPLICATION, szSrc, ARRAYSIZE(szSrc));
- pszSource = szSrc;
- }
- Static_SetIcon(GetDlgItem(hDlg, IDD_ARPINFORMATION),
- LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION)));
- }
- else
- {
- SHGetFileInfo(pcd->pFileSource,
- (pcd->fConfirm==CONFIRM_DELETE_FOLDER || pcd->fConfirm==CONFIRM_WONT_RECYCLE_FOLDER)? FILE_ATTRIBUTE_DIRECTORY : 0,
- &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
- pszSource = sfi.szDisplayName;
- PathCompactPath(NULL, pszSource, cxWidth);
- }
- break;
- default:
- pszSource = AddCommas(pcd->nSourceFiles, szSrc);
- break;
- }
- // if we're supposed to show the date info, grab the icons and format the date string
- if (pcd->bShowDates)
- {
- SHFILEINFO sfi2;
- TCHAR szDateSrc[64], szDateDest[64];
- BuildDateLine(szDateSrc, pcd->pfdSource, pcd->pFileSource);
- SetDlgItemText(hDlg, IDD_FILEINFO_NEW, szDateSrc);
- BuildDateLine(szDateDest, pcd->pfdDest, pcd->pFileDest);
- SetDlgItemText(hDlg, IDD_FILEINFO_OLD, szDateDest);
- SHGetFileInfo(pcd->pFileDest, pcd->pfdDest ? pcd->pfdDest->dwFileAttributes : 0, &sfi2, SIZEOF(sfi2),
- pcd->pfdDest ? (SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_LARGEICON) : (SHGFI_ICON|SHGFI_LARGEICON));
- ReplaceDlgIcon(hDlg, IDD_ICON_OLD, sfi2.hIcon);
- SHGetFileInfo(pcd->pFileSource, pcd->pfdSource ? pcd->pfdSource->dwFileAttributes : 0, &sfi2, SIZEOF(sfi2),
- pcd->pfdSource ? (SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_LARGEICON) : (SHGFI_ICON|SHGFI_LARGEICON));
- ReplaceDlgIcon(hDlg, IDD_ICON_NEW, sfi2.hIcon);
- }
- if (!bIsARPWarning)
- {
- // there are multiple controls:
- // IDD_TEXT contains regular text (normal file/folder)
- // IDD_TEXT1 - IDD_TEXT4 contain optional secondary text
- for (i = IDD_TEXT; i <= IDD_TEXT4; i++)
- {
- if (i == pcd->idText)
- {
- szMessage[0] = 0;
- GetDlgItemText(hDlg, i, szMessage, ARRAYSIZE(szMessage));
- }
- else
- {
- HWND hwndCtl = GetDlgItem(hDlg, i);
- if (hwndCtl)
- ShowWindow(hwndCtl, SW_HIDE);
- }
- }
- }
- else
- GetDlgItemText(hDlg, IDD_ARPWARNINGTEXT, szMessage, ARRAYSIZE(szMessage));
- // REVIEW Is there some better way? The code above always hides
- // this control, and I don't see a way around this
- if (pcd->pStreamNames)
- {
- SetDlgItemText(hDlg, IDD_TEXT1, pcd->pStreamNames);
- ShowWindow(GetDlgItem(hDlg, IDD_TEXT1), SW_SHOW);
- }
- if (pcd->bShrinkDialog)
- ShrinkDialog(hDlg, pcd->idText);
- if (pcd->pFileDest)
- {
- SHGetFileInfo(pcd->pFileDest, 0,
- &sfiDest, SIZEOF(sfiDest), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
- pszFileDest = sfiDest.szDisplayName;
- PathCompactPath(NULL, pszFileDest, cxWidth);
- }
- if (pcd->uDeleteWarning)
- {
- LPITEMIDLIST pidl;
- if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_BITBUCKET, &pidl)))
- {
- SHFILEINFO fi;
- if (SHGetFileInfo((LPCTSTR)pidl, 0, &fi, sizeof(fi), SHGFI_PIDL | SHGFI_ICON |SHGFI_LARGEICON))
- {
- ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, fi.hIcon);
- }
- ILFree(pidl);
- }
- LoadString(HINST_THISDLL, pcd->uDeleteWarning, szDeleteWarning, ARRAYSIZE(szDeleteWarning));
- }
- else
- szDeleteWarning[0] = 0;
- if (pcd->bFireIcon)
- {
- ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_NUKEFILE), IMAGE_ICON, 0, 0, LR_LOADMAP3DCOLORS));
- }
- pszMsg = ShellConstructMessageString(HINST_THISDLL, szMessage,
- pszSource, pszFileDest, szDeleteWarning);
- if (pszMsg)
- {
- SetDlgItemText(hDlg, pcd->idText, pszMsg);
- LocalFree(pszMsg);
- }
- if (bIsARPWarning)
- {
- TCHAR szLinkWindow[MAX_PATH];
- GetDlgItemText(hDlg, IDD_ARPLINKWINDOW, szLinkWindow, ARRAYSIZE(szLinkWindow));
- pszMsg = ShellConstructMessageString(HINST_THISDLL, szLinkWindow, pszSource);
- if (pszMsg)
- {
- SetDlgItemText(hDlg, IDD_ARPLINKWINDOW, pszMsg);
- LocalFree(pszMsg);
- }
- }
- }
- BOOL_PTR CALLBACK ConfirmDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
- {
- CONFDLG_DATA *pcd = (CONFDLG_DATA *)GetWindowLongPtr(hDlg, DWLP_USER);
- switch (wMsg) {
- case WM_INITDIALOG:
- SetWindowLongPtr(hDlg, DWLP_USER, lParam);
- pcd = (CONFDLG_DATA *)lParam;
- pcd->InitConfirmDlg(hDlg, pcd);
- break;
- case WM_DESTROY:
- // Handle case where the allocation of the PCD failed.
- if (!pcd)
- break;
- if (pcd->bShowDates)
- {
- ReplaceDlgIcon(hDlg, IDD_ICON_NEW, NULL);
- ReplaceDlgIcon(hDlg, IDD_ICON_OLD, NULL);
- }
- if (pcd->bARPWarning)
- {
- ReplaceDlgIcon(hDlg, IDD_ARPINFORMATION, NULL);
- }
- ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, NULL);
- break;
- case WM_COMMAND:
- if (!pcd)
- break;
- switch (GET_WM_COMMAND_ID(wParam, lParam))
- {
- case IDNO:
- if (GetKeyState(VK_SHIFT) < 0) // force NOTOALL
- {
- // I use the fYesToAllMask here. There used to be a fNoToAllMask but I
- // removed it. When you select "No To All" what you are saying is that
- // anything I would be saying yes to all for I am actually saying "no to
- // all" for. I feel that it is confusing and unnecessary to have both.
- pcd->pcd->fNoToAll |= pcd->fYesToAllMask;
- }
- EndDialog(hDlg, IDNO);
- break;
- case IDD_YESTOALL:
- // pcd is the confirmation data for just this file/folder. pcd->pcd is the
- // confirm data for the entire copy operation. When we get a Yes To All we
- // remove the coresponding bits from the entire operation.
- pcd->pcd->fConfirm &= ~pcd->fYesToAllMask;
- EndDialog(hDlg, IDYES);
- break;
- case IDYES:
- // There are some messages that we only want to tell the use once even if they
- // select Yes instead of Yes To All. As such we sometimes remove bits from the
- // global confirm state even on a simple Yes. This mask is usually zero.
- pcd->pcd->fConfirm &= ~pcd->fYesMask;
- EndDialog(hDlg, IDYES);
- break;
- case IDCANCEL:
- EndDialog(hDlg, IDCANCEL);
- break;
- }
- break;
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->code)
- {
- case NM_RETURN:
- case NM_CLICK:
- {
- TCHAR szModule[MAX_PATH];
- if (GetSystemDirectory(szModule, ARRAYSIZE(szModule)))
- {
- if (PathAppend(szModule, TEXT("appwiz.cpl")))
- {
- TCHAR szParam[1 + MAX_PATH + 2 + MAX_CCH_CPLNAME]; // See MakeCPLCommandLine function
- TCHAR szAppwiz[64];
- LoadString(g_hinst, IDS_APPWIZCPL, szAppwiz, SIZECHARS(szAppwiz));
- MakeCPLCommandLine(szModule, szAppwiz, szParam, ARRAYSIZE(szParam));
- SHRunControlPanelEx(szParam, NULL, FALSE);
- }
- }
- EndDialog(hDlg, IDNO);
- }
- break;
- }
- break;
- default:
- return FALSE;
- }
- return TRUE;
- }
- BOOL IgnoreSysAndRO(LPCTSTR lpszFileName)
- {
- TCHAR szIniFile[MAX_PATH];
- //
- // if there's a desktop.ini with a .ShellClassInfo ConfirmFileOp=0
- // then we can ignore the SYS and RO attributes of a file
- //
- if (lpszFileName && (lstrlen(lpszFileName) + lstrlen(c_szDesktopIni) + 2 < MAX_PATH))
- {
- lstrcpy(szIniFile, lpszFileName);
- PathAppend(szIniFile, TEXT("desktop.ini"));
- return GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("ConfirmFileOp"), TRUE, szIniFile) == 0;
- }
- return FALSE;
- }
- #define FILE_ATTRIBUTE_SUPERHIDDEN (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
- #define IS_SYSTEM_HIDDEN(dw) ((dw & FILE_ATTRIBUTE_SUPERHIDDEN) == FILE_ATTRIBUTE_SUPERHIDDEN)
- void SetConfirmMaskAndText(CONFDLG_DATA *pcd, DWORD dwFileAttributes, LPCTSTR pszFile)
- {
- if (IS_SYSTEM_HIDDEN(dwFileAttributes) && !ShowSuperHidden())
- {
- dwFileAttributes &= ~FILE_ATTRIBUTE_SUPERHIDDEN;
- }
- // We only need to do this if this is a directory as we are going to append on
- // desktop.ini onto the path and do GetPrivatePfofileInt calls which does not
- // make sense if we pass in something like C:foo.exe
- // Probably a minor perf win.
- if ((dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) &&
- (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- (IgnoreSysAndRO(pszFile)))
- {
- dwFileAttributes &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY);
- }
- if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
- {
- pcd->fConfirm = CONFIRM_SYSTEM_FILE;
- pcd->fYesToAllMask |= CONFIRM_SYSTEM_FILE;
- pcd->idText = IDD_TEXT2;
- }
- else if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- {
- pcd->fConfirm = CONFIRM_READONLY_FILE;
- pcd->fYesToAllMask |= CONFIRM_READONLY_FILE;
- pcd->idText = IDD_TEXT1;
- }
- else if (pszFile && ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
- PathIsRegisteredProgram(pszFile))
- {
- pcd->fConfirm = CONFIRM_PROGRAM_FILE;
- pcd->fYesToAllMask |= CONFIRM_PROGRAM_FILE;
- pcd->idText = IDD_TEXT3;
- }
- }
- void PauseAnimation(COPY_STATE *pcs, BOOL bStop)
- {
- // only called from within the hwndProgress wndproc so assum it's there
- if (bStop)
- Animate_Stop(GetDlgItem(pcs->hwndProgress, IDD_ANIMATE));
- else
- Animate_Play(GetDlgItem(pcs->hwndProgress, IDD_ANIMATE), -1, -1, -1);
- }
- // confirm a file operation UI.
- //
- // this routine uses the CONFIRM_DATA in the copy state structure to
- // decide if it needs to put up a dailog to confirm the given file operation.
- //
- // in:
- // pcs current copy state (confirm flags, hwnd)
- // fConfirm only one bit may be set! (operation to confirm)
- // pFileSource source file
- // pFileDest optional destination file
- // pfdSource
- // pfdDest find data describing the destination
- //
- // returns:
- // IDYES
- // IDNO
- // IDCANCEL
- // ERROR_ (DE_) error codes (DE_MEMORY)
- //
- int ConfirmFileOp(HWND hwnd, COPY_STATE *pcs, CONFIRM_DATA *pcd,
- int nSourceFiles, int cDepth, CONFIRM_FLAG fConfirm,
- LPCTSTR pFileSource, const WIN32_FIND_DATA *pfdSource,
- LPCTSTR pFileDest, const WIN32_FIND_DATA *pfdDest,
- LPCTSTR pStreamNames)
- {
- int dlg;
- int ret;
- CONFDLG_DATA cdd;
- CONFIRM_FLAG fConfirmType;
- if (pcs)
- nSourceFiles = pcs->nSourceFiles;
- cdd.pfdSource = pfdSource;
- cdd.pfdDest = NULL; // pfdDest // BUGBUG: pfdDest is only partially filed in
- cdd.pFileSource = pFileSource;
- cdd.pFileDest = pFileDest;
- cdd.pcd = pcd;
- cdd.fConfirm = fConfirm; // default, changed below
- cdd.fYesMask = 0;
- cdd.fYesToAllMask = 0;
- cdd.nSourceFiles = 1; // default to individual file names in message
- cdd.idText = IDD_TEXT; // default string from the dlg template
- cdd.bShowCancel = ((nSourceFiles != 1) || cDepth);
- cdd.uDeleteWarning = 0;
- cdd.bFireIcon = FALSE;
- cdd.bShowDates = FALSE;
- cdd.bShrinkDialog = FALSE;
- cdd.InitConfirmDlg = InitConfirmDlg;
- cdd.pStreamNames = NULL;
- cdd.bARPWarning = FALSE;
- fConfirmType = fConfirm & CONFIRM_FLAG_TYPE_MASK;
- switch (fConfirmType)
- {
- case CONFIRM_DELETE_FILE:
- case CONFIRM_DELETE_FOLDER:
- {
- BOOL bIsFolderShortcut = FALSE;
- cdd.bShrinkDialog = TRUE;
- // find data for source is in pdfDest
- if ((nSourceFiles != 1) && (pcd->fConfirm & CONFIRM_MULTIPLE))
- {
- // this is the special CONFIRM_MULTIPLE case (usuall SHIFT+DELETE, or
- // SHIFT+DRAG to Recycle Bin). if the user says yes to this, they
- // basically get no more warnings.
- cdd.nSourceFiles = nSourceFiles;
- if ((fConfirm & CONFIRM_WASTEBASKET_PURGE) ||
- (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO)) ||
- !BBWillRecycle(cdd.pFileSource, NULL))
- {
- // have the fire icon and the REALLY delete warning
- cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
- cdd.bFireIcon = TRUE;
- if (pcs)
- pcs->fFlags &= ~FOF_ALLOWUNDO;
- if (nSourceFiles == -1)
- {
- // -1 indicates that there were > MAX_EMPTY_FILES files, so we stoped counting
- // them all up for perf. We use the more generic message in this case.
- cdd.idText = IDD_TEXT3;
- }
- else
- {
- // use the "are you sure you want to nuke XX files?" message
- cdd.idText = IDD_TEXT4;
- }
- }
- else
- {
- // uDeleteWarning must be set for the proper recycle icon to be loaded.
- cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
- }
- if (!pcs || !pcs->fNoConfirmRecycle)
- {
- ret = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DELETE_MULTIPLE), hwnd, ConfirmDlgProc, (LPARAM)&cdd);
- if (ret != IDYES)
- return IDCANCEL;
- }
- // clear all other possible warnings
- pcd->fConfirm &= ~(CONFIRM_MULTIPLE | CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER);
- cdd.fConfirm &= ~(CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER);
- cdd.nSourceFiles = 1; // use individual file name
- }
- SetConfirmMaskAndText(&cdd, pfdDest->dwFileAttributes, cdd.pFileSource);
- if ((pfdDest->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && PathIsShortcut(cdd.pFileSource))
- {
- // Its a folder and its a shortcut... must be a FolderShortcut!
- bIsFolderShortcut = TRUE;
- // since its a folder, we need to clear out all of these warnings
- cdd.fYesMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
- cdd.fYesToAllMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
- }
- // we want to treat FolderShortcuts as "files" instead of folders. We do this so we don't display dialogs
- // that say stuff like "do you want to delete this and all of its contents" when to the user, this looks like
- // an item instead of a folder (eg nethood shortcut).
- if ((fConfirmType == CONFIRM_DELETE_FILE) || bIsFolderShortcut)
- {
- dlg = DLG_DELETE_FILE;
- if ((nSourceFiles == 1) && PathIsShortcutToProgram(cdd.pFileSource))
- {
- LinkWindow_RegisterClass();
- dlg = DLG_DELETE_FILE_ARP;
- cdd.idText = IDD_ARPWARNINGTEXT;
- cdd.bShrinkDialog = FALSE;
- cdd.bARPWarning = TRUE;
- }
- if ((fConfirm & CONFIRM_WASTEBASKET_PURGE) ||
- (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO)) ||
- !BBWillRecycle(cdd.pFileSource, NULL))
- {
- // we are really nuking it, so show the appropriate icon/dialog
- cdd.bFireIcon = TRUE;
- if (pcs)
- {
- pcs->fFlags &= ~FOF_ALLOWUNDO;
- }
- cdd.uDeleteWarning = IDS_FILEDELETEWARNING;
- if (cdd.idText == IDD_TEXT)
- {
- cdd.idText = IDD_TEXT4;
- }
- }
- else
- {
- // we are recycling it
- cdd.uDeleteWarning = IDS_FILERECYCLEWARNING;
- }
- }
- else
- {
- // fConfirmType == CONFIRM_DELETE_FOLDER
- if (pcs)
- {
- // show cancel on NEXT confirm dialog
- pcs->nSourceFiles = -1;
- }
- cdd.fYesMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
- cdd.fYesToAllMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
- dlg = DLG_DELETE_FOLDER;
- if ((fConfirm & CONFIRM_WASTEBASKET_PURGE) ||
- (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO)) ||
- !BBWillRecycle(cdd.pFileSource, NULL))
- {
- // we are really nuking it, so show the appropriate icon/dialog
- cdd.bFireIcon = TRUE;
- if (pcs)
- {
- pcs->fFlags &= ~FOF_ALLOWUNDO;
- }
- cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
- }
- else
- {
- // we are recycling it
- cdd.uDeleteWarning = IDS_FOLDERRECYCLEWARNING;
- }
- }
- if (pcs && pcs->fNoConfirmRecycle)
- {
- cdd.fConfirm = 0;
- }
- }
- break;
- case CONFIRM_WONT_RECYCLE_FILE:
- case CONFIRM_WONT_RECYCLE_FOLDER:
- cdd.bShrinkDialog = TRUE;
- cdd.nSourceFiles = 1;
- cdd.bFireIcon = TRUE;
- cdd.idText = IDD_TEXT;
- cdd.fYesMask = CONFIRM_MULTIPLE;
- cdd.fConfirm = fConfirmType;
- cdd.fYesToAllMask = fConfirmType | CONFIRM_MULTIPLE;
- // set the dialog to be file or folder
- if (fConfirmType == CONFIRM_WONT_RECYCLE_FOLDER)
- {
- dlg = DLG_WONT_RECYCLE_FOLDER;
- }
- else
- {
- dlg = DLG_WONT_RECYCLE_FILE;
- }
- break;
- case CONFIRM_PATH_TOO_LONG:
- cdd.bShrinkDialog = TRUE;
- cdd.nSourceFiles = 1;
- cdd.bFireIcon = TRUE;
- cdd.idText = IDD_TEXT;
- cdd.fYesMask = CONFIRM_MULTIPLE;
- cdd.fConfirm = CONFIRM_PATH_TOO_LONG;
- cdd.fYesToAllMask = CONFIRM_PATH_TOO_LONG | CONFIRM_MULTIPLE;
- dlg = DLG_PATH_TOO_LONG;
- break;
- case CONFIRM_WONT_RECYCLE_OFFLINE:
- cdd.bShrinkDialog = TRUE;
- cdd.nSourceFiles = 1;
- cdd.bFireIcon = TRUE;
- cdd.idText = IDD_TEXT;
- cdd.fYesMask = CONFIRM_MULTIPLE;
- cdd.fConfirm = fConfirmType;
- cdd.fYesToAllMask = fConfirmType | CONFIRM_MULTIPLE;
- dlg = DLG_WONT_RECYCLE_OFFLINE;
- break;
- case CONFIRM_STREAMLOSS:
- cdd.bShrinkDialog = FALSE;
- cdd.nSourceFiles = 1;
- cdd.idText = IDD_TEXT;
- cdd.fConfirm = CONFIRM_STREAMLOSS;
- cdd.fYesToAllMask = CONFIRM_STREAMLOSS;
- cdd.pStreamNames = pStreamNames;
- dlg = DLG_STREAMLOSS_ON_COPY;
- break;
- case CONFIRM_FAILED_ENCRYPT:
- cdd.bShrinkDialog = FALSE;
- cdd.nSourceFiles = nSourceFiles;
- cdd.idText = IDD_TEXT;
- cdd.bShowCancel = TRUE;
- cdd.fConfirm = CONFIRM_FAILED_ENCRYPT;
- cdd.fYesToAllMask = CONFIRM_FAILED_ENCRYPT;
- dlg = DLG_FAILED_ENCRYPT;
- break;
- case CONFIRM_REPLACE_FILE:
- cdd.bShowDates = TRUE;
- cdd.fYesToAllMask = CONFIRM_REPLACE_FILE;
- SetConfirmMaskAndText(&cdd, pfdDest->dwFileAttributes, NULL);
- dlg = DLG_REPLACE_FILE;
- break;
- case CONFIRM_REPLACE_FOLDER:
- cdd.bShowCancel = TRUE;
- if (pcs) pcs->nSourceFiles = -1; // show cancel on NEXT confirm dialog
- // this implies operations on the files
- cdd.fYesMask = CONFIRM_REPLACE_FILE;
- cdd.fYesToAllMask = CONFIRM_REPLACE_FILE | CONFIRM_REPLACE_FOLDER;
- dlg = DLG_REPLACE_FOLDER;
- break;
- case CONFIRM_MOVE_FILE:
- cdd.fYesToAllMask = CONFIRM_MOVE_FILE;
- SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, NULL);
- dlg = DLG_MOVE_FILE;
- break;
- case CONFIRM_MOVE_FOLDER:
- cdd.bShowCancel = TRUE;
- cdd.fYesToAllMask = CONFIRM_MOVE_FOLDER;
- SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, cdd.pFileSource);
- dlg = DLG_MOVE_FOLDER;
- break;
- case CONFIRM_RENAME_FILE:
- SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, NULL);
- dlg = DLG_RENAME_FILE;
- break;
- case CONFIRM_RENAME_FOLDER:
- cdd.bShowCancel = TRUE;
- if (pcs) pcs->nSourceFiles = -1; // show cancel on NEXT confirm dialog
- SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, cdd.pFileSource);
- dlg = DLG_RENAME_FOLDER;
- break;
- default:
- DebugMsg(DM_WARNING, TEXT("bogus confirm option"));
- return IDCANCEL;
- }
- // Does this operation need to be confirmed?
- if (pcd->fConfirm & cdd.fConfirm)
- {
- // Has the user already said "No To All" for this operation?
- if ((pcd->fNoToAll & cdd.fConfirm) == cdd.fConfirm)
- {
- ret = IDNO;
- }
- else
- {
- // HACK for multimon, make sure the file operation dialog box comes
- // up on the correct monitor
- POINT ptInvoke;
- HWND hwndPos = NULL;
- if ((GetNumberOfMonitors() > 1) && GetCursorPos(&ptInvoke))
- {
- HMONITOR hMon = MonitorFromPoint(ptInvoke, MONITOR_DEFAULTTONULL);
- if (hMon)
- {
- hwndPos = _CreateStubWindow(&ptInvoke, hwnd);
- }
- }
- ret = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(dlg), (hwndPos ? hwndPos : hwnd), ConfirmDlgProc, (LPARAM)&cdd);
- if (hwndPos)
- DestroyWindow(hwndPos);
- if (ret == -1)
- ret = DE_INSMEM;
- }
- }
- else
- {
- ret = IDYES;
- }
- return ret;
- }
- //
- // DTNIsParentConnectOrigin()
- //
- // When a folder ("c:foo files") is moved to a different drive ("a:"), the source and the
- // destinations have different roots, and therefore the "fRecursive" flag is turned ON by default.
- // This results in confirmations obtained for the individual files ("c:foo filesaaa.gif")
- // rather than the folder itself. We need to first find the parent and then save the confirmation
- // in the connected element of it's parent. This function gets the top-most parent and then
- // checks to see if it is a connect origin and if so returns that parent pointer.
- //
- PDIRTREENODE DTNGetConnectOrigin(PDIRTREENODE pdtn)
- {
- PDIRTREENODE pdtnParent = pdtn;
- //Get the top-level parent of the given node.
- while(pdtn)
- {
- pdtnParent = pdtn;
- pdtn = pdtn->pdtnParent;
- }
- //Now check if the parent is a connect origin.
- if (pdtnParent && DTNIsConnectOrigin(pdtnParent))
- return pdtnParent; //If so, return him.
- else
- return NULL;
- }
- //
- // CachedConfirmFileOp()
- //
- // When a file("foo.htm") is moved/copied, we may put up a confirmation dialog in case
- // of a conflict and the end-user might have responded saying "Yes", "no" etc., When the
- // corresponding connected element ("foo files") is also moved/copied etc., we should NOT put up
- // a confirmation dialog again. We must simply store the answer to the original confirmation and
- // use it later.
- //
- // What this function does is: if the given node is a connected element, it simply retrieves the
- // confirmation for the original operation and returns. If the given element is NOT a connected
- // element, then this function calls the ConfirmFileOp and stores the confirmation result in
- // it's connected element sothat, it later it can be used by the connected element.
- //
- int CachedConfirmFileOp(HWND hwnd, COPY_STATE *pcs, CONFIRM_DATA *pcd,
- int nSourceFiles, int cDepth, CONFIRM_FLAG fConfirm,
- LPCTSTR pFileSource, const WIN32_FIND_DATA *pfdSource,
- LPCTSTR pFileDest, const WIN32_FIND_DATA *pfdDest,
- LPCTSTR pStreamNames)
- {
- int result;
- //See if this is a connected item.
- if (DTNIsConnected(pcs->dth.pdtnCurrent))
- {
- // Since this is a connected item, the confirmation must already have been obtained from
- // the user and get it from the cache!
- result = DTNGetConfirmationResult(pcs->dth.pdtnCurrent);
- }
- else
- {
- PDIRTREENODE pdtnConnectOrigin;
- result = ConfirmFileOp(hwnd, pcs, pcd, nSourceFiles, cDepth, fConfirm, pFileSource,
- pfdSource, pFileDest, pfdDest, pStreamNames);
- //Check if this node has a connection.
- if (pdtnConnectOrigin = DTNGetConnectOrigin(pcs->dth.pdtnCurrent))
- {
- pdtnConnectOrigin->pdtnConnected->ConnectedInfo.dwConfirmation = result;
- // BUGBUG: Can we check for the result to be IDCANCEL or IDNO and if so make the
- // connected node a Dummy? Currently this won't work because current code assumes
- // that dummy nodes do not have children. This connected node might have some children.
- // if ((result == IDCANCEL) || (result == IDNO))
- // pdtnConnectOrigin->pdtnConnected->fDummy = TRUE;
- }
- }
- return result;
- }
- void GuessAShortName(LPCTSTR p, LPTSTR szT)
- {
- int i, j, fDot, cMax;
- // BUGBUG: use AnsiNext here?
- for (i = j = fDot = 0, cMax = 8; *p; p++) {
- if (*p == TEXT('.')) {
- // if there was a previous dot, step back to it
- // this way, we get the last extension
- if (fDot)
- i -= j+1;
- // set number of chars to 0, put the dot in
- j = 0;
- szT[i++] = TEXT('.');
- // remember we saw a dot and set max 3 chars.
- fDot = TRUE;
- cMax = 3;
- } else if (j < cMax && (PathGetCharType(*p) & GCT_SHORTCHAR)) {
- // if *p is a lead byte, we move forward one more
- if (IsDBCSLeadByte(*p)) {
- szT[i] = *p++;
- if (++j >= cMax)
- continue;
- ++i;
- }
- j++;
- szT[i++] = *p;
- }
- }
- szT[i] = 0;
- }
- /* GetNameDialog
- *
- * Runs the dialog box to prompt the user for a new filename when copying
- * or moving from HPFS to FAT.
- */
- typedef struct {
- LPTSTR pszDialogFrom;
- LPTSTR pszDialogTo;
- BOOL bShowCancel;
- } GETNAME_DATA;
- BOOL_PTR CALLBACK GetNameDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
- {
- TCHAR szT[14];
- TCHAR szTo[MAX_PATH];
- GETNAME_DATA * pgn = (GETNAME_DATA *)GetWindowLongPtr(hDlg, DWLP_USER);
- switch (wMsg)
- {
- case WM_INITDIALOG:
- SetWindowLongPtr(hDlg, DWLP_USER, lParam);
- pgn = (GETNAME_DATA *)lParam;
- // inform the user of the old name
- PathSetDlgItemPath(hDlg, IDD_FROM, pgn->pszDialogFrom);
- // directory the file will go into
- PathRemoveFileSpec(pgn->pszDialogTo);
- PathSetDlgItemPath(hDlg, IDD_DIR, pgn->pszDialogTo);
- // generate a guess for the new name
- GuessAShortName(PathFindFileName(pgn->pszDialogFrom), szT);
- lstrcpy(szTo, pgn->pszDialogTo);
- PathAppend(szTo, szT);
- // make sure that name is unique
- PathYetAnotherMakeUniqueName(szTo, szTo, NULL, NULL);
- SetDlgItemText(hDlg, IDD_TO, PathFindFileName(szTo));
- SendDlgItemMessage(hDlg, IDD_TO, EM_LIMITTEXT, 13, 0L);
- SHAutoComplete(GetDlgItem(hDlg, IDD_TO), 0);
- if (!pgn->bShowCancel)
- HideYesToAllAndNo(hDlg);
- break;
- case WM_COMMAND:
- switch (GET_WM_COMMAND_ID(wParam, lParam))
- {
- case IDD_YESTOALL:
- case IDYES:
- GetDlgItemText(hDlg, IDD_TO, szT, ARRAYSIZE(szT));
- PathAppend(pgn->pszDialogTo, szT);
- PathQualify(pgn->pszDialogTo);
- // fall through
- case IDNO:
- case IDCANCEL:
- EndDialog(hDlg,GET_WM_COMMAND_ID(wParam, lParam));
- break;
- case IDD_TO:
- {
- LPCTSTR p;
- GetDlgItemText(hDlg, IDD_TO, szT, ARRAYSIZE(szT));
- for (p = szT; *p; p = CharNext(p))
- {
- if (!(PathGetCharType(*p) & GCT_SHORTCHAR))
- break;
- }
- EnableWindow(GetDlgItem(hDlg,IDYES), ((!*p) && (p != szT)));
- }
- break;
- default:
- return FALSE;
- }
- break;
- default:
- return FALSE;
- }
- return TRUE;
- }
- int GetNameDialog(HWND hwnd, COPY_STATE *pcs, BOOL fMultiple,UINT wOp, LPTSTR pFrom, LPTSTR pTo)
- {
- int iRet;
- // if we don't want to confirm this, just mock up a string and return ok
- if (!(pcs->cd.fConfirm & CONFIRM_LFNTOFAT))
- {
- TCHAR szTemp[MAX_PATH];
- GuessAShortName(PathFindFileName(pFrom), szTemp);
- PathRemoveFileSpec(pTo);
- PathAppend(pTo, szTemp);
- // make sure that name is unique
- PathYetAnotherMakeUniqueName(pTo, pTo, NULL, NULL);
- iRet = IDYES;
- }
- else
- {
- GETNAME_DATA gn;
- gn.pszDialogFrom = pFrom;
- gn.pszDialogTo = pTo;
- gn.bShowCancel = fMultiple;
- iRet = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_LFNTOFAT), hwnd, GetNameDlgProc, (LPARAM)(GETNAME_DATA *)&gn);
- if (iRet == IDD_YESTOALL)
- pcs->cd.fConfirm &= ~CONFIRM_LFNTOFAT;
- }
- return iRet;
- }
- STDAPI_(void) SHFreeNameMappings(void *hNameMappings)
- {
- HDSA hdsaRenamePairs = (HDSA)hNameMappings;
- int i;
- if (!hdsaRenamePairs)
- return;
- i = DSA_GetItemCount(hdsaRenamePairs) - 1;
- for (; i >= 0; i--)
- {
- SHNAMEMAPPING FAR* prp = DSA_GetItemPtr(hdsaRenamePairs, i);
- Free(prp->pszOldPath);
- Free(prp->pszNewPath);
- }
- DSA_Destroy(hdsaRenamePairs);
- }
- void _ProcessNameMappings(LPTSTR pszTarget, HDSA hdsaRenamePairs)
- {
- int i;
- if (!hdsaRenamePairs)
- return;
- for (i = DSA_GetItemCount(hdsaRenamePairs) - 1; i >= 0; i--)
- {