mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
[SHELL32] Use correct PIDL and name when dropping to create a shortcut (#6999)
Ask for the editing name instead of the parsing name to get a suitable shortcut name for PIDL items.
This commit is contained in:
parent
089788a52a
commit
fa95a96e9b
3 changed files with 86 additions and 100 deletions
|
@ -900,7 +900,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
|
||||||
|
|
||||||
if (m_pPidl)
|
if (m_pPidl)
|
||||||
m_Header.dwFlags |= SLDF_HAS_ID_LIST;
|
m_Header.dwFlags |= SLDF_HAS_ID_LIST;
|
||||||
if (m_sPath)
|
if (m_sPath && *m_sPath && !(m_Header.dwFlags & SLDF_FORCE_NO_LINKINFO))
|
||||||
m_Header.dwFlags |= SLDF_HAS_LINK_INFO;
|
m_Header.dwFlags |= SLDF_HAS_LINK_INFO;
|
||||||
if (m_sDescription && *m_sDescription)
|
if (m_sDescription && *m_sDescription)
|
||||||
m_Header.dwFlags |= SLDF_HAS_NAME;
|
m_Header.dwFlags |= SLDF_HAS_NAME;
|
||||||
|
@ -936,7 +936,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_sPath)
|
if (m_Header.dwFlags & SLDF_HAS_LINK_INFO)
|
||||||
{
|
{
|
||||||
hr = Stream_WriteLocationInfo(stm, m_sPath, &volume);
|
hr = Stream_WriteLocationInfo(stm, m_sPath, &volume);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
|
|
@ -24,6 +24,35 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL (shell);
|
WINE_DEFAULT_DEBUG_CHANNEL (shell);
|
||||||
|
|
||||||
|
static void SHELL_StripIllegalFsNameCharacters(_Inout_ LPWSTR Buf)
|
||||||
|
{
|
||||||
|
for (LPWSTR src = Buf, dst = src;;)
|
||||||
|
{
|
||||||
|
*dst = *src;
|
||||||
|
if (!*dst)
|
||||||
|
break;
|
||||||
|
else if (wcschr(INVALID_FILETITLE_CHARACTERSW, *dst))
|
||||||
|
src = CharNextW(src);
|
||||||
|
else
|
||||||
|
++src, ++dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT
|
||||||
|
SHELL_LimitDropEffectToItemAttributes(_In_ IDataObject *pDataObject, _Inout_ PDWORD pdwEffect)
|
||||||
|
{
|
||||||
|
DWORD att = *pdwEffect & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK); // DROPEFFECT maps perfectly to these SFGAO bits
|
||||||
|
HRESULT hr = SHGetAttributesFromDataObject(pDataObject, att, &att, NULL);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return S_FALSE;
|
||||||
|
*pdwEffect &= ~(SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK) | att;
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetDefaultCopyMoveEffect()
|
||||||
|
{
|
||||||
|
// FIXME: When the source is on a different volume than the target, change default from move to copy
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* CFSDropTarget::_CopyItems
|
* CFSDropTarget::_CopyItems
|
||||||
|
@ -166,6 +195,7 @@ BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
|
||||||
{
|
{
|
||||||
/* TODO Windows does different drop effects if dragging across drives.
|
/* TODO Windows does different drop effects if dragging across drives.
|
||||||
i.e., it will copy instead of move if the directories are on different disks. */
|
i.e., it will copy instead of move if the directories are on different disks. */
|
||||||
|
GetDefaultCopyMoveEffect();
|
||||||
|
|
||||||
DWORD dwEffect = m_dwDefaultEffect;
|
DWORD dwEffect = m_dwDefaultEffect;
|
||||||
|
|
||||||
|
@ -193,18 +223,20 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
|
||||||
|
|
||||||
HMENU hpopupmenu = GetSubMenu(hmenu, 0);
|
HMENU hpopupmenu = GetSubMenu(hmenu, 0);
|
||||||
|
|
||||||
|
SHELL_LimitDropEffectToItemAttributes(pDataObject, &dwAvailableEffects);
|
||||||
if ((dwAvailableEffects & DROPEFFECT_COPY) == 0)
|
if ((dwAvailableEffects & DROPEFFECT_COPY) == 0)
|
||||||
DeleteMenu(hpopupmenu, IDM_COPYHERE, MF_BYCOMMAND);
|
DeleteMenu(hpopupmenu, IDM_COPYHERE, MF_BYCOMMAND);
|
||||||
else if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0)
|
if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0)
|
||||||
DeleteMenu(hpopupmenu, IDM_MOVEHERE, MF_BYCOMMAND);
|
DeleteMenu(hpopupmenu, IDM_MOVEHERE, MF_BYCOMMAND);
|
||||||
else if ((dwAvailableEffects & DROPEFFECT_LINK) == 0)
|
if ((dwAvailableEffects & DROPEFFECT_LINK) == 0 && (dwAvailableEffects & (DROPEFFECT_COPY | DROPEFFECT_MOVE)))
|
||||||
DeleteMenu(hpopupmenu, IDM_LINKHERE, MF_BYCOMMAND);
|
DeleteMenu(hpopupmenu, IDM_LINKHERE, MF_BYCOMMAND);
|
||||||
|
|
||||||
if ((*pdwEffect & DROPEFFECT_COPY))
|
GetDefaultCopyMoveEffect();
|
||||||
|
if (*pdwEffect & dwAvailableEffects & DROPEFFECT_COPY)
|
||||||
SetMenuDefaultItem(hpopupmenu, IDM_COPYHERE, FALSE);
|
SetMenuDefaultItem(hpopupmenu, IDM_COPYHERE, FALSE);
|
||||||
else if ((*pdwEffect & DROPEFFECT_MOVE))
|
else if (*pdwEffect & dwAvailableEffects & DROPEFFECT_MOVE)
|
||||||
SetMenuDefaultItem(hpopupmenu, IDM_MOVEHERE, FALSE);
|
SetMenuDefaultItem(hpopupmenu, IDM_MOVEHERE, FALSE);
|
||||||
else if ((*pdwEffect & DROPEFFECT_LINK))
|
else if (dwAvailableEffects & DROPEFFECT_LINK)
|
||||||
SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE);
|
SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE);
|
||||||
|
|
||||||
/* FIXME: We need to support shell extensions here */
|
/* FIXME: We need to support shell extensions here */
|
||||||
|
@ -223,6 +255,7 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
|
||||||
NULL,
|
NULL,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
SetForegroundWindow(hwndDummy); // Required for aborting by pressing Esc when dragging from Explorer to desktop
|
||||||
UINT uCommand = TrackPopupMenu(hpopupmenu,
|
UINT uCommand = TrackPopupMenu(hpopupmenu,
|
||||||
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY,
|
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY,
|
||||||
pt.x, pt.y, 0, hwndDummy, NULL);
|
pt.x, pt.y, 0, hwndDummy, NULL);
|
||||||
|
@ -548,125 +581,83 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
|
|
||||||
if (bLinking)
|
if (bLinking)
|
||||||
{
|
{
|
||||||
WCHAR wszPath[MAX_PATH];
|
WCHAR wszNewLnk[MAX_PATH];
|
||||||
WCHAR wszTarget[MAX_PATH];
|
|
||||||
|
|
||||||
TRACE("target path = %s\n", debugstr_w(m_sPathTarget));
|
TRACE("target path = %s\n", debugstr_w(m_sPathTarget));
|
||||||
|
|
||||||
/* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
|
/* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
|
||||||
for (UINT i = 0; i < lpcida->cidl; i++)
|
for (UINT i = 0; i < lpcida->cidl; i++)
|
||||||
{
|
{
|
||||||
// Find out which file we're linking.
|
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlFull;
|
||||||
STRRET strFile;
|
hr = SHILCombine(pidl, apidl[i], &pidlFull);
|
||||||
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
|
|
||||||
|
WCHAR targetName[MAX_PATH];
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
hr = Shell_DisplayNameOf(psfFrom, apidl[i], SHGDN_FOREDITING | SHGDN_INFOLDER, targetName, _countof(targetName));
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
if (FAILED_UNEXPECTEDLY(hr))
|
||||||
break;
|
|
||||||
|
|
||||||
hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
|
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
|
||||||
break;
|
|
||||||
|
|
||||||
TRACE("source path = %s\n", debugstr_w(wszPath));
|
|
||||||
|
|
||||||
WCHAR wszDisplayName[MAX_PATH];
|
|
||||||
LPWSTR pwszFileName = PathFindFileNameW(wszPath);
|
|
||||||
if (PathIsRootW(wszPath)) // Drive?
|
|
||||||
{
|
{
|
||||||
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_NORMAL, &strFile);
|
SHELL_ErrorBox(m_hwndSite, hr);
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
hr = StrRetToBufW(&strFile, apidl[i], wszDisplayName, _countof(wszDisplayName));
|
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Delete a ':' in wszDisplayName.
|
|
||||||
LPWSTR pch0 = wcschr(wszDisplayName, L':');
|
|
||||||
if (pch0)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
*pch0 = *(pch0 + 1);
|
|
||||||
++pch0;
|
|
||||||
} while (*pch0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pwszFileName = wszDisplayName; // Use wszDisplayName
|
|
||||||
}
|
|
||||||
else if (wszPath[0] == L':' && wszPath[1] == L':') // ::{GUID}?
|
|
||||||
{
|
|
||||||
CLSID clsid;
|
|
||||||
hr = ::CLSIDFromString(&wszPath[2], &clsid);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
LPITEMIDLIST pidl = ILCreateFromPathW(wszPath);
|
|
||||||
if (pidl)
|
|
||||||
{
|
|
||||||
SHFILEINFOW fi = { NULL };
|
|
||||||
SHGetFileInfoW((LPCWSTR)pidl, 0, &fi, sizeof(fi),
|
|
||||||
SHGFI_DISPLAYNAME | SHGFI_PIDL);
|
|
||||||
if (fi.szDisplayName[0])
|
|
||||||
{
|
|
||||||
lstrcpynW(wszDisplayName, fi.szDisplayName, _countof(wszDisplayName));
|
|
||||||
pwszFileName = wszDisplayName; // Use wszDisplayName
|
|
||||||
}
|
|
||||||
ILFree(pidl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SHELL_StripIllegalFsNameCharacters(targetName);
|
||||||
|
|
||||||
// Creating a buffer to hold the combined path.
|
WCHAR wszCombined[MAX_PATH + _countof(targetName)];
|
||||||
WCHAR wszCombined[MAX_PATH];
|
PathCombineW(wszCombined, m_sPathTarget, targetName);
|
||||||
PathCombineW(wszCombined, m_sPathTarget, pwszFileName);
|
|
||||||
|
|
||||||
// Check to see if the source is a link
|
// Check to see if the source is a link
|
||||||
|
SFGAOF att = SHGetAttributes(psfFrom, apidl[i], SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM);
|
||||||
BOOL fSourceIsLink = FALSE;
|
BOOL fSourceIsLink = FALSE;
|
||||||
if (!wcsicmp(PathFindExtensionW(wszPath), L".lnk"))
|
if (!wcsicmp(PathFindExtensionW(targetName), L".lnk") && (att & (SFGAO_FOLDER | SFGAO_STREAM)) != SFGAO_FOLDER)
|
||||||
{
|
{
|
||||||
fSourceIsLink = TRUE;
|
fSourceIsLink = TRUE;
|
||||||
PathRemoveExtensionW(wszCombined);
|
PathRemoveExtensionW(wszCombined);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a pathname to save the new link.
|
// Create a pathname to save the new link.
|
||||||
_GetUniqueFileName(wszCombined, L".lnk", wszTarget, TRUE);
|
_GetUniqueFileName(wszCombined, L".lnk", wszNewLnk, TRUE);
|
||||||
|
|
||||||
CComPtr<IPersistFile> ppf;
|
CComPtr<IPersistFile> ppf;
|
||||||
if (fSourceIsLink)
|
if (fSourceIsLink)
|
||||||
{
|
{
|
||||||
hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf));
|
PWSTR pwszTargetFull;
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
hr = SHGetNameFromIDList(pidlFull, SIGDN_DESKTOPABSOLUTEPARSING, &pwszTargetFull);
|
||||||
break;
|
if (!FAILED_UNEXPECTEDLY(hr))
|
||||||
|
{
|
||||||
|
hr = IShellLink_ConstructFromPath(pwszTargetFull, IID_PPV_ARG(IPersistFile, &ppf));
|
||||||
|
FAILED_UNEXPECTEDLY(hr);
|
||||||
|
SHFree(pwszTargetFull);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CComPtr<IShellLinkW> pLink;
|
CComPtr<IShellLinkW> pLink;
|
||||||
hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
|
hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
if (!FAILED_UNEXPECTEDLY(hr))
|
||||||
break;
|
{
|
||||||
|
hr = pLink->SetIDList(pidlFull);
|
||||||
|
if (!FAILED_UNEXPECTEDLY(hr))
|
||||||
|
hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
|
||||||
|
|
||||||
WCHAR szDirPath[MAX_PATH], *pwszFile;
|
PWSTR pwszPath, pSep;
|
||||||
GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
|
if ((att & SFGAO_FILESYSTEM) && SUCCEEDED(SHGetNameFromIDList(pidlFull, SIGDN_FILESYSPATH, &pwszPath)))
|
||||||
if (pwszFile)
|
{
|
||||||
pwszFile[0] = 0;
|
if ((pSep = PathFindFileNameW(pwszPath)) > pwszPath)
|
||||||
|
{
|
||||||
hr = pLink->SetPath(wszPath);
|
pSep[-1] = UNICODE_NULL;
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
pLink->SetWorkingDirectory(pwszPath);
|
||||||
break;
|
}
|
||||||
|
SHFree(pwszPath);
|
||||||
hr = pLink->SetWorkingDirectory(szDirPath);
|
}
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
|
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
hr = ppf->Save(wszTarget, !fSourceIsLink);
|
hr = ppf->Save(wszNewLnk, !fSourceIsLink);
|
||||||
if (FAILED_UNEXPECTEDLY(hr))
|
if (FAILED_UNEXPECTEDLY(hr))
|
||||||
|
{
|
||||||
|
SHELL_ErrorBox(m_hwndSite, hr);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszNewLnk, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -28,12 +28,7 @@ static_assert(sizeof(DataObjectAttributes) == 0xc, "Unexpected struct size!");
|
||||||
static
|
static
|
||||||
HRESULT _BindToObject(PCUIDLIST_ABSOLUTE pidl, CComPtr<IShellFolder>& spFolder)
|
HRESULT _BindToObject(PCUIDLIST_ABSOLUTE pidl, CComPtr<IShellFolder>& spFolder)
|
||||||
{
|
{
|
||||||
CComPtr<IShellFolder> spDesktop;
|
return SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellFolder, &spFolder));
|
||||||
HRESULT hr = SHGetDesktopFolder(&spDesktop);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
|
|
||||||
return spDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &spFolder));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXTERN_C
|
EXTERN_C
|
||||||
|
|
Loading…
Reference in a new issue