win32u: Move NtUserGetUpdateRect implementation from user32.
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
0c752feda6
commit
1df20bb22a
|
@ -47,22 +47,6 @@ void free_dce( struct dce *dce, HWND hwnd )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* delete_clip_rgn
|
||||
*/
|
||||
static void delete_clip_rgn( struct dce *dce )
|
||||
{
|
||||
if (!dce->clip_rgn) return; /* nothing to do */
|
||||
|
||||
dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
|
||||
DeleteObject( dce->clip_rgn );
|
||||
dce->clip_rgn = 0;
|
||||
|
||||
/* make it dirty so that the vis rgn gets recomputed next time */
|
||||
SetHookFlags( dce->hdc, DCHF_INVALIDATEVISRGN );
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* invalidate_dce
|
||||
*
|
||||
|
@ -79,36 +63,6 @@ void invalidate_dce( WND *win, const RECT *extra_rect )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* release_dc
|
||||
*
|
||||
* Implementation of ReleaseDC.
|
||||
*/
|
||||
static INT release_dc( HWND hwnd, HDC hdc, BOOL end_paint )
|
||||
{
|
||||
struct dce *dce;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
TRACE("%p %p\n", hwnd, hdc );
|
||||
|
||||
USER_Lock();
|
||||
dce = (struct dce *)GetDCHook( hdc, NULL );
|
||||
if (dce && dce->count && dce->hwnd)
|
||||
{
|
||||
if (!(dce->flags & DCX_NORESETATTRS)) SetHookFlags( dce->hdc, DCHF_RESETDC );
|
||||
if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
|
||||
if (dce->flags & DCX_CACHE)
|
||||
{
|
||||
dce->count = 0;
|
||||
SetHookFlags( dce->hdc, DCHF_DISABLEDC );
|
||||
}
|
||||
ret = TRUE;
|
||||
}
|
||||
USER_Unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* get_update_region
|
||||
*
|
||||
|
@ -157,140 +111,6 @@ static HRGN get_update_region( HWND hwnd, UINT *flags, HWND *child )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* get_update_flags
|
||||
*
|
||||
* Get only the update flags, not the update region.
|
||||
*/
|
||||
static BOOL get_update_flags( HWND hwnd, HWND *child, UINT *flags )
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
SERVER_START_REQ( get_update_region )
|
||||
{
|
||||
req->window = wine_server_user_handle( hwnd );
|
||||
req->from_child = wine_server_user_handle( child ? *child : 0 );
|
||||
req->flags = *flags | UPDATE_NOREGION;
|
||||
if ((ret = !wine_server_call_err( req )))
|
||||
{
|
||||
if (child) *child = wine_server_ptr_handle( reply->child );
|
||||
*flags = reply->flags;
|
||||
}
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* send_ncpaint
|
||||
*
|
||||
* Send a WM_NCPAINT message if needed, and return the resulting update region (in screen coords).
|
||||
* Helper for erase_now and BeginPaint.
|
||||
*/
|
||||
static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags )
|
||||
{
|
||||
HRGN whole_rgn = get_update_region( hwnd, flags, child );
|
||||
HRGN client_rgn = 0;
|
||||
DWORD style;
|
||||
|
||||
if (child) hwnd = *child;
|
||||
|
||||
if (hwnd == GetDesktopWindow()) return whole_rgn;
|
||||
|
||||
if (whole_rgn)
|
||||
{
|
||||
DPI_AWARENESS_CONTEXT context;
|
||||
RECT client, window, update;
|
||||
INT type;
|
||||
|
||||
context = SetThreadDpiAwarenessContext( GetWindowDpiAwarenessContext( hwnd ));
|
||||
|
||||
/* check if update rgn overlaps with nonclient area */
|
||||
type = GetRgnBox( whole_rgn, &update );
|
||||
WIN_GetRectangles( hwnd, COORDS_SCREEN, &window, &client );
|
||||
|
||||
if ((*flags & UPDATE_NONCLIENT) ||
|
||||
update.left < client.left || update.top < client.top ||
|
||||
update.right > client.right || update.bottom > client.bottom)
|
||||
{
|
||||
client_rgn = CreateRectRgnIndirect( &client );
|
||||
CombineRgn( client_rgn, client_rgn, whole_rgn, RGN_AND );
|
||||
|
||||
/* check if update rgn contains complete nonclient area */
|
||||
if (type == SIMPLEREGION && EqualRect( &window, &update ))
|
||||
{
|
||||
DeleteObject( whole_rgn );
|
||||
whole_rgn = (HRGN)1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
client_rgn = whole_rgn;
|
||||
whole_rgn = 0;
|
||||
}
|
||||
|
||||
if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
|
||||
{
|
||||
if (*flags & UPDATE_NONCLIENT)
|
||||
{
|
||||
/* Mark standard scroll bars as not painted before sending WM_NCPAINT */
|
||||
style = GetWindowLongW( hwnd, GWL_STYLE );
|
||||
if (style & WS_HSCROLL)
|
||||
SCROLL_SetStandardScrollPainted( hwnd, SB_HORZ, FALSE );
|
||||
if (style & WS_VSCROLL)
|
||||
SCROLL_SetStandardScrollPainted( hwnd, SB_VERT, FALSE );
|
||||
|
||||
SendMessageW( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 );
|
||||
}
|
||||
if (whole_rgn > (HRGN)1) DeleteObject( whole_rgn );
|
||||
}
|
||||
SetThreadDpiAwarenessContext( context );
|
||||
}
|
||||
return client_rgn;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* send_erase
|
||||
*
|
||||
* Send a WM_ERASEBKGND message if needed, and optionally return the DC for painting.
|
||||
* If a DC is requested, the region is selected into it. In all cases the region is deleted.
|
||||
* Helper for erase_now and BeginPaint.
|
||||
*/
|
||||
static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn,
|
||||
RECT *clip_rect, HDC *hdc_ret )
|
||||
{
|
||||
BOOL need_erase = (flags & UPDATE_DELAYED_ERASE) != 0;
|
||||
HDC hdc = 0;
|
||||
RECT dummy;
|
||||
|
||||
if (!clip_rect) clip_rect = &dummy;
|
||||
if (hdc_ret || (flags & UPDATE_ERASE))
|
||||
{
|
||||
UINT dcx_flags = DCX_INTERSECTRGN | DCX_USESTYLE;
|
||||
if (IsIconic(hwnd)) dcx_flags |= DCX_WINDOW;
|
||||
|
||||
if ((hdc = NtUserGetDCEx( hwnd, client_rgn, dcx_flags )))
|
||||
{
|
||||
INT type = GetClipBox( hdc, clip_rect );
|
||||
|
||||
if (flags & UPDATE_ERASE)
|
||||
{
|
||||
/* don't erase if the clip box is empty */
|
||||
if (type != NULLREGION)
|
||||
need_erase = !SendMessageW( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 );
|
||||
}
|
||||
if (!hdc_ret) release_dc( hwnd, hdc, TRUE );
|
||||
}
|
||||
|
||||
if (hdc_ret) *hdc_ret = hdc;
|
||||
}
|
||||
if (!hdc) DeleteObject( client_rgn );
|
||||
return need_erase;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* copy_bits_from_surface
|
||||
*
|
||||
|
@ -582,44 +402,6 @@ BOOL WINAPI ValidateRect( HWND hwnd, const RECT *rect )
|
|||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* GetUpdateRect (USER32.@)
|
||||
*/
|
||||
BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
|
||||
{
|
||||
DPI_AWARENESS_CONTEXT context;
|
||||
UINT flags = UPDATE_NOCHILDREN;
|
||||
HRGN update_rgn;
|
||||
BOOL need_erase;
|
||||
|
||||
if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
|
||||
|
||||
if (!(update_rgn = send_ncpaint( hwnd, NULL, &flags ))) return FALSE;
|
||||
|
||||
if (rect)
|
||||
{
|
||||
if (GetRgnBox( update_rgn, rect ) != NULLREGION)
|
||||
{
|
||||
HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
|
||||
DWORD layout = SetLayout( hdc, 0 ); /* MapWindowPoints mirrors already */
|
||||
context = SetThreadDpiAwarenessContext( GetWindowDpiAwarenessContext( hwnd ));
|
||||
MapWindowPoints( 0, hwnd, (LPPOINT)rect, 2 );
|
||||
SetThreadDpiAwarenessContext( context );
|
||||
*rect = rect_win_to_thread_dpi( hwnd, *rect );
|
||||
DPtoLP( hdc, (LPPOINT)rect, 2 );
|
||||
SetLayout( hdc, layout );
|
||||
NtUserReleaseDC( hwnd, hdc );
|
||||
}
|
||||
}
|
||||
need_erase = send_erase( hwnd, flags, update_rgn, NULL, NULL );
|
||||
|
||||
/* check if we still have an update region */
|
||||
flags = UPDATE_PAINT | UPDATE_NOCHILDREN;
|
||||
if (need_erase) flags |= UPDATE_DELAYED_ERASE;
|
||||
return (get_update_flags( hwnd, NULL, &flags ) && (flags & UPDATE_PAINT));
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* ExcludeUpdateRgn (USER32.@)
|
||||
*/
|
||||
|
|
|
@ -392,7 +392,7 @@
|
|||
@ stdcall GetTitleBarInfo(long ptr)
|
||||
@ stdcall GetTopWindow(long)
|
||||
@ stdcall GetTouchInputInfo(long long ptr long)
|
||||
@ stdcall GetUpdateRect(long ptr long)
|
||||
@ stdcall GetUpdateRect(long ptr long) NtUserGetUpdateRect
|
||||
@ stdcall GetUpdateRgn(long long long) NtUserGetUpdateRgn
|
||||
@ stdcall GetUpdatedClipboardFormats(ptr long ptr) NtUserGetUpdatedClipboardFormats
|
||||
@ stdcall GetUserObjectInformationA (long long ptr long ptr)
|
||||
|
|
|
@ -1167,3 +1167,35 @@ INT WINAPI NtUserGetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
|||
set_thread_dpi_awareness_context( context );
|
||||
return retval;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* NtUserGetUpdateRect (win32u.@)
|
||||
*/
|
||||
BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase )
|
||||
{
|
||||
UINT flags = UPDATE_NOCHILDREN;
|
||||
HRGN update_rgn;
|
||||
BOOL need_erase;
|
||||
|
||||
if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
|
||||
|
||||
if (!(update_rgn = send_ncpaint( hwnd, NULL, &flags ))) return FALSE;
|
||||
|
||||
if (rect && NtGdiGetRgnBox( update_rgn, rect ) != NULLREGION)
|
||||
{
|
||||
HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
|
||||
DWORD layout = NtGdiSetLayout( hdc, -1, 0 ); /* map_window_points mirrors already */
|
||||
UINT win_dpi = get_dpi_for_window( hwnd );
|
||||
map_window_points( 0, hwnd, (POINT *)rect, 2, win_dpi );
|
||||
*rect = map_dpi_rect( *rect, win_dpi, get_thread_dpi() );
|
||||
NtGdiTransformPoints( hdc, (POINT *)rect, (POINT *)rect, 2, NtGdiDPtoLP );
|
||||
NtGdiSetLayout( hdc, -1, layout );
|
||||
NtUserReleaseDC( hwnd, hdc );
|
||||
}
|
||||
need_erase = send_erase( hwnd, flags, update_rgn, NULL, NULL );
|
||||
|
||||
/* check if we still have an update region */
|
||||
flags = UPDATE_PAINT | UPDATE_NOCHILDREN;
|
||||
if (need_erase) flags |= UPDATE_DELAYED_ERASE;
|
||||
return get_update_flags( hwnd, NULL, &flags ) && (flags & UPDATE_PAINT);
|
||||
}
|
||||
|
|
|
@ -1179,6 +1179,7 @@ static struct unix_funcs unix_funcs =
|
|||
NtUserGetKeyboardLayoutList,
|
||||
NtUserGetPriorityClipboardFormat,
|
||||
NtUserGetQueueStatus,
|
||||
NtUserGetUpdateRect,
|
||||
NtUserGetUpdateRgn,
|
||||
NtUserGetUpdatedClipboardFormats,
|
||||
NtUserIsClipboardFormatAvailable,
|
||||
|
@ -1214,7 +1215,6 @@ static struct unix_funcs unix_funcs =
|
|||
NtUserVkKeyScanEx,
|
||||
NtUserWindowFromPoint,
|
||||
|
||||
GetDCHook,
|
||||
SetDIBits,
|
||||
SetHookFlags,
|
||||
__wine_get_brush_bitmap_info,
|
||||
|
|
|
@ -1002,7 +1002,7 @@
|
|||
@ stub NtUserGetTouchInputInfo
|
||||
@ stub NtUserGetTouchValidationStatus
|
||||
@ stub NtUserGetUniformSpaceMapping
|
||||
@ stub NtUserGetUpdateRect
|
||||
@ stdcall NtUserGetUpdateRect(long ptr long)
|
||||
@ stdcall NtUserGetUpdateRgn(long long long)
|
||||
@ stdcall NtUserGetUpdatedClipboardFormats(ptr long ptr)
|
||||
@ stub NtUserGetWOWClass
|
||||
|
@ -1321,7 +1321,6 @@
|
|||
# Wine internal extensions
|
||||
|
||||
# user32
|
||||
@ stdcall GetDCHook(long ptr)
|
||||
@ stdcall SetHookFlags(long long)
|
||||
@ cdecl __wine_set_visible_region(long long ptr ptr ptr)
|
||||
|
||||
|
|
|
@ -222,6 +222,7 @@ struct unix_funcs
|
|||
UINT (WINAPI *pNtUserGetKeyboardLayoutList)( INT size, HKL *layouts );
|
||||
INT (WINAPI *pNtUserGetPriorityClipboardFormat)( UINT *list, INT count );
|
||||
DWORD (WINAPI *pNtUserGetQueueStatus)( UINT flags );
|
||||
BOOL (WINAPI *pNtUserGetUpdateRect)( HWND hwnd, RECT *rect, BOOL erase );
|
||||
INT (WINAPI *pNtUserGetUpdateRgn)( HWND hwnd, HRGN hrgn, BOOL erase );
|
||||
BOOL (WINAPI *pNtUserGetUpdatedClipboardFormats)( UINT *formats, UINT size, UINT *out_size );
|
||||
BOOL (WINAPI *pNtUserIsClipboardFormatAvailable)( UINT format );
|
||||
|
@ -269,7 +270,6 @@ struct unix_funcs
|
|||
HWND (WINAPI *pNtUserWindowFromPoint)( LONG x, LONG y );
|
||||
|
||||
/* Wine-specific functions */
|
||||
DWORD_PTR (WINAPI *pGetDCHook)( HDC hdc, DCHOOKPROC *proc );
|
||||
INT (WINAPI *pSetDIBits)( HDC hdc, HBITMAP hbitmap, UINT startscan,
|
||||
UINT lines, const void *bits, const BITMAPINFO *info,
|
||||
UINT coloruse );
|
||||
|
@ -358,6 +358,8 @@ extern BOOL get_window_rects( HWND hwnd, enum coords_relative relative, RECT *wi
|
|||
RECT *client_rect, UINT dpi ) DECLSPEC_HIDDEN;
|
||||
extern HWND *list_window_children( HDESK desktop, HWND hwnd, UNICODE_STRING *class,
|
||||
DWORD tid ) DECLSPEC_HIDDEN;
|
||||
extern int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count,
|
||||
UINT dpi ) DECLSPEC_HIDDEN;
|
||||
extern void map_window_region( HWND from, HWND to, HRGN hrgn ) DECLSPEC_HIDDEN;
|
||||
extern BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) DECLSPEC_HIDDEN;
|
||||
extern void update_window_state( HWND hwnd ) DECLSPEC_HIDDEN;
|
||||
|
|
|
@ -1873,7 +1873,7 @@ INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
|
|||
* Calculate the offset between the origin of the two windows. Used
|
||||
* to implement MapWindowPoints.
|
||||
*/
|
||||
static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, BOOL *mirrored, POINT *ret_offset )
|
||||
static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
|
||||
{
|
||||
WND *win;
|
||||
POINT offset;
|
||||
|
@ -1915,7 +1915,7 @@ static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, BOOL *mirrored, PO
|
|||
}
|
||||
}
|
||||
if (win && win != WND_DESKTOP) release_win_ptr( win );
|
||||
offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), get_thread_dpi() );
|
||||
offset = map_dpi_point( offset, get_dpi_for_window( hwnd_from ), dpi );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1952,7 +1952,7 @@ static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, BOOL *mirrored, PO
|
|||
}
|
||||
}
|
||||
if (win && win != WND_DESKTOP) release_win_ptr( win );
|
||||
pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), get_thread_dpi() );
|
||||
pt = map_dpi_point( pt, get_dpi_for_window( hwnd_to ), dpi );
|
||||
offset.x -= pt.x;
|
||||
offset.y -= pt.y;
|
||||
}
|
||||
|
@ -1968,7 +1968,7 @@ other_process: /* one of the parents may belong to another process, do it the h
|
|||
{
|
||||
req->from = wine_server_user_handle( hwnd_from );
|
||||
req->to = wine_server_user_handle( hwnd_to );
|
||||
req->dpi = get_thread_dpi();
|
||||
req->dpi = dpi;
|
||||
if ((ret = !wine_server_call_err( req )))
|
||||
{
|
||||
ret_offset->x = reply->x;
|
||||
|
@ -1990,7 +1990,7 @@ void map_window_region( HWND from, HWND to, HRGN hrgn )
|
|||
HRGN new_rgn;
|
||||
RECT *rect;
|
||||
|
||||
if (!get_windows_offset( from, to, &mirrored, &offset )) return;
|
||||
if (!get_windows_offset( from, to, get_thread_dpi(), &mirrored, &offset )) return;
|
||||
|
||||
if (!mirrored)
|
||||
{
|
||||
|
@ -2017,6 +2017,30 @@ void map_window_region( HWND from, HWND to, HRGN hrgn )
|
|||
free( data );
|
||||
}
|
||||
|
||||
/* see MapWindowPoints */
|
||||
int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
|
||||
{
|
||||
BOOL mirrored;
|
||||
POINT offset;
|
||||
UINT i;
|
||||
|
||||
if (!get_windows_offset( hwnd_from, hwnd_to, dpi, &mirrored, &offset )) return 0;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
points[i].x += offset.x;
|
||||
points[i].y += offset.y;
|
||||
if (mirrored) points[i].x = -points[i].x;
|
||||
}
|
||||
if (mirrored && count == 2) /* special case for rectangle */
|
||||
{
|
||||
int tmp = points[0].x;
|
||||
points[0].x = points[1].x;
|
||||
points[1].x = tmp;
|
||||
}
|
||||
return MAKELONG( LOWORD(offset.x), LOWORD(offset.y) );
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* dump_winpos_flags
|
||||
*/
|
||||
|
|
|
@ -873,6 +873,12 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
|
|||
return unix_funcs->pNtUserGetKeyNameText( lparam, buffer, size );
|
||||
}
|
||||
|
||||
BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase )
|
||||
{
|
||||
if (!unix_funcs) return FALSE;
|
||||
return unix_funcs->pNtUserGetUpdateRect( hwnd, rect, erase );
|
||||
}
|
||||
|
||||
INT WINAPI NtUserGetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
|
||||
{
|
||||
if (!unix_funcs) return 0;
|
||||
|
@ -1098,12 +1104,6 @@ HWND WINAPI NtUserWindowFromPoint( LONG x, LONG y )
|
|||
return unix_funcs->pNtUserWindowFromPoint( x, y );
|
||||
}
|
||||
|
||||
DWORD_PTR WINAPI GetDCHook( HDC hdc, DCHOOKPROC *proc )
|
||||
{
|
||||
if (!unix_funcs) return 0;
|
||||
return unix_funcs->pGetDCHook( hdc, proc );
|
||||
}
|
||||
|
||||
INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan,
|
||||
UINT lines, const void *bits, const BITMAPINFO *info,
|
||||
UINT coloruse )
|
||||
|
|
|
@ -355,6 +355,7 @@ ULONG WINAPI NtUserGetSystemDpiForProcess( HANDLE process );
|
|||
HDESK WINAPI NtUserGetThreadDesktop( DWORD thread );
|
||||
INT WINAPI NtUserGetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase );
|
||||
BOOL WINAPI NtUserGetUpdatedClipboardFormats( UINT *formats, UINT size, UINT *out_size );
|
||||
BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase );
|
||||
int WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk );
|
||||
NTSTATUS WINAPI NtUserInitializeClientPfnArrays( const struct user_client_procs *client_procsA,
|
||||
const struct user_client_procs *client_procsW,
|
||||
|
|
Loading…
Reference in New Issue