/*****************************************************************************\ FILE: newmenu.cpp DESCRIPTION: The file supports the "New" menu to create new items on the FTP server. This currently only supports Folders but hopefully it will support other items later. \*****************************************************************************/ #include "priv.h" #include "util.h" #include "newmenu.h" // This is used to surf the hwnds to find the one we need to // hack because IShellView2::SelectAndPositionItem() isn't implemented // on Browser Only. #define DEFVIEW_CLASS_BROWSERONLYA "SHELLDLL_DefView" ///////////////////////////////////////////////////////////////////////// /////// Private helpers ///////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// LPITEMIDLIST DV_GetPIDL(HWND hwndLV, int i) { LV_ITEM item; item.mask = LVIF_PARAM; item.iItem = i; item.iSubItem = 0; item.lParam = 0; if (i != -1) { ListView_GetItem(hwndLV, &item); } return (LPITEMIDLIST) item.lParam; } int DefView_FindItemHack(CFtpFolder * pff, HWND hwndListView, LPCITEMIDLIST pidl) { int nIndex; int nItemsTotal; nItemsTotal = ListView_GetItemCount(hwndListView); for (nIndex = 0; nItemsTotal > nIndex; nIndex++) { HRESULT hres = ResultFromShort(-1); LPITEMIDLIST pidlT = DV_GetPIDL(hwndListView, nIndex); if (!pidlT) return -1; hres = pff->CompareIDs(COL_NAME, pidl, pidlT); ASSERT(SUCCEEDED(hres)); if (FAILED(hres)) return -1; if (ShortFromResult(hres) == 0) { return nIndex; } } return -1; // not found } typedef struct tagFOLDERNAMECOMP { BOOL * pfFound; LPCWSTR pszFolderName; } FOLDERNAMECOMP; /*****************************************************************************\ FUNCTION: _ComparePidlAndFolderStr DESCRIPTION: Compare the pidl and folder name str. \*****************************************************************************/ int _ComparePidlAndFolderStr(LPVOID pvPidl, LPVOID pvFolderNameComp) { FOLDERNAMECOMP * pFolderNameComp = (FOLDERNAMECOMP *) pvFolderNameComp; LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl; WCHAR wzDisplayName[MAX_PATH]; BOOL fContinue = TRUE; if (EVAL(SUCCEEDED(FtpPidl_GetDisplayName(pidl, wzDisplayName, ARRAYSIZE(wzDisplayName))))) { if (!StrCmpW(wzDisplayName, pFolderNameComp->pszFolderName)) { *pFolderNameComp->pfFound = TRUE; fContinue = FALSE; } } return fContinue; // Continue looking? } /*****************************************************************************\ FUNCTION: _DoesFolderExist DESCRIPTION: Look thru all the items (files and folders) in this folder and see if any have the same name as pszFolderName. \*****************************************************************************/ BOOL _DoesFolderExist(LPCWSTR pszFolderName, CFtpDir * pfd) { BOOL fExist = FALSE; if (EVAL(pfd)) { CFtpPidlList * pPidlList = pfd->GetHfpl(); // This may fail, but the worst that will happen is that the new folder won't appear. // This happens when the cache is flushed. if (pPidlList) { FOLDERNAMECOMP folderNameComp = {&fExist, pszFolderName}; pPidlList->Enum(_ComparePidlAndFolderStr, (LPVOID) &folderNameComp); pPidlList->Release(); } } return fExist; } /*****************************************************************************\ FUNCTION: _CreateNewFolderName DESCRIPTION: Create the name of a new folder. \*****************************************************************************/ HRESULT _CreateNewFolderName(LPWSTR pszNewFolder, DWORD cchSize, CFtpDir * pfd) { HRESULT hr = S_OK; int nTry = 1; WCHAR wzTemplate[MAX_PATH]; wzTemplate[0] = 0; LoadStringW(HINST_THISDLL, IDS_NEW_FOLDER_FIRST, pszNewFolder, cchSize); while (_DoesFolderExist(pszNewFolder, pfd)) { if (0 == wzTemplate[0]) LoadStringW(HINST_THISDLL, IDS_NEW_FOLDER_TEMPLATE, wzTemplate, ARRAYSIZE(wzTemplate)); nTry++; // Try the next number. wnsprintf(pszNewFolder, cchSize, wzTemplate, nTry); } return hr; } /*****************************************************************************\ FUNCTION: _CreateNewFolder DESCRIPTION: Create the actual directory. \*****************************************************************************/ HRESULT CreateNewFolderCB(HINTERNET hint, HINTPROCINFO * phpi, LPVOID pvFCFS, BOOL * pfReleaseHint) { HRESULT hr = S_OK; FTPCREATEFOLDERSTRUCT * pfcfs = (FTPCREATEFOLDERSTRUCT *) pvFCFS; WIRECHAR wFilePath[MAX_PATH]; CWireEncoding * pWireEncoding = phpi->pfd->GetFtpSite()->GetCWireEncoding(); hr = pWireEncoding->UnicodeToWireBytes(NULL, pfcfs->pszNewFolderName, (phpi->pfd->IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wFilePath, ARRAYSIZE(wFilePath)); if (EVAL(SUCCEEDED(hr))) { hr = FtpCreateDirectoryWrap(hint, TRUE, wFilePath); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlNew; HINTERNET hIntFind; // For some reason, FtpFindFirstFile needs an '*' behind the name. StrCatBuffA(wFilePath, SZ_ASTRICSA, ARRAYSIZE(wFilePath)); hr = FtpFindFirstFilePidlWrap(hint, TRUE, NULL, pWireEncoding, wFilePath, &pidlNew, (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD), 0, &hIntFind); if (EVAL(SUCCEEDED(hr))) { // Notify the folder of the new item so the Shell Folder updates. // PERF: I worry about doing a FtpFindFirstFile() being too expensive onto to get the date correct // for SHChangeNotify(). FtpChangeNotify(phpi->hwnd, SHCNE_MKDIR, pfcfs->pff, phpi->pfd, pidlNew, NULL, TRUE); ILFree(pidlNew); InternetCloseHandle(hIntFind); } } } return hr; } ///////////////////////////////////////////////////////////////////////// /////// DLL Wide Functions ///////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// HRESULT CreateNewFolder(HWND hwnd, CFtpFolder * pff, CFtpDir * pfd, IUnknown * punkSite, BOOL fPosition, POINT point) { HRESULT hr = E_FAIL; CFtpDir * pfdTemp = NULL; if (!pfd) pfd = pfdTemp = pff->GetFtpDir(); if (EVAL(pfd)) { WCHAR wzNewFolderName[MAX_PATH]; // 1. Check if "New Folder" exists. // 2. Cycle thru names until a unique name is found. hr = _CreateNewFolderName(wzNewFolderName, ARRAYSIZE(wzNewFolderName), pfd); if (EVAL(SUCCEEDED(hr) && pfd)) { FTPCREATEFOLDERSTRUCT fcfs = {wzNewFolderName, pff}; // 3. Create a Directory with that name. hr = pfd->WithHint(NULL, hwnd, CreateNewFolderCB, (LPVOID) &fcfs, punkSite, pff); if (SUCCEEDED(hr)) { WIRECHAR wNewFolderWireName[MAX_PATH]; LPITEMIDLIST pidlFolder = NULL; CWireEncoding * pWireEncoding = pff->GetCWireEncoding(); // Give me UTF-8 baby. EVAL(SUCCEEDED(pWireEncoding->UnicodeToWireBytes(NULL, wzNewFolderName, (pfd->IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wNewFolderWireName, ARRAYSIZE(wNewFolderWireName)))); if (EVAL(SUCCEEDED(FtpItemID_CreateFake(wzNewFolderName, wNewFolderWireName, TRUE, FALSE, FALSE, &pidlFolder)))) { // Is this browser only? if (SHELL_VERSION_W95NT4 == GetShellVersion()) { HWND hwndDefView = NULL; // Yes, so we need to do this the hard way. // 1. ShellFolderView_SetItemPos(hwnd, pidlFolder, point.x, point.y); hwndDefView = FindWindowExA(hwnd, NULL, DEFVIEW_CLASS_BROWSERONLYA, NULL); if (EVAL(hwndDefView)) { HWND hwndListView = FindWindowExA(hwndDefView, NULL, WC_LISTVIEWA, NULL); if (EVAL(hwndListView)) { int nIndex = DefView_FindItemHack(pff, hwndListView, pidlFolder); if (EVAL(-1 != nIndex)) ListView_EditLabel(hwndListView, nIndex); } } } else { // No, so this won't be as hard. IShellView2 * pShellView2 = NULL; // ASSERT(punkSite); // Can happen when invoked from Captionbar. IUnknown_QueryService(punkSite, SID_DefView, IID_IShellView2, (void **)&pShellView2); if (!pShellView2) { IDefViewFrame * pdvf = NULL; IUnknown_QueryService(punkSite, SID_DefView, IID_IDefViewFrame, (void **)&pdvf); if (pdvf) // Can fail when invoked from caption bar. { EVAL(SUCCEEDED(pdvf->QueryInterface(IID_IShellView2, (void **) &pShellView2))); pdvf->Release(); } } if (pShellView2) // Can fail when invoked from the caption bar. Oh well, cry me a river. { if (fPosition) pShellView2->SelectAndPositionItem(pidlFolder, (SVSI_SELECT | SVSI_TRANSLATEPT | SVSI_EDIT), &point); else pShellView2->SelectItem(pidlFolder, (SVSI_EDIT | SVSI_SELECT)); pShellView2->Release(); } } ILFree(pidlFolder); } } else { // An error occured, so display UI. Most often because access is denied. DisplayWininetError(hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_NEWFOLDER, IDS_FTPERR_WININET, MB_OK, NULL); hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } } if (pfdTemp) pfdTemp->Release(); } return hr; }