mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 07:46:29 +00:00
[0.4.13][SHELL32] Improve Right-click copy/move/link menu
by porting back: 0.4.15-dev-5588-ge89675768b
[SHELL32] Add 3 accelerators for IDM_DRAGFILE MENU de-DE.rc (#5001) pick a single line only of 0.4.15-dev-984-ga5a30fc249
[SHELL32] CFSDropTarget.cpp ERR->TRACE to mute logspam and strip some EOL-whitespaces in 3 related source-files thats contents I will port back into even older branches, like it was done in master.
This commit is contained in:
parent
f7423b8f10
commit
b5d2895b86
4 changed files with 39 additions and 39 deletions
|
@ -401,7 +401,7 @@ CDefView::~CDefView()
|
||||||
::DeleteObject(m_viewinfo_data.hbmBack);
|
::DeleteObject(m_viewinfo_data.hbmBack);
|
||||||
m_viewinfo_data.hbmBack = NULL;
|
m_viewinfo_data.hbmBack = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_hWnd)
|
if (m_hWnd)
|
||||||
{
|
{
|
||||||
DestroyViewWindow();
|
DestroyViewWindow();
|
||||||
|
@ -1266,10 +1266,10 @@ HRESULT CDefView::FillArrangeAsMenu(HMENU hmenuArrange)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Also check the menu item according to which we sort */
|
/* Also check the menu item according to which we sort */
|
||||||
CheckMenuRadioItem(hmenuArrange,
|
CheckMenuRadioItem(hmenuArrange,
|
||||||
0x30,
|
0x30,
|
||||||
0x100,
|
0x100,
|
||||||
m_sortInfo.nHeaderID + 0x30,
|
m_sortInfo.nHeaderID + 0x30,
|
||||||
MF_BYCOMMAND);
|
MF_BYCOMMAND);
|
||||||
|
|
||||||
if (m_FolderSettings.ViewMode == FVM_DETAILS || m_FolderSettings.ViewMode == FVM_LIST)
|
if (m_FolderSettings.ViewMode == FVM_DETAILS || m_FolderSettings.ViewMode == FVM_LIST)
|
||||||
|
@ -1278,7 +1278,7 @@ HRESULT CDefView::FillArrangeAsMenu(HMENU hmenuArrange)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND);
|
EnableMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_BYCOMMAND);
|
||||||
|
|
||||||
if (GetAutoArrange() == S_OK)
|
if (GetAutoArrange() == S_OK)
|
||||||
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_CHECKED);
|
CheckMenuItem(hmenuArrange, FCIDM_SHVIEW_AUTOARRANGE, MF_CHECKED);
|
||||||
|
@ -1377,7 +1377,7 @@ HRESULT CDefView::OpenSelectedItems()
|
||||||
return hResult;
|
return hResult;
|
||||||
|
|
||||||
hMenu = CreatePopupMenu();
|
hMenu = CreatePopupMenu();
|
||||||
if (!hMenu)
|
if (!hMenu)
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
|
||||||
hResult = GetItemObject(SVGIO_SELECTION, IID_PPV_ARG(IContextMenu, &m_pCM));
|
hResult = GetItemObject(SVGIO_SELECTION, IID_PPV_ARG(IContextMenu, &m_pCM));
|
||||||
|
@ -1398,7 +1398,7 @@ HRESULT CDefView::OpenSelectedItems()
|
||||||
InvokeContextMenuCommand(uCommand);
|
InvokeContextMenuCommand(uCommand);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
|
||||||
if (hMenu)
|
if (hMenu)
|
||||||
DestroyMenu(hMenu);
|
DestroyMenu(hMenu);
|
||||||
|
|
||||||
|
@ -1423,7 +1423,7 @@ LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &b
|
||||||
TRACE("(%p)\n", this);
|
TRACE("(%p)\n", this);
|
||||||
|
|
||||||
m_hContextMenu = CreatePopupMenu();
|
m_hContextMenu = CreatePopupMenu();
|
||||||
if (!m_hContextMenu)
|
if (!m_hContextMenu)
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
|
||||||
m_cidl = m_ListView.GetSelectedCount();
|
m_cidl = m_ListView.GetSelectedCount();
|
||||||
|
@ -1467,7 +1467,7 @@ LRESULT CDefView::OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &b
|
||||||
/* We have to drop it somewhere.. */
|
/* We have to drop it somewhere.. */
|
||||||
pt.x = pt.y = 0;
|
pt.x = pt.y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ListView.ClientToScreen(&pt);
|
m_ListView.ClientToScreen(&pt);
|
||||||
x = pt.x;
|
x = pt.x;
|
||||||
y = pt.y;
|
y = pt.y;
|
||||||
|
@ -1676,7 +1676,7 @@ LRESULT CDefView::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHand
|
||||||
DWORD dwCmdID;
|
DWORD dwCmdID;
|
||||||
DWORD dwCmd;
|
DWORD dwCmd;
|
||||||
HWND hwndCmd;
|
HWND hwndCmd;
|
||||||
int nCount;
|
int nCount;
|
||||||
|
|
||||||
dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
|
dwCmdID = GET_WM_COMMAND_ID(wParam, lParam);
|
||||||
dwCmd = GET_WM_COMMAND_CMD(wParam, lParam);
|
dwCmd = GET_WM_COMMAND_CMD(wParam, lParam);
|
||||||
|
@ -1936,7 +1936,7 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
|
||||||
{
|
{
|
||||||
dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
|
dwEffect |= dwAttributes & (SFGAO_CANCOPY | SFGAO_CANLINK);
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IAsyncOperation> piaso;
|
CComPtr<IAsyncOperation> piaso;
|
||||||
if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
|
if (SUCCEEDED(pda->QueryInterface(IID_PPV_ARG(IAsyncOperation, &piaso))))
|
||||||
{
|
{
|
||||||
|
@ -2125,7 +2125,7 @@ LRESULT CDefView::OnCustomItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bH
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The lParam of WM_DRAWITEM WM_MEASUREITEM contain a menu id and this also needs to
|
/* The lParam of WM_DRAWITEM WM_MEASUREITEM contain a menu id and this also needs to
|
||||||
be changed to a menu identifier offset */
|
be changed to a menu identifier offset */
|
||||||
UINT CmdID;
|
UINT CmdID;
|
||||||
HRESULT hres = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdID);
|
HRESULT hres = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdID);
|
||||||
|
@ -2592,7 +2592,7 @@ HRESULT STDMETHODCALLTYPE CDefView::GetSpacing(POINT *ppt)
|
||||||
{
|
{
|
||||||
TRACE("(%p)->(%p)\n", this, ppt);
|
TRACE("(%p)->(%p)\n", this, ppt);
|
||||||
|
|
||||||
if (!m_ListView)
|
if (!m_ListView)
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
|
|
||||||
if (ppt)
|
if (ppt)
|
||||||
|
@ -2693,7 +2693,7 @@ HRESULT STDMETHODCALLTYPE CDefView::CreateViewWindow3(IShellBrowser *psb, IShell
|
||||||
TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", this, psvPrevious, psb, prcView, hwnd, mode, flags);
|
TRACE("(%p)->(shlview=%p shlbrs=%p rec=%p hwnd=%p vmode=%x flags=%x)\n", this, psvPrevious, psb, prcView, hwnd, mode, flags);
|
||||||
if (prcView != NULL)
|
if (prcView != NULL)
|
||||||
TRACE("-- left=%i top=%i right=%i bottom=%i\n", prcView->left, prcView->top, prcView->right, prcView->bottom);
|
TRACE("-- left=%i top=%i right=%i bottom=%i\n", prcView->left, prcView->top, prcView->right, prcView->bottom);
|
||||||
|
|
||||||
/* Validate the Shell Browser */
|
/* Validate the Shell Browser */
|
||||||
if (psb == NULL || m_hWnd)
|
if (psb == NULL || m_hWnd)
|
||||||
return E_UNEXPECTED;
|
return E_UNEXPECTED;
|
||||||
|
@ -2887,7 +2887,7 @@ HRESULT STDMETHODCALLTYPE CDefView::GetSelectedObjects(PCUITEMID_CHILD **pidl, U
|
||||||
{
|
{
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* it's documented that caller shouldn't PIDLs, only array itself */
|
/* it's documented that caller shouldn't PIDLs, only array itself */
|
||||||
memcpy(*pidl, m_apidl, *items * sizeof(PCUITEMID_CHILD));
|
memcpy(*pidl, m_apidl, *items * sizeof(PCUITEMID_CHILD));
|
||||||
}
|
}
|
||||||
|
@ -2897,7 +2897,7 @@ HRESULT STDMETHODCALLTYPE CDefView::GetSelectedObjects(PCUITEMID_CHILD **pidl, U
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE CDefView::IsDropOnSource(IDropTarget *drop_target)
|
HRESULT STDMETHODCALLTYPE CDefView::IsDropOnSource(IDropTarget *drop_target)
|
||||||
{
|
{
|
||||||
if ((m_iDragOverItem == -1 || m_pCurDropTarget == NULL) &&
|
if ((m_iDragOverItem == -1 || m_pCurDropTarget == NULL) &&
|
||||||
(m_pSourceDataObject.p))
|
(m_pSourceDataObject.p))
|
||||||
{
|
{
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -2983,7 +2983,7 @@ HRESULT STDMETHODCALLTYPE CDefView::SetAutomationObject(IDispatch *disp)
|
||||||
FIXME("(%p)->(%p) stub\n", this, disp);
|
FIXME("(%p)->(%p) stub\n", this, disp);
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
* ISVOleCmdTarget_QueryStatus (IOleCommandTarget)
|
* ISVOleCmdTarget_QueryStatus (IOleCommandTarget)
|
||||||
*/
|
*/
|
||||||
|
@ -3191,7 +3191,7 @@ HRESULT WINAPI CDefView::DragEnter(IDataObject *pDataObject, DWORD grfKeyState,
|
||||||
/* Get a hold on the data object for later calls to DragEnter on the sub-folders */
|
/* Get a hold on the data object for later calls to DragEnter on the sub-folders */
|
||||||
m_pCurDataObject = pDataObject;
|
m_pCurDataObject = pDataObject;
|
||||||
|
|
||||||
HRESULT hr = drag_notify_subitem(grfKeyState, pt, pdwEffect);
|
HRESULT hr = drag_notify_subitem(grfKeyState, pt, pdwEffect);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
POINT ptClient = {pt.x, pt.y};
|
POINT ptClient = {pt.x, pt.y};
|
||||||
|
@ -3235,8 +3235,8 @@ HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINT
|
||||||
ImageList_DragLeave(m_hWnd);
|
ImageList_DragLeave(m_hWnd);
|
||||||
ImageList_EndDrag();
|
ImageList_EndDrag();
|
||||||
|
|
||||||
if ((IsDropOnSource(NULL) == S_OK) &&
|
if ((IsDropOnSource(NULL) == S_OK) &&
|
||||||
(*pdwEffect & DROPEFFECT_MOVE) &&
|
(*pdwEffect & DROPEFFECT_MOVE) &&
|
||||||
(m_grfKeyState & MK_LBUTTON))
|
(m_grfKeyState & MK_LBUTTON))
|
||||||
{
|
{
|
||||||
if (m_pCurDropTarget)
|
if (m_pCurDropTarget)
|
||||||
|
@ -3269,7 +3269,7 @@ HRESULT WINAPI CDefView::Drop(IDataObject* pDataObject, DWORD grfKeyState, POINT
|
||||||
m_pCurDropTarget.Release();
|
m_pCurDropTarget.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pCurDataObject.Release();
|
m_pCurDataObject.Release();
|
||||||
m_iDragOverItem = 0;
|
m_iDragOverItem = 0;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ HRESULT CFSDropTarget::_CopyItems(IShellFolder * pSFFrom, UINT cidl,
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
pszSrcList = BuildPathsList(strretFrom.pOleStr, cidl, apidl);
|
pszSrcList = BuildPathsList(strretFrom.pOleStr, cidl, apidl);
|
||||||
ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(m_sPathTarget), bCopy);
|
TRACE("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(m_sPathTarget), bCopy);
|
||||||
CoTaskMemFree(strretFrom.pOleStr);
|
CoTaskMemFree(strretFrom.pOleStr);
|
||||||
if (!pszSrcList)
|
if (!pszSrcList)
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
@ -161,7 +161,7 @@ CFSDropTarget::_GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR p
|
||||||
*/
|
*/
|
||||||
BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
|
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. */
|
||||||
|
|
||||||
DWORD dwEffect = m_dwDefaultEffect;
|
DWORD dwEffect = m_dwDefaultEffect;
|
||||||
|
@ -170,7 +170,7 @@ BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
|
||||||
|
|
||||||
if (m_fAcceptFmt) { /* Does our interpretation of the keystate ... */
|
if (m_fAcceptFmt) { /* Does our interpretation of the keystate ... */
|
||||||
*pdwEffect = KeyStateToDropEffect (dwKeyState);
|
*pdwEffect = KeyStateToDropEffect (dwKeyState);
|
||||||
|
|
||||||
if (*pdwEffect == DROPEFFECT_NONE)
|
if (*pdwEffect == DROPEFFECT_NONE)
|
||||||
*pdwEffect = dwEffect;
|
*pdwEffect = dwEffect;
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject,
|
||||||
DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
|
DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
|
||||||
{
|
{
|
||||||
TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
|
TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
|
||||||
|
|
||||||
if (!pdwEffect)
|
if (!pdwEffect)
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
|
|
||||||
|
@ -485,7 +485,7 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
/* use desktop shell folder */
|
/* use desktop shell folder */
|
||||||
psfFrom = psfDesktop;
|
psfFrom = psfDesktop;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
|
hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -514,14 +514,14 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
//Find out which file we're copying
|
//Find out which file we're copying
|
||||||
STRRET strFile;
|
STRRET strFile;
|
||||||
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
|
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
ERR("Error source obtaining path");
|
ERR("Error source obtaining path");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
|
hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
ERR("Error putting source path into buffer");
|
ERR("Error putting source path into buffer");
|
||||||
break;
|
break;
|
||||||
|
@ -538,11 +538,11 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
LPWSTR placementPath = PathCombineW(lpStr1, m_sPathTarget, pwszFileName);
|
LPWSTR placementPath = PathCombineW(lpStr1, m_sPathTarget, pwszFileName);
|
||||||
CComPtr<IPersistFile> ppf;
|
CComPtr<IPersistFile> ppf;
|
||||||
|
|
||||||
//Check to see if it's already a link.
|
//Check to see if it's already a link.
|
||||||
if (!wcsicmp(pwszExt, L".lnk"))
|
if (!wcsicmp(pwszExt, L".lnk"))
|
||||||
{
|
{
|
||||||
//It's a link so, we create a new one which copies the old.
|
//It's a link so, we create a new one which copies the old.
|
||||||
if(!_GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
|
if(!_GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
|
||||||
{
|
{
|
||||||
ERR("Error getting unique file name");
|
ERR("Error getting unique file name");
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
|
@ -600,7 +600,7 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
|
hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
|
||||||
}
|
}
|
||||||
|
@ -621,7 +621,7 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
wcscpy(wszTargetPath, m_sPathTarget);
|
wcscpy(wszTargetPath, m_sPathTarget);
|
||||||
//Double NULL terminate.
|
//Double NULL terminate.
|
||||||
wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
|
wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
|
||||||
|
|
||||||
LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
|
LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
|
||||||
if (!lpdf)
|
if (!lpdf)
|
||||||
{
|
{
|
||||||
|
@ -644,11 +644,11 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
|
||||||
ERR("Error calling GetData\n");
|
ERR("Error calling GetData\n");
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ERR("No viable drop format.\n");
|
ERR("No viable drop format.\n");
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
}
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ struct _DoDropData {
|
||||||
CFSDropTarget *This;
|
CFSDropTarget *This;
|
||||||
IStream *pStream;
|
IStream *pStream;
|
||||||
DWORD dwKeyState;
|
DWORD dwKeyState;
|
||||||
POINTL pt;
|
POINTL pt;
|
||||||
DWORD pdwEffect;
|
DWORD pdwEffect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,9 @@ IDM_DRAGFILE MENU
|
||||||
BEGIN
|
BEGIN
|
||||||
POPUP ""
|
POPUP ""
|
||||||
BEGIN
|
BEGIN
|
||||||
MENUITEM "Hierher kopieren", IDM_COPYHERE
|
MENUITEM "Hierher &kopieren", IDM_COPYHERE
|
||||||
MENUITEM "Hierher verschieben", IDM_MOVEHERE
|
MENUITEM "Hierher &verschieben", IDM_MOVEHERE
|
||||||
MENUITEM "Verknüpfung hier erstellen", IDM_LINKHERE
|
MENUITEM "Verknüpfung hier &erstellen", IDM_LINKHERE
|
||||||
MENUITEM SEPARATOR
|
MENUITEM SEPARATOR
|
||||||
MENUITEM "Abbrechen", 0
|
MENUITEM "Abbrechen", 0
|
||||||
END
|
END
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue