2011-07-01 00:17:55 +04:00
|
|
|
/**
|
2012-10-09 07:02:04 +04:00
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
2011-07-01 00:17:55 +04:00
|
|
|
* GDI Region Functions
|
|
|
|
*
|
|
|
|
* Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:09:01 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2011-07-01 00:17:55 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
#include <freerdp/api.h>
|
2011-07-01 00:17:55 +04:00
|
|
|
#include <freerdp/freerdp.h>
|
2011-08-15 22:33:04 +04:00
|
|
|
#include <freerdp/gdi/gdi.h>
|
2011-07-01 00:17:55 +04:00
|
|
|
|
2011-08-22 21:08:01 +04:00
|
|
|
#include <freerdp/gdi/region.h>
|
2011-07-01 00:17:55 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a region from rectangular coordinates.\n
|
|
|
|
* @msdn{dd183514}
|
|
|
|
* @param nLeftRect x1
|
|
|
|
* @param nTopRect y1
|
|
|
|
* @param nRightRect x2
|
|
|
|
* @param nBottomRect y2
|
|
|
|
* @return new region
|
|
|
|
*/
|
|
|
|
|
|
|
|
HGDI_RGN gdi_CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
|
|
|
|
{
|
2012-10-09 07:21:26 +04:00
|
|
|
HGDI_RGN hRgn = (HGDI_RGN) malloc(sizeof(GDI_RGN));
|
2015-04-14 11:14:23 +03:00
|
|
|
if (!hRgn)
|
|
|
|
return NULL;
|
2015-06-16 16:42:07 +03:00
|
|
|
|
2011-07-01 00:17:55 +04:00
|
|
|
hRgn->objectType = GDIOBJECT_REGION;
|
|
|
|
hRgn->x = nLeftRect;
|
|
|
|
hRgn->y = nTopRect;
|
|
|
|
hRgn->w = nRightRect - nLeftRect + 1;
|
|
|
|
hRgn->h = nBottomRect - nTopRect + 1;
|
|
|
|
hRgn->null = 0;
|
|
|
|
return hRgn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new rectangle.
|
|
|
|
* @param xLeft x1
|
|
|
|
* @param yTop y1
|
|
|
|
* @param xRight x2
|
|
|
|
* @param yBottom y2
|
|
|
|
* @return new rectangle
|
|
|
|
*/
|
|
|
|
|
|
|
|
HGDI_RECT gdi_CreateRect(int xLeft, int yTop, int xRight, int yBottom)
|
|
|
|
{
|
2012-10-09 07:21:26 +04:00
|
|
|
HGDI_RECT hRect = (HGDI_RECT) malloc(sizeof(GDI_RECT));
|
2015-04-17 17:21:55 +03:00
|
|
|
if (!hRect)
|
|
|
|
return NULL;
|
2015-06-16 16:42:07 +03:00
|
|
|
|
2011-07-01 00:17:55 +04:00
|
|
|
hRect->objectType = GDIOBJECT_RECT;
|
|
|
|
hRect->left = xLeft;
|
|
|
|
hRect->top = yTop;
|
|
|
|
hRect->right = xRight;
|
|
|
|
hRect->bottom = yBottom;
|
|
|
|
return hRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a rectangle to a region.
|
|
|
|
* @param rect source rectangle
|
|
|
|
* @param rgn destination region
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_RectToRgn(HGDI_RECT rect, HGDI_RGN rgn)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
rgn->x = rect->left;
|
|
|
|
rgn->y = rect->top;
|
|
|
|
rgn->w = rect->right - rect->left + 1;
|
|
|
|
rgn->h = rect->bottom - rect->top + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert rectangular coordinates to a region.
|
|
|
|
* @param left x1
|
|
|
|
* @param top y1
|
|
|
|
* @param right x2
|
|
|
|
* @param bottom y2
|
|
|
|
* @param rgn destination region
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_CRectToRgn(int left, int top, int right, int bottom, HGDI_RGN rgn)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
rgn->x = left;
|
|
|
|
rgn->y = top;
|
|
|
|
rgn->w = right - left + 1;
|
|
|
|
rgn->h = bottom - top + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a rectangle to region coordinates.
|
|
|
|
* @param rect source rectangle
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param w width
|
|
|
|
* @param h height
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_RectToCRgn(HGDI_RECT rect, int *x, int *y, int *w, int *h)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
*x = rect->left;
|
|
|
|
*y = rect->top;
|
|
|
|
*w = rect->right - rect->left + 1;
|
|
|
|
*h = rect->bottom - rect->top + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert rectangular coordinates to region coordinates.
|
|
|
|
* @param left x1
|
|
|
|
* @param top y1
|
|
|
|
* @param right x2
|
|
|
|
* @param bottom y2
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param w width
|
|
|
|
* @param h height
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_CRectToCRgn(int left, int top, int right, int bottom, int *x, int *y, int *w, int *h)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
*x = left;
|
|
|
|
*y = top;
|
|
|
|
*w = right - left + 1;
|
|
|
|
*h = bottom - top + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a region to a rectangle.
|
|
|
|
* @param rgn source region
|
|
|
|
* @param rect destination rectangle
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_RgnToRect(HGDI_RGN rgn, HGDI_RECT rect)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
rect->left = rgn->x;
|
|
|
|
rect->top = rgn->y;
|
|
|
|
rect->right = rgn->x + rgn->w - 1;
|
|
|
|
rect->bottom = rgn->y + rgn->h - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert region coordinates to a rectangle.
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param w width
|
|
|
|
* @param h height
|
|
|
|
* @param rect destination rectangle
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_CRgnToRect(int x, int y, int w, int h, HGDI_RECT rect)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
rect->left = x;
|
|
|
|
rect->top = y;
|
|
|
|
rect->right = x + w - 1;
|
|
|
|
rect->bottom = y + h - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert a region to rectangular coordinates.
|
|
|
|
* @param rgn source region
|
|
|
|
* @param left x1
|
|
|
|
* @param top y1
|
|
|
|
* @param right x2
|
|
|
|
* @param bottom y2
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_RgnToCRect(HGDI_RGN rgn, int *left, int *top, int *right, int *bottom)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
*left = rgn->x;
|
|
|
|
*top = rgn->y;
|
|
|
|
*right = rgn->x + rgn->w - 1;
|
|
|
|
*bottom = rgn->y + rgn->h - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert region coordinates to rectangular coordinates.
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param w width
|
|
|
|
* @param h height
|
|
|
|
* @param left x1
|
|
|
|
* @param top y1
|
|
|
|
* @param right x2
|
|
|
|
* @param bottom y2
|
|
|
|
*/
|
|
|
|
|
2011-08-31 12:35:50 +04:00
|
|
|
INLINE void gdi_CRgnToCRect(int x, int y, int w, int h, int *left, int *top, int *right, int *bottom)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
*left = x;
|
|
|
|
*top = y;
|
|
|
|
*right = x + w - 1;
|
|
|
|
*bottom = y + h - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if copying would involve overlapping regions
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param width width
|
|
|
|
* @param height height
|
|
|
|
* @param srcx source x1
|
|
|
|
* @param srcy source y1
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if there is an overlap, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_CopyOverlap(int x, int y, int width, int height, int srcx, int srcy)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
GDI_RECT dst;
|
|
|
|
GDI_RECT src;
|
|
|
|
|
|
|
|
gdi_CRgnToRect(x, y, width, height, &dst);
|
|
|
|
gdi_CRgnToRect(srcx, srcy, width, height, &src);
|
|
|
|
|
|
|
|
return (dst.right > src.left && dst.left < src.right &&
|
2015-06-26 15:32:38 +03:00
|
|
|
dst.bottom > src.top && dst.top < src.bottom) ? TRUE : FALSE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the coordinates of a given rectangle.\n
|
|
|
|
* @msdn{dd145085}
|
|
|
|
* @param rc rectangle
|
|
|
|
* @param xLeft x1
|
|
|
|
* @param yTop y1
|
|
|
|
* @param xRight x2
|
|
|
|
* @param yBottom y2
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if successful, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_SetRect(HGDI_RECT rc, int xLeft, int yTop, int xRight, int yBottom)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
rc->left = xLeft;
|
|
|
|
rc->top = yTop;
|
|
|
|
rc->right = xRight;
|
|
|
|
rc->bottom = yBottom;
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the coordinates of a given region.
|
|
|
|
* @param hRgn region
|
|
|
|
* @param nXLeft x1
|
|
|
|
* @param nYLeft y1
|
|
|
|
* @param nWidth width
|
|
|
|
* @param nHeight height
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if successful, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_SetRgn(HGDI_RGN hRgn, int nXLeft, int nYLeft, int nWidth, int nHeight)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
hRgn->x = nXLeft;
|
|
|
|
hRgn->y = nYLeft;
|
|
|
|
hRgn->w = nWidth;
|
|
|
|
hRgn->h = nHeight;
|
|
|
|
hRgn->null = 0;
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert rectangular coordinates to a region
|
|
|
|
* @param hRgn destination region
|
|
|
|
* @param nLeftRect x1
|
|
|
|
* @param nTopRect y1
|
|
|
|
* @param nRightRect x2
|
|
|
|
* @param nBottomRect y2
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if successful, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_SetRectRgn(HGDI_RGN hRgn, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
gdi_CRectToRgn(nLeftRect, nTopRect, nRightRect, nBottomRect, hRgn);
|
|
|
|
hRgn->null = 0;
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare two regions for equality.\n
|
|
|
|
* @msdn{dd162700}
|
|
|
|
* @param hSrcRgn1 first region
|
|
|
|
* @param hSrcRgn2 second region
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if both regions are equal, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_EqualRgn(HGDI_RGN hSrcRgn1, HGDI_RGN hSrcRgn2)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
if ((hSrcRgn1->x == hSrcRgn2->x) &&
|
|
|
|
(hSrcRgn1->y == hSrcRgn2->y) &&
|
|
|
|
(hSrcRgn1->w == hSrcRgn2->w) &&
|
|
|
|
(hSrcRgn1->h == hSrcRgn2->h))
|
|
|
|
{
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
return FALSE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy coordinates from a rectangle to another rectangle
|
2015-06-26 15:32:38 +03:00
|
|
|
* @msdn{dd183481}
|
2011-07-01 00:17:55 +04:00
|
|
|
* @param dst destination rectangle
|
|
|
|
* @param src source rectangle
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if successful, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_CopyRect(HGDI_RECT dst, HGDI_RECT src)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
dst->left = src->left;
|
|
|
|
dst->top = src->top;
|
|
|
|
dst->right = src->right;
|
|
|
|
dst->bottom = src->bottom;
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a point is inside a rectangle.\n
|
|
|
|
* @msdn{dd162882}
|
|
|
|
* @param rc rectangle
|
|
|
|
* @param x point x position
|
|
|
|
* @param y point y position
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero if the point is inside, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
INLINE BOOL gdi_PtInRect(HGDI_RECT rc, int x, int y)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* points on the left and top sides are considered in,
|
|
|
|
* while points on the right and bottom sides are considered out
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (x >= rc->left && x <= rc->right)
|
|
|
|
{
|
|
|
|
if (y >= rc->top && y <= rc->bottom)
|
|
|
|
{
|
2015-06-26 15:32:38 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-26 15:32:38 +03:00
|
|
|
return FALSE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidate a given region, such that it is redrawn on the next region update.\n
|
|
|
|
* @msdn{dd145003}
|
|
|
|
* @param hdc device context
|
|
|
|
* @param x x1
|
|
|
|
* @param y y1
|
|
|
|
* @param w width
|
|
|
|
* @param h height
|
2015-06-26 15:32:38 +03:00
|
|
|
* @return nonzero on success, 0 otherwise
|
2011-07-01 00:17:55 +04:00
|
|
|
*/
|
|
|
|
|
2015-04-17 17:21:55 +03:00
|
|
|
INLINE BOOL gdi_InvalidateRegion(HGDI_DC hdc, int x, int y, int w, int h)
|
2011-07-01 00:17:55 +04:00
|
|
|
{
|
|
|
|
GDI_RECT inv;
|
|
|
|
GDI_RECT rgn;
|
|
|
|
HGDI_RGN invalid;
|
2011-08-26 21:37:23 +04:00
|
|
|
HGDI_RGN cinvalid;
|
2011-07-01 00:17:55 +04:00
|
|
|
|
2014-09-12 04:12:32 +04:00
|
|
|
if (!hdc->hwnd)
|
2015-04-17 17:21:55 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
|
2014-09-12 04:12:32 +04:00
|
|
|
if (!hdc->hwnd->invalid)
|
2015-04-17 17:21:55 +03:00
|
|
|
return TRUE;
|
2011-08-24 09:47:43 +04:00
|
|
|
|
2014-12-05 21:40:40 +03:00
|
|
|
if (w == 0 || h == 0)
|
2015-04-17 17:21:55 +03:00
|
|
|
return TRUE;
|
2014-12-05 21:40:40 +03:00
|
|
|
|
2011-08-26 21:37:23 +04:00
|
|
|
cinvalid = hdc->hwnd->cinvalid;
|
2011-08-24 09:47:43 +04:00
|
|
|
|
2014-09-12 04:12:32 +04:00
|
|
|
if ((hdc->hwnd->ninvalid + 1) > hdc->hwnd->count)
|
2011-08-26 21:37:23 +04:00
|
|
|
{
|
2015-03-11 08:55:41 +03:00
|
|
|
int new_cnt;
|
|
|
|
HGDI_RGN new_rgn;
|
|
|
|
|
|
|
|
new_cnt = hdc->hwnd->count * 2;
|
|
|
|
new_rgn = (HGDI_RGN) realloc(cinvalid, sizeof(GDI_RGN) * new_cnt);
|
|
|
|
if (!new_rgn)
|
2015-04-17 17:21:55 +03:00
|
|
|
return FALSE;
|
2015-03-11 08:55:41 +03:00
|
|
|
hdc->hwnd->count = new_cnt;
|
|
|
|
cinvalid = new_rgn;
|
2011-08-24 09:47:43 +04:00
|
|
|
}
|
|
|
|
|
2011-08-26 21:37:23 +04:00
|
|
|
gdi_SetRgn(&cinvalid[hdc->hwnd->ninvalid++], x, y, w, h);
|
|
|
|
hdc->hwnd->cinvalid = cinvalid;
|
2011-07-01 00:17:55 +04:00
|
|
|
|
|
|
|
invalid = hdc->hwnd->invalid;
|
|
|
|
|
|
|
|
if (invalid->null)
|
|
|
|
{
|
|
|
|
invalid->x = x;
|
|
|
|
invalid->y = y;
|
|
|
|
invalid->w = w;
|
|
|
|
invalid->h = h;
|
|
|
|
invalid->null = 0;
|
2015-04-17 17:21:55 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
gdi_CRgnToRect(x, y, w, h, &rgn);
|
|
|
|
gdi_RgnToRect(invalid, &inv);
|
|
|
|
|
|
|
|
if (rgn.left < 0)
|
|
|
|
rgn.left = 0;
|
|
|
|
|
|
|
|
if (rgn.top < 0)
|
|
|
|
rgn.top = 0;
|
|
|
|
|
|
|
|
if (rgn.left < inv.left)
|
|
|
|
inv.left = rgn.left;
|
|
|
|
|
|
|
|
if (rgn.top < inv.top)
|
|
|
|
inv.top = rgn.top;
|
|
|
|
|
|
|
|
if (rgn.right > inv.right)
|
|
|
|
inv.right = rgn.right;
|
|
|
|
|
|
|
|
if (rgn.bottom > inv.bottom)
|
|
|
|
inv.bottom = rgn.bottom;
|
|
|
|
|
|
|
|
gdi_RectToRgn(&inv, invalid);
|
|
|
|
|
2015-04-17 17:21:55 +03:00
|
|
|
return TRUE;
|
2011-07-01 00:17:55 +04:00
|
|
|
}
|